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

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>

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">
import { useTheme } from 'vuetify'
import { useThemeConfig } from '@/plugins/vuetify/@core/composable/useThemeConfig'
import { hexToRgb } from '@/plugins/vuetify/@layouts/utils'
import { themeChange } from 'theme-change'
import { onMounted } from 'vue'
const { syncInitialLoaderTheme, syncVuetifyThemeWithTheme: syncConfigThemeWithVuetifyTheme, isAppRtl } = useThemeConfig()
import { useTheme } from 'vuetify';
import { useThemeConfig } from '@/plugins/vuetify/@core/composable/useThemeConfig';
import { hexToRgb } from '@/plugins/vuetify/@layouts/utils';
import { themeChange } from 'theme-change';
import { onMounted } from 'vue';
const {
syncInitialLoaderTheme,
syncVuetifyThemeWithTheme: syncConfigThemeWithVuetifyTheme,
isAppRtl,
} = useThemeConfig();
const { global } = useTheme()
const { global } = useTheme();
// Sync current theme with initial loader theme
syncInitialLoaderTheme()
syncConfigThemeWithVuetifyTheme()
syncInitialLoaderTheme();
syncConfigThemeWithVuetifyTheme();
onMounted(()=> {
themeChange(false)
})
onMounted(() => {
themeChange(false);
});
</script>
<template>
<VLocaleProvider :rtl="isAppRtl">
<!-- 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 />
</VApp>
</VLocaleProvider>

View File

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

View File

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

View File

@ -21,7 +21,13 @@ const isPositive = controlledComputed(
<template>
<VCard class="h-full flex-col content-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" />
</VAvatar>

View File

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

View File

@ -1,12 +1,17 @@
<script lang="ts" setup >
<script lang="ts" setup>
import VueCountdown from '@chenfengyuan/vue-countdown';
const props = defineProps({
time: { type: Number},
})
time: { type: Number },
});
</script>
<template>
<vue-countdown 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
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>
</template>

View File

@ -15,17 +15,28 @@ const props = defineProps({
});
const total = computed(() => props.pool?.bonded_tokens);
const format = useFormatter();
const yes = computed(() => format.calculatePercent(props.tally?.yes, 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));
const yes = computed(() =>
format.calculatePercent(props.tally?.yes, 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>
<template>
<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-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>
</template>

View File

@ -14,28 +14,30 @@ const {
next: getNextThemeName,
index: currentThemeIndex,
} = useCycleList(
props.themes.map(t => t.name),
props.themes.map((t) => t.name),
{ initialValue: theme.value }
);
const changeTheme = () => {
theme.value = getNextThemeName();
theme.value = getNextThemeName();
};
const changeMode = (val: 'dark' | 'light' | 'system') => {
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';
}
if (value === 'dark') {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
} else {
document.documentElement.classList.add('light');
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
watch(theme, (val: 'dark' | 'light' | 'system') => {
@ -52,10 +54,7 @@ onMounted(() => {
<template>
<div class="tooltip tooltip-bottom delay-1000" :data-tip="currentThemeName">
<button
class="btn btn-ghost btn-circle btn-sm mx-1"
@click="changeTheme"
>
<button class="btn btn-ghost btn-circle btn-sm mx-1" @click="changeTheme">
<Icon :icon="props.themes[currentThemeIndex].icon" class="text-2xl" />
</button>
</div>

View File

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

View File

@ -1,44 +1,61 @@
<script setup lang="ts">
import VueApexCharts from 'vue3-apexcharts'
import { useTheme } from 'vuetify'
import { hexToRgb } from '@/plugins/vuetify/@layouts/utils'
import VueApexCharts from 'vue3-apexcharts';
import { useTheme } from 'vuetify';
import { hexToRgb } from '@/plugins/vuetify/@layouts/utils';
import { computed, type PropType } from 'vue';
import { useFormatter } from '@/stores';
import type { CommissionRate } from '@/types'
import type { CommissionRate } from '@/types';
const props = defineProps({
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)
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
);
// const rate = 15 // props.commision?.commissionRates.rate
// const change = 15
// const max = 20
const left = rate
const right = computed(() => max.value - rate.value)
const left = rate;
const right = computed(() => max.value - rate.value);
const s1 = computed(() => left.value > change.value ? left.value - change.value : 0 )
const s2 = computed(() => 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 s1 = computed(() =>
left.value > change.value ? left.value - change.value : 0
);
const s2 = computed(() =>
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 format = useFormatter()
const vuetifyTheme = useTheme();
const format = useFormatter();
const chartConfig = computed(() => {
const themeColors = vuetifyTheme.current.value.colors
const variableTheme = vuetifyTheme.current.value.variables
const themeColors = vuetifyTheme.current.value.colors;
const variableTheme = vuetifyTheme.current.value.variables;
const secondaryText = `rgba(${hexToRgb(String(themeColors['on-background']))},${variableTheme['medium-emphasis-opacity']})`
const primaryText = `rgba(${hexToRgb(String(themeColors['on-background']))},${variableTheme['high-emphasis-opacity']})`
const secondaryText = `rgba(${hexToRgb(
String(themeColors['on-background'])
)},${variableTheme['medium-emphasis-opacity']})`;
const primaryText = `rgba(${hexToRgb(String(themeColors['on-background']))},${
variableTheme['high-emphasis-opacity']
})`;
return {
chart: {
@ -54,8 +71,18 @@ const chartConfig = computed(() => {
legend: { show: false },
tooltip: { enabled: false },
dataLabels: { enabled: false },
stroke: { width: 3, lineCap: 'round', colors: ['rgba(var(--v-theme-surface), 1)'] },
labels: ['Available', 'Daily Change', 'Commission Rate', 'Daily Change', 'Available'],
stroke: {
width: 3,
lineCap: 'round',
colors: ['rgba(var(--v-theme-surface), 1)'],
},
labels: [
'Available',
'Daily Change',
'Commission Rate',
'Daily Change',
'Available',
],
states: {
hover: {
filter: { type: 'none' },
@ -90,7 +117,7 @@ const chartConfig = computed(() => {
label: 'Commission Rate',
fontSize: '1rem',
color: secondaryText,
formatter: ( ) => `${rate.value}%`,
formatter: () => `${rate.value}%`,
},
},
},
@ -104,13 +131,18 @@ const chartConfig = computed(() => {
},
},
],
}
})
};
});
</script>
<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>
<VueApexCharts
type="donut"
@ -121,15 +153,15 @@ const chartConfig = computed(() => {
<div class="d-flex align-center justify-center flex-wrap mx-2 gap-x-6">
<div class="d-flex align-center gap-2">
<VBadge dot color="success"/>
<VBadge dot color="success" />
<span class="mt-1 text-caption">Rate:{{ rate }}%</span>
</div>
<div class="d-flex align-center gap-2">
<VBadge dot color="success" style="opacity:0.2"/>
<VBadge dot color="success" style="opacity: 0.2" />
<span class="mt-1 text-caption">24h: ±{{ change }}%</span>
</div>
<div class="d-flex align-center gap-2">
<VBadge dot color="secondary"/>
<VBadge dot color="secondary" />
<span class="mt-1 text-caption">Max:{{ max }}%</span>
</div>
</div>

View File

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

View File

@ -1,30 +1,41 @@
<script lang="ts" setup>
import VueApexCharts from 'vue3-apexcharts'
import { useTheme } from 'vuetify'
import { getAreaChartSplineConfig, getMarketPriceChartConfig } from './apexChartConfig'
import VueApexCharts from 'vue3-apexcharts';
import { useTheme } from 'vuetify';
import {
getAreaChartSplineConfig,
getMarketPriceChartConfig,
} from './apexChartConfig';
import { useIndexModule } from '@/modules/[chain]/indexStore';
import { computed, ref } from '@vue/reactivity';
const store = useIndexModule()
const vuetifyTheme = useTheme()
const store = useIndexModule();
const vuetifyTheme = useTheme();
const chartConfig = computed(() => {
const labels = store.marketData.prices.map(x => x[0])
return getMarketPriceChartConfig(vuetifyTheme.current.value, labels)
})
const kind = ref('price')
const labels = store.marketData.prices.map((x) => x[0]);
return getMarketPriceChartConfig(vuetifyTheme.current.value, labels);
});
const kind = ref('price');
const series = computed(() => {
return [{
name: 'Price',
data: kind.value ==='price'?store.marketData.prices.map(x => x[1]) : store.marketData.total_volumes.map(x => x[1])}]
})
return [
{
name: 'Price',
data:
kind.value === 'price'
? store.marketData.prices.map((x) => x[1])
: store.marketData.total_volumes.map((x) => x[1]),
},
];
});
</script>
<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
type="area"
height="261"
:options="chartConfig"
:series="series"
/>
</template>
</template>

View File

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

View File

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

View File

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

View File

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

View File

@ -6,33 +6,44 @@ const props = defineProps({
value: { type: null as any },
thead: {
type: Boolean,
default: true
}
})
default: true,
},
});
const header = computed(() => {
if(props.value && props.value.length > 0) {
return Object.keys(props.value[0])
if (props.value && props.value.length > 0) {
return Object.keys(props.value[0]);
}
return []
})
return [];
});
</script>
<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">
<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>
</thead>
<tbody>
<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>
</tbody>
</VTable>
<div v-else class="h-[300px]">
</div>
</template>
<div v-else class="h-[300px]"></div>
</template>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,35 +1,51 @@
<script lang="ts" setup>
import { fromBase64, toBase64 } from '@cosmjs/encoding';
import { decodeTxRaw } from '@cosmjs/proto-signing'
import { decodeTxRaw } from '@cosmjs/proto-signing';
import { computed } from '@vue/reactivity';
import { hashTx } from '@/libs'
import { hashTx } from '@/libs';
import { useBlockchain, useFormatter } from '@/stores';
const props = defineProps({
value: { type: Array<string>},
value: { type: Array<string> },
});
const txs = computed(() => {
return props.value?.map(x => ({ hash: hashTx(fromBase64(x)) , tx: decodeTxRaw(fromBase64(x)) })) || []
})
const format = useFormatter()
const chain = useBlockchain()
return (
props.value?.map((x) => ({
hash: hashTx(fromBase64(x)),
tx: decodeTxRaw(fromBase64(x)),
})) || []
);
});
const format = useFormatter();
const chain = useBlockchain();
</script>
<template>
<VTable density="compact" v-if="txs.length > 0">
<thead>
<tr>
<th>Hash</th><th>Msgs</th><th>Memo</th>
</tr>
</thead>
<tbody>
<tr v-for="item in txs">
<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>
</tr>
</tbody>
</VTable>
<div v-else>[]</div>
</template>
<VTable density="compact" v-if="txs.length > 0">
<thead>
<tr>
<th>Hash</th>
<th>Msgs</th>
<th>Memo</th>
</tr>
</thead>
<tbody>
<tr v-for="item in txs">
<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>
</tr>
</tbody>
</VTable>
<div v-else>[]</div>
</template>

View File

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

View File

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

View File

@ -1,5 +1,10 @@
<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
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>
<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
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>
<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
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>
<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
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">
import { useSkins } from '@core/composable/useSkins'
import { useSkins } from '@core/composable/useSkins';
export default defineComponent({
setup() {
const routerView = resolveComponent('router-view')
const { injectSkinClasses } = useSkins()
const routerView = resolveComponent('router-view');
const { injectSkinClasses } = useSkins();
// 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>
<style>

View File

@ -4,22 +4,27 @@ import { computed } from '@vue/reactivity';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
const chain = useBlockchain()
const route = useRoute()
const i18n = useI18n()
/// To display human readable module name, we have to set the prefix("module.") + route name to the key in i18n.
const chain = useBlockchain();
const route = useRoute();
const i18n = useI18n();
/// 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'
const moduleName = computed(() => i18n.t(`module.${route.name?.toString()||''}`))
const items = computed(() => [{title: String(chain.name).toUpperCase(), href: `/${chain.name}`}, moduleName.value])
const moduleName = computed(() =>
i18n.t(`module.${route.name?.toString() || ''}`)
);
const items = computed(() => [
{ title: String(chain.name).toUpperCase(), href: `/${chain.name}` },
moduleName.value,
]);
</script>
<template>
<div class="d-flex flex-rows align-center">
<span class="text-h5 mr-3">{{ moduleName }}</span>
<v-icon icon="mdi-dots-vertical" />
<v-icon icon="mdi-dots-vertical" />
<VBreadcrumbs :items="items">
<template v-slot:divider>
<v-icon icon="mdi-chevron-right"></v-icon>
</template>
<template v-slot:divider>
<v-icon icon="mdi-chevron-right"></v-icon>
</template>
</VBreadcrumbs>
</div>
</template>
</template>

View File

@ -1,61 +1,72 @@
<script setup lang="ts">
import { useBlockchain, useBaseStore } from '@/stores';
const chainStore = useBlockchain()
const baseStore = useBaseStore()
chainStore.initial()
const chainStore = useBlockchain();
const baseStore = useBaseStore();
chainStore.initial();
chainStore.$subscribe((m, s) => {
if(!Array.isArray(m.events) && m.events.key === 'endpoint') {
chainStore.initial()
if (!Array.isArray(m.events) && m.events.key === 'endpoint') {
chainStore.initial();
}
})
});
</script>
<template>
<VListItem class="m-0 p-0">
<template #prepend>
<VBadge
dot
location="bottom right"
offset-x="3"
offset-y="3"
bordered
color="success"
class="mr-2"
>
<VAvatar
class="cursor-pointer"
color="primary"
variant="tonal"
<VBadge
dot
location="bottom right"
offset-x="3"
offset-y="3"
bordered
color="success"
class="mr-2"
>
<VImg :src="chainStore.logo" />
<VAvatar class="cursor-pointer" color="primary" variant="tonal">
<VImg :src="chainStore.logo" />
<!-- SECTION Menu -->
<VMenu
activator="parent"
location="bottom start"
offset="14px"
>
<VList>
<!-- 👉 Rest -->
<VListSubheader v-if="chainStore.current?.endpoints?.rest" 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>
</VListItem>
<!-- SECTION Menu -->
<VMenu activator="parent" location="bottom start" offset="14px">
<VList>
<!-- 👉 Rest -->
<VListSubheader
v-if="chainStore.current?.endpoints?.rest"
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>
</VListItem>
<VListSubheader v-if="chainStore.current?.endpoints?.grpc" title="gRPC Endpoint" />
<VListItem v-for="i in chainStore.current?.endpoints?.grpc" link>
<VListItemTitle>{{ i.provider }}</VListItemTitle>
<VListItemSubtitle>{{ i.address }}</VListItemSubtitle>
</VListItem>
</VList>
</VMenu>
<!-- !SECTION -->
</VAvatar>
</VBadge>
</template>
<VListItemTitle>{{ baseStore.latest.block?.header?.chain_id || chainStore.chainName || '' }}</VListItemTitle>
<VListItemSubtitle> {{ chainStore.connErr|| chainStore.endpoint.address }}</VListItemSubtitle>
<VListSubheader
v-if="chainStore.current?.endpoints?.grpc"
title="gRPC Endpoint"
/>
<VListItem v-for="i in chainStore.current?.endpoints?.grpc" link>
<VListItemTitle>{{ i.provider }}</VListItemTitle>
<VListItemSubtitle>{{ i.address }}</VListItemSubtitle>
</VListItem>
</VList>
</VMenu>
<!-- !SECTION -->
</VAvatar>
</VBadge>
</template>
<VListItemTitle>{{
baseStore.latest.block?.header?.chain_id || chainStore.chainName || ''
}}</VListItemTitle>
<VListItemSubtitle>
{{ chainStore.connErr || chainStore.endpoint.address }}</VListItemSubtitle
>
</VListItem>
</template>

View File

@ -67,7 +67,7 @@ blockchain.$subscribe((m, s) => {
<NavBarNotifications class="hidden md:inline-block" />
<NavBarI18n class="hidden md:inline-block" />
<NavbarThemeSwitcher class="hidden md:inline-block" />
<ConnectWallet class="md:inline-block"/>
<ConnectWallet class="md:inline-block" />
</div>
</template>

View File

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

View File

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

View File

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

View File

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

View File

@ -1,26 +1,34 @@
<template>
<footer class="footer items-center p-4 text-base">
<div class="items-center grid-flow-col">
&copy;
{{ new Date().getFullYear() }}
Made With
<img src="../../assets/images/heart.svg"/>
By
<a class="link link-info no-underline"
href="https://ping.pub"
target="_blank"
rel="noopener noreferrer"
>Ping.pub</a>
</div>
<div 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"
target="noopener noreferrer"
>License</a>
<a class="link link-info no-underline"
href="https://github.com/ping-pub/explorer"
target="noopener noreferrer"
>Github</a>
</div>
</footer>
</template>
<footer class="footer items-center p-4 text-base">
<div class="items-center grid-flow-col">
&copy;
{{ new Date().getFullYear() }}
Made With
<img src="../../assets/images/heart.svg" />
By
<a
class="link link-info no-underline"
href="https://ping.pub"
target="_blank"
rel="noopener noreferrer"
>Ping.pub</a
>
</div>
<div
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"
target="noopener noreferrer"
>License</a
>
<a
class="link link-info no-underline"
href="https://github.com/ping-pub/explorer"
target="noopener noreferrer"
>Github</a
>
</div>
</footer>
</template>

View File

@ -1,65 +1,79 @@
import {fromBase64, fromBech32, fromHex, toBase64, toBech32, toHex} from '@cosmjs/encoding'
import { Ripemd160, sha256 } from '@cosmjs/crypto'
import {
fromBase64,
fromBech32,
fromHex,
toBase64,
toBech32,
toHex,
} from '@cosmjs/encoding';
import { Ripemd160, sha256 } from '@cosmjs/crypto';
export function decodeAddress(address: string) {
return fromBech32(address)
return fromBech32(address);
}
export function valoperToPrefix(valoper?: string) {
if(!valoper) return ''
const prefixIndex = valoper.indexOf('valoper')
if (prefixIndex === -1) return null
return valoper.slice(0, prefixIndex)
}
export function operatorAddressToAccount(operAddress?: string) {
if(!operAddress) return ''
const { prefix, data } = fromBech32(operAddress)
if (prefix === 'iva') { // handle special cases
return toBech32('iaa', data)
}
if (prefix === 'crocncl') { // handle special cases
return toBech32('cro', data)
}
return toBech32(prefix.replace('valoper', ''), data)
}
if (!valoper) return '';
const prefixIndex = valoper.indexOf('valoper');
if (prefixIndex === -1) return null;
return valoper.slice(0, prefixIndex);
}
export function consensusPubkeyToHexAddress(consensusPubkey?: {"@type": string, key: string}) {
if(!consensusPubkey) return ""
let raw = ""
export function operatorAddressToAccount(operAddress?: string) {
if (!operAddress) return '';
const { prefix, data } = fromBech32(operAddress);
if (prefix === 'iva') {
// handle special cases
return toBech32('iaa', data);
}
if (prefix === 'crocncl') {
// handle special cases
return toBech32('cro', data);
}
return toBech32(prefix.replace('valoper', ''), data);
}
export function consensusPubkeyToHexAddress(consensusPubkey?: {
'@type': string;
key: string;
}) {
if (!consensusPubkey) return '';
let raw = '';
if (consensusPubkey['@type'] === '/cosmos.crypto.ed25519.PubKey') {
const pubkey = fromBase64(consensusPubkey.key)
if(pubkey) return toHex(sha256(pubkey)).slice(0, 40).toUpperCase()
const pubkey = fromBase64(consensusPubkey.key);
if (pubkey) return toHex(sha256(pubkey)).slice(0, 40).toUpperCase();
}
if (consensusPubkey['@type'] === '/cosmos.crypto.secp256k1.PubKey') {
const pubkey = fromBase64(consensusPubkey.key)
if(pubkey) return toHex(new Ripemd160().update(sha256(pubkey)).digest())
const pubkey = fromBase64(consensusPubkey.key);
if (pubkey) return toHex(new Ripemd160().update(sha256(pubkey)).digest());
}
return raw
return raw;
}
export function pubKeyToValcons(consensusPubkey: {"@type": string, key: string}, prefix: string) {
if(consensusPubkey && consensusPubkey.key) {
const pubkey = fromBase64(consensusPubkey.key)
if(pubkey) {
const addressData = sha256(pubkey).slice(0, 20)
return toBech32(`${prefix}valcons`, addressData)
}
export function pubKeyToValcons(
consensusPubkey: { '@type': string; key: string },
prefix: string
) {
if (consensusPubkey && consensusPubkey.key) {
const pubkey = fromBase64(consensusPubkey.key);
if (pubkey) {
const addressData = sha256(pubkey).slice(0, 20);
return toBech32(`${prefix}valcons`, addressData);
}
return ''
}
return '';
}
export function valconsToBase64(address: string) {
if(address) return toHex(fromBech32(address).data).toUpperCase()
return ''
}
export function toETHAddress(cosmosAddress: string) {
return `0x${toHex(fromBech32(cosmosAddress).data)}`
}
export function addressEnCode(prefix: string, pubkey: Uint8Array) {
return toBech32(prefix, pubkey)
}
export function valconsToBase64(address: string) {
if (address) return toHex(fromBech32(address).data).toUpperCase();
return '';
}
export function toETHAddress(cosmosAddress: string) {
return `0x${toHex(fromBech32(cosmosAddress).data)}`;
}
export function addressEnCode(prefix: string, pubkey: Uint8Array) {
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 = {
auth_params: { url: "/cosmos/auth/v1beta1/params", adapter },
auth_accounts: { url: "/cosmos/auth/v1beta1/accounts", adapter },
auth_account_address: { url: "/cosmos/auth/v1beta1/accounts/{address}", adapter },
bank_params: { url: "/cosmos/bank/v1beta1/params", adapter },
bank_balances_address: { url: "/cosmos/bank/v1beta1/balances/{address}", adapter },
bank_denoms_metadata: { url: "/cosmos/bank/v1beta1/denoms_metadata", adapter },
bank_supply: { url: "/cosmos/bank/v1beta1/supply", adapter },
bank_supply_by_denom: { url: "/cosmos/bank/v1beta1/supply/{denom}", adapter },
distribution_params: { url: "/cosmos/distribution/v1beta1/params", adapter },
distribution_community_pool: { url: "/cosmos/distribution/v1beta1/community_pool", 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 },
distribution_validator_slashes: { url: "/cosmos/distribution/v1beta1/validators/{validator_address}/slashes", adapter },
distribution_delegator_rewards: { url: "/cosmos/distribution/v1beta1/delegators/{delegator_addr}/rewards", adapter },
slashing_params: { url: "/cosmos/slashing/v1beta1/params", adapter },
slashing_signing_info: { url: "/cosmos/slashing/v1beta1/signing_infos", adapter },
gov_params_voting: { url: "/cosmos/gov/v1beta1/params/voting", 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 },
auth_params: { url: '/cosmos/auth/v1beta1/params', adapter },
auth_accounts: { url: '/cosmos/auth/v1beta1/accounts', adapter },
auth_account_address: {
url: '/cosmos/auth/v1beta1/accounts/{address}',
adapter,
},
bank_params: { url: '/cosmos/bank/v1beta1/params', adapter },
bank_balances_address: {
url: '/cosmos/bank/v1beta1/balances/{address}',
adapter,
},
bank_denoms_metadata: {
url: '/cosmos/bank/v1beta1/denoms_metadata',
adapter,
},
bank_supply: { url: '/cosmos/bank/v1beta1/supply', adapter },
bank_supply_by_denom: { url: '/cosmos/bank/v1beta1/supply/{denom}', adapter },
distribution_params: { url: '/cosmos/distribution/v1beta1/params', adapter },
distribution_community_pool: {
url: '/cosmos/distribution/v1beta1/community_pool',
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,
},
distribution_validator_slashes: {
url: '/cosmos/distribution/v1beta1/validators/{validator_address}/slashes',
adapter,
},
distribution_delegator_rewards: {
url: '/cosmos/distribution/v1beta1/delegators/{delegator_addr}/rewards',
adapter,
},
slashing_params: { url: '/cosmos/slashing/v1beta1/params', adapter },
slashing_signing_info: {
url: '/cosmos/slashing/v1beta1/signing_infos',
adapter,
},
gov_params_voting: { url: '/cosmos/gov/v1beta1/params/voting', 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_params: { url: "/cosmos/mint/v1beta1/params", adapter },
mint_annual_provisions: { url: "/cosmos/mint/v1beta1/annual_provisions", adapter },
mint_inflation: { url: '/cosmos/mint/v1beta1/inflation', adapter },
mint_params: { url: '/cosmos/mint/v1beta1/params', adapter },
mint_annual_provisions: {
url: '/cosmos/mint/v1beta1/annual_provisions',
adapter,
},
// ibc
ibc_app_ica_controller_params: { url: "/ibc/apps/interchain_accounts/controller/v1/params", adapter },
ibc_app_ica_host_params: { url: "/ibc/apps/interchain_accounts/host/v1/params", adapter },
ibc_app_transfer_escrow_address: { url: "/ibc/apps/transfer/v1/channels/{channel_id}/ports/{port_id}/escrow_address", 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 }
// ibc
ibc_app_ica_controller_params: {
url: '/ibc/apps/interchain_accounts/controller/v1/params',
adapter,
},
ibc_app_ica_host_params: {
url: '/ibc/apps/interchain_accounts/host/v1/params',
adapter,
},
ibc_app_transfer_escrow_address: {
url: '/ibc/apps/transfer/v1/channels/{channel_id}/ports/{port_id}/escrow_address',
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 = {
"0.46.1": DEFAULT
}
'0.46.1': DEFAULT,
};
export const NAME_REGISTRY: Registry = {
"evmos": withCustomAdapter(DEFAULT, {})
}
evmos: withCustomAdapter(DEFAULT, {}),
};

View File

@ -1,205 +1,268 @@
import { fetchData } from '@/libs';
import { DEFAULT } from '@/libs'
import { adapter, withCustomAdapter, type Request, type RequestRegistry, type Registry, type AbstractRegistry } from './registry';
import { DEFAULT } from '@/libs';
import {
adapter,
withCustomAdapter,
type Request,
type RequestRegistry,
type Registry,
type AbstractRegistry,
} from './registry';
export class BaseRestClient<R extends AbstractRegistry> {
endpoint: string;
registry: R;
constructor(endpoint: string, registry: R) {
this.endpoint = endpoint
this.registry = registry
}
async request<T>(request: Request<T>, args: Record<string, any>, query="") {
let url = `${this.endpoint}${request.url}${query}`
Object.keys(args).forEach(k => {
url = url.replace(`{${k}}`, args[k] || "")
})
return fetchData<T>(url, adapter)
}
endpoint: string;
registry: R;
constructor(endpoint: string, registry: R) {
this.endpoint = endpoint;
this.registry = registry;
}
async request<T>(request: Request<T>, args: Record<string, any>, query = '') {
let url = `${this.endpoint}${request.url}${query}`;
Object.keys(args).forEach((k) => {
url = url.replace(`{${k}}`, args[k] || '');
});
return fetchData<T>(url, adapter);
}
}
export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
// Auth Module
async getAuthAccounts() {
return this.request(this.registry.auth_accounts, {})
}
async getAuthAccount(address: string) {
return this.request(this.registry.auth_account_address, {address})
}
// Bank Module
async getBankParams() {
return this.request(this.registry.bank_params, {})
}
async getBankBalances(address: string) {
return this.request(this.registry.bank_balances_address, {address})
}
async getBankDenomMetadata() {
return this.request(this.registry.bank_denoms_metadata, {})
}
async getBankSupply() {
return this.request(this.registry.bank_supply, {})
}
async getBankSupplyByDenom(denom: string) {
return this.request(this.registry.bank_supply_by_denom, {denom})
}
// Distribution Module
async getDistributionParams() {
return this.request(this.registry.distribution_params, {})
}
async getDistributionCommunityPool() {
return this.request(this.registry.distribution_community_pool, {})
}
async getDistributionDelegatorRewards(delegator_addr: string) {
return this.request(this.registry.distribution_delegator_rewards, {delegator_addr})
}
async getDistributionValidatorCommission(validator_address: string) {
return this.request(this.registry.distribution_validator_commission, {validator_address})
}
async getDistributionValidatorOutstandingRewards(validator_address: string) {
return this.request(this.registry.distribution_validator_outstanding_rewards, {validator_address})
}
async getDistributionValidatorSlashes(validator_address: string) {
return this.request(this.registry.distribution_validator_slashes, {validator_address})
}
// Slashing
async getSlashingParams() {
return this.request(this.registry.slashing_params, {})
}
async getSlashingSigningInfos() {
const query = "?pagination.limit=300"
return this.request(this.registry.slashing_signing_info, {}, query)
}
// Gov
async getGovParamsVoting() {
return this.request(this.registry.gov_params_voting, {})
}
async getGovParamsDeposit() {
return this.request(this.registry.gov_params_deposit, {})
}
async getGovParamsTally() {
return this.request(this.registry.gov_params_tally, {})
}
async getGovProposals(status: string, limit = 50) {
const 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) {
return this.request(this.registry.gov_proposals_proposal_id, {proposal_id})
}
async getGovProposalDeposits(proposal_id: string) {
return this.request(this.registry.gov_proposals_deposits, {proposal_id})
}
async getGovProposalTally(proposal_id: string) {
return this.request(this.registry.gov_proposals_tally, {proposal_id})
}
async getGovProposalVotes(proposal_id: string, next_key?: string) {
return this.request(this.registry.gov_proposals_votes, {proposal_id, next_key})
}
async getGovProposalVotesVoter(proposal_id: string, voter: string ) {
return this.request(this.registry.gov_proposals_votes_voter, {proposal_id, voter})
}
// staking
async getStakingDelegations(delegator_addr: string) {
return this.request(this.registry.staking_deletations, {delegator_addr})
}
async getStakingDelegatorRedelegations(delegator_addr: string) {
return this.request(this.registry.staking_delegator_redelegations, {delegator_addr})
}
async getStakingDelegatorUnbonding(delegator_addr: string) {
return this.request(this.registry.staking_delegator_unbonding_delegations, {delegator_addr})
}
async getStakingDelegatorValidators(delegator_addr: string) {
return this.request(this.registry.staking_delegator_validators, {delegator_addr})
}
async getStakingParams() {
return this.request(this.registry.staking_params, {})
}
async getStakingPool() {
return this.request(this.registry.staking_pool, {})
}
async getStakingValidators(status: string, limit = 200) {
return this.request(this.registry.staking_validators, {status, limit})
}
async getStakingValidator(validator_addr: string) {
return this.request(this.registry.staking_validators_address, {validator_addr})
}
async getStakingValidatorsDelegations(validator_addr: string) {
return this.request(this.registry.staking_validators_delegations, {validator_addr})
}
async getStakingValidatorsDelegationsDelegator(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) {
return this.request(this.registry.staking_validators_delegations_unbonding_delegations, {validator_addr, delegator_addr})
}
// Auth Module
async getAuthAccounts() {
return this.request(this.registry.auth_accounts, {});
}
async getAuthAccount(address: string) {
return this.request(this.registry.auth_account_address, { address });
}
// Bank Module
async getBankParams() {
return this.request(this.registry.bank_params, {});
}
async getBankBalances(address: string) {
return this.request(this.registry.bank_balances_address, { address });
}
async getBankDenomMetadata() {
return this.request(this.registry.bank_denoms_metadata, {});
}
async getBankSupply() {
return this.request(this.registry.bank_supply, {});
}
async getBankSupplyByDenom(denom: string) {
return this.request(this.registry.bank_supply_by_denom, { denom });
}
// Distribution Module
async getDistributionParams() {
return this.request(this.registry.distribution_params, {});
}
async getDistributionCommunityPool() {
return this.request(this.registry.distribution_community_pool, {});
}
async getDistributionDelegatorRewards(delegator_addr: string) {
return this.request(this.registry.distribution_delegator_rewards, {
delegator_addr,
});
}
async getDistributionValidatorCommission(validator_address: string) {
return this.request(this.registry.distribution_validator_commission, {
validator_address,
});
}
async getDistributionValidatorOutstandingRewards(validator_address: string) {
return this.request(
this.registry.distribution_validator_outstanding_rewards,
{ validator_address }
);
}
async getDistributionValidatorSlashes(validator_address: string) {
return this.request(this.registry.distribution_validator_slashes, {
validator_address,
});
}
// Slashing
async getSlashingParams() {
return this.request(this.registry.slashing_params, {});
}
async getSlashingSigningInfos() {
const query = '?pagination.limit=300';
return this.request(this.registry.slashing_signing_info, {}, query);
}
// Gov
async getGovParamsVoting() {
return this.request(this.registry.gov_params_voting, {});
}
async getGovParamsDeposit() {
return this.request(this.registry.gov_params_deposit, {});
}
async getGovParamsTally() {
return this.request(this.registry.gov_params_tally, {});
}
async getGovProposals(status: string, limit = 50) {
const 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) {
return this.request(this.registry.gov_proposals_proposal_id, {
proposal_id,
});
}
async getGovProposalDeposits(proposal_id: string) {
return this.request(this.registry.gov_proposals_deposits, { proposal_id });
}
async getGovProposalTally(proposal_id: string) {
return this.request(this.registry.gov_proposals_tally, { proposal_id });
}
async getGovProposalVotes(proposal_id: string, next_key?: string) {
return this.request(this.registry.gov_proposals_votes, {
proposal_id,
next_key,
});
}
async getGovProposalVotesVoter(proposal_id: string, voter: string) {
return this.request(this.registry.gov_proposals_votes_voter, {
proposal_id,
voter,
});
}
// staking
async getStakingDelegations(delegator_addr: string) {
return this.request(this.registry.staking_deletations, { delegator_addr });
}
async getStakingDelegatorRedelegations(delegator_addr: string) {
return this.request(this.registry.staking_delegator_redelegations, {
delegator_addr,
});
}
async getStakingDelegatorUnbonding(delegator_addr: string) {
return this.request(this.registry.staking_delegator_unbonding_delegations, {
delegator_addr,
});
}
async getStakingDelegatorValidators(delegator_addr: string) {
return this.request(this.registry.staking_delegator_validators, {
delegator_addr,
});
}
async getStakingParams() {
return this.request(this.registry.staking_params, {});
}
async getStakingPool() {
return this.request(this.registry.staking_pool, {});
}
async getStakingValidators(status: string, limit = 200) {
return this.request(this.registry.staking_validators, { status, limit });
}
async getStakingValidator(validator_addr: string) {
return this.request(this.registry.staking_validators_address, {
validator_addr,
});
}
async getStakingValidatorsDelegations(validator_addr: string) {
return this.request(this.registry.staking_validators_delegations, {
validator_addr,
});
}
async getStakingValidatorsDelegationsDelegator(
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
) {
return this.request(
this.registry.staking_validators_delegations_unbonding_delegations,
{ validator_addr, delegator_addr }
);
}
//tendermint
async getBaseAbciQuery() {
return this.request(this.registry.base_tendermint_abci_query, {})
}
async getBaseBlockLatest() {
return this.request(this.registry.base_tendermint_block_latest, {})
}
async getBaseBlockAt(height: string|number) {
return this.request(this.registry.base_tendermint_block_height, {height})
}
async getBaseNodeInfo() {
return this.request(this.registry.base_tendermint_node_info, {})
}
async getBaseValidatorsetAt(height: string|number) {
return this.request(this.registry.base_tendermint_validatorsets_height, {height})
}
async getBaseValidatorsetLatest() {
return this.request(this.registry.base_tendermint_validatorsets_latest, {})
}
// tx
async getTxsBySender(sender: string) {
const query = `?pagination.reverse=true&events=message.sender='${sender}'`
return this.request(this.registry.tx_txs, {}, query)
}
async getTxsAt(height: string|number) {
return this.request(this.registry.tx_txs_block, {height})
}
async getTx(hash: string) {
return this.request(this.registry.tx_hash, {hash})
}
// mint
async getMintParam() {
return this.request(this.registry.mint_params, {})
}
async getMintInflation() {
return this.request(this.registry.mint_inflation, {})
}
async getMintAnnualProvisions() {
return this.request(this.registry.mint_annual_provisions, {})
}
// ibc
async getIBCAppTransferDenom(hash: string) {
return this.request(this.registry.ibc_app_transfer_denom_traces_hash, {hash})
}
async getIBCConnections() {
return this.request(this.registry.ibc_core_connection_connections, {})
}
async getIBCConnectionsById(connection_id: string) {
return this.request(this.registry.ibc_core_connection_connections_connection_id, {connection_id})
}
async getIBCConnectionsClientState(connection_id: string) {
return this.request(this.registry.ibc_core_connection_connections_connection_id_client_state, {connection_id})
}
async getIBCConnectionsChannels(connection_id: string) {
return this.request(this.registry.ibc_core_channel_connections_channels, {connection_id})
}
async getIBCChannels() {
return this.request(this.registry.ibc_core_channel_channels, {})
}
async getIBCChannelAcknowledgements(channel_id: string, port_id: string) {
return this.request(this.registry.ibc_core_channel_channels_acknowledgements, {channel_id, port_id})
}
async getIBCChannelNextSequence(channel_id: string, port_id: string) {
return this.request(this.registry.ibc_core_channel_channels_next_sequence, {channel_id, port_id})
}
//tendermint
async getBaseAbciQuery() {
return this.request(this.registry.base_tendermint_abci_query, {});
}
async getBaseBlockLatest() {
return this.request(this.registry.base_tendermint_block_latest, {});
}
async getBaseBlockAt(height: string | number) {
return this.request(this.registry.base_tendermint_block_height, { height });
}
async getBaseNodeInfo() {
return this.request(this.registry.base_tendermint_node_info, {});
}
async getBaseValidatorsetAt(height: string | number) {
return this.request(this.registry.base_tendermint_validatorsets_height, {
height,
});
}
async getBaseValidatorsetLatest() {
return this.request(this.registry.base_tendermint_validatorsets_latest, {});
}
// tx
async getTxsBySender(sender: string) {
const query = `?pagination.reverse=true&events=message.sender='${sender}'`;
return this.request(this.registry.tx_txs, {}, query);
}
async getTxsAt(height: string | number) {
return this.request(this.registry.tx_txs_block, { height });
}
async getTx(hash: string) {
return this.request(this.registry.tx_hash, { hash });
}
// mint
async getMintParam() {
return this.request(this.registry.mint_params, {});
}
async getMintInflation() {
return this.request(this.registry.mint_inflation, {});
}
async getMintAnnualProvisions() {
return this.request(this.registry.mint_annual_provisions, {});
}
// ibc
async getIBCAppTransferDenom(hash: string) {
return this.request(this.registry.ibc_app_transfer_denom_traces_hash, {
hash,
});
}
async getIBCConnections() {
return this.request(this.registry.ibc_core_connection_connections, {});
}
async getIBCConnectionsById(connection_id: string) {
return this.request(
this.registry.ibc_core_connection_connections_connection_id,
{ connection_id }
);
}
async getIBCConnectionsClientState(connection_id: string) {
return this.request(
this.registry.ibc_core_connection_connections_connection_id_client_state,
{ connection_id }
);
}
async getIBCConnectionsChannels(connection_id: string) {
return this.request(this.registry.ibc_core_channel_connections_channels, {
connection_id,
});
}
async getIBCChannels() {
return this.request(this.registry.ibc_core_channel_channels, {});
}
async getIBCChannelAcknowledgements(channel_id: string, port_id: string) {
return this.request(
this.registry.ibc_core_channel_channels_acknowledgements,
{ channel_id, port_id }
);
}
async getIBCChannelNextSequence(channel_id: string, port_id: string) {
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);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
@ -27,22 +30,22 @@ try {
// */
export async function get(url: string) {
return (await fetch(url)).json()
return (await fetch(url)).json();
}
export async function post(url: string, data: any) {
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
// mode: 'cors', // no-cors, *cors, same-origin
// credentials: 'same-origin', // redirect: 'follow', // manual, *follow, error
// referrerPolicy: 'origin', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
headers: {
'Content-Type': 'text/plain',
Accept: '*/*',
'Accept-Encoding': 'gzip, deflate, br',
},
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
// const response = axios.post((config ? config.api : this.config.api) + url, data)
return response.json() // parses JSON response into native JavaScript objects
}
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
// mode: 'cors', // no-cors, *cors, same-origin
// credentials: 'same-origin', // redirect: 'follow', // manual, *follow, error
// referrerPolicy: 'origin', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
headers: {
'Content-Type': 'text/plain',
Accept: '*/*',
'Accept-Encoding': 'gzip, deflate, br',
},
body: JSON.stringify(data), // body data type must match "Content-Type" header
});
// const response = axios.post((config ? config.api : this.config.api) + url, data)
return response.json(); // parses JSON response into native JavaScript objects
}

View File

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

View File

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

View File

@ -1,61 +1,101 @@
import type { AuthAccount, Block, ClientStateWithProof, Coin, ConnectionWithProof, 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";
import type {
AuthAccount,
Block,
ClientStateWithProof,
Coin,
ConnectionWithProof,
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> {
url: string,
adapter: (source: any) => T
url: string;
adapter: (source: any) => T;
}
export interface AbstractRegistry {
[key: string]: Request<any>
[key: string]: Request<any>;
}
// use snake style, since the all return object use snake style.
export interface RequestRegistry extends AbstractRegistry {
auth_params: Request<any>
auth_params: Request<any>;
auth_accounts: Request<PaginabledAccounts>;
auth_account_address: Request<{account: AuthAccount}>;
auth_account_address: Request<{ account: AuthAccount }>;
bank_params: Request<BankParams>;
bank_balances_address: Request<PaginatedBalances>;
bank_denoms_metadata: Request<PaginatedDenomMetadata>;
bank_supply: Request<PaginatedSupply>;
bank_supply_by_denom: Request<{amount: Coin}>;
bank_supply_by_denom: Request<{ amount: Coin }>;
distribution_params: Request<DistributionParams>;
distribution_validator_commission: Request<{commission?: {commission?: Coin[]}}>;
distribution_validator_outstanding_rewards: Request<{rewards?: {rewards?: Coin[]}}>;
distribution_validator_commission: Request<{
commission?: { commission?: Coin[] };
}>;
distribution_validator_outstanding_rewards: Request<{
rewards?: { rewards?: Coin[] };
}>;
distribution_validator_slashes: Request<PaginatedSlashes>;
distribution_community_pool: Request<{pool: Coin[]}>;
distribution_community_pool: Request<{ pool: Coin[] }>;
distribution_delegator_rewards: Request<any>;
mint_inflation: Request<{inflation: string}>;
mint_inflation: Request<{ inflation: string }>;
mint_params: Request<{
params: {
mint_denom: string,
blocks_per_year: string
}
mint_denom: string;
blocks_per_year: string;
};
}>;
mint_annual_provisions: Request<{annual_provisions: string}>
mint_annual_provisions: Request<{ annual_provisions: string }>;
slashing_params: Request<any>;
slashing_signing_info: Request<PaginatedSigningInfo>;
gov_params_voting: Request<GovParams>;
gov_params_tally: Request<GovParams>;
gov_params_deposit: Request<GovParams>;
gov_proposals: Request<PaginatedProposals>;
gov_proposals_proposal_id: Request<{proposal: GovProposal}>;
gov_proposals_proposal_id: Request<{ proposal: GovProposal }>;
gov_proposals_deposits: Request<PaginatedProposalDeposit>;
gov_proposals_tally: Request<{tally: Tally}>;
gov_proposals_tally: Request<{ tally: Tally }>;
gov_proposals_votes: Request<PaginatedProposalVotes>;
gov_proposals_votes_voter: Request<{vote: GovVote}>;
gov_proposals_votes_voter: Request<{ vote: GovVote }>;
staking_deletations: Request<PaginatedDelegations>;
staking_delegator_redelegations: Request<PaginatedRedelegations>;
@ -64,9 +104,11 @@ export interface RequestRegistry extends AbstractRegistry {
staking_params: Request<StakingParam>;
staking_pool: Request<StakingPool>;
staking_validators: Request<PaginatedValdiators>;
staking_validators_address: Request<{validator: Validator}>;
staking_validators_address: Request<{ validator: Validator }>;
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>;
base_tendermint_abci_query: Request<any>;
@ -78,45 +120,50 @@ export interface RequestRegistry extends AbstractRegistry {
tx_txs: Request<PaginatedTxs>;
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_host_params: Request<any>
ibc_app_ica_host_params: Request<any>;
ibc_app_transfer_escrow_address: Request<any>;
ibc_app_transfer_denom_traces: Request<any>;
ibc_app_transfer_denom_traces_hash: Request<{
denom_trace: DenomTrace
denom_trace: DenomTrace;
}>;
ibc_core_channel_channels: Request<PaginatedIBCChannels>;
ibc_core_channel_channels_next_sequence: Request<{
next_sequence_receive: string,
proof: string,
next_sequence_receive: string;
proof: string;
proof_height: {
revision_number: string,
revision_height: string
}
revision_number: string;
revision_height: string;
};
}>;
ibc_core_channel_channels_acknowledgements: Request<any>;
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_client_state: Request<ClientStateWithProof>;
}
export function adapter<T>(source: any): T {
return source
return source;
}
export interface Registry {
[key: string]: RequestRegistry;
}
export function withCustomAdapter<T extends RequestRegistry>(target: T, source?: Partial<T>): T {
return source ? Object.assign({}, target, source): target;
export function withCustomAdapter<T extends RequestRegistry>(
target: T,
source?: Partial<T>
): T {
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];
if (!url) {
throw new Error(`Unsupported version or name: ${name}`);
@ -125,7 +172,10 @@ export function findConfigByName(name: string, registry: Registry): RequestRegis
return url;
}
export function findConfigByVersion(version: string, registry: Registry): RequestRegistry {
export function findConfigByVersion(
version: string,
registry: Registry
): RequestRegistry {
let closestVersion: string | null = null;
for (const key in registry) {

View File

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

View File

@ -1,290 +1,350 @@
<script lang="ts" setup>
import { useBlockchain, useFormatter, useStakingStore } from '@/stores';
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 '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';
const props = defineProps(['address', 'chain'])
const props = defineProps(['address', 'chain']);
const blockchain = useBlockchain()
const stakingStore = useStakingStore()
const format = useFormatter()
const account = ref({} as AuthAccount)
const txs = ref({} as TxResponse[])
const delegations = ref([] as Delegation[])
const rewards = ref({} as DelegatorRewards)
const balances = ref([] as Coin[])
const unbonding = ref([] as UnbondingResponses[])
const unbondingTotal = ref(0)
const chart = {}
const blockchain = useBlockchain();
const stakingStore = useStakingStore();
const format = useFormatter();
const account = ref({} as AuthAccount);
const txs = ref({} as TxResponse[]);
const delegations = ref([] as Delegation[]);
const rewards = ref({} as DelegatorRewards);
const balances = ref([] as Coin[]);
const unbonding = ref([] as UnbondingResponses[]);
const unbondingTotal = ref(0);
const chart = {};
const totalAmountByCategory = computed(()=> {
let sumDel = 0;
delegations.value?.forEach(x => {
sumDel += Number(x.balance.amount)
})
let sumRew = 0
rewards.value?.total?.forEach(x => {
sumRew += Number(x.amount)
})
let sumBal = 0
balances.value?.forEach(x => {
sumBal += Number(x.amount)
})
let sumUn = 0
unbonding.value?.forEach(x => {
x.entries?.forEach(y => {
sumUn += Number(y.balance)
})
})
return [sumBal, sumDel, sumRew, sumUn]
})
const totalAmountByCategory = computed(() => {
let sumDel = 0;
delegations.value?.forEach((x) => {
sumDel += Number(x.balance.amount);
});
let sumRew = 0;
rewards.value?.total?.forEach((x) => {
sumRew += Number(x.amount);
});
let sumBal = 0;
balances.value?.forEach((x) => {
sumBal += Number(x.amount);
});
let sumUn = 0;
unbonding.value?.forEach((x) => {
x.entries?.forEach((y) => {
sumUn += Number(y.balance);
});
});
return [sumBal, sumDel, sumRew, sumUn];
});
const labels = ['Balance', 'Delegation', 'Reward', 'Unbonding']
const totalAmount= computed(()=> {
return totalAmountByCategory.value.reduce((p, c)=> c + p, 0)
})
const labels = ['Balance', 'Delegation', 'Reward', 'Unbonding'];
const totalAmount = computed(() => {
return totalAmountByCategory.value.reduce((p, c) => c + p, 0);
});
function loadAccount(address: string) {
blockchain.rpc.getAuthAccount(address).then(x => {
account.value = x.account
})
blockchain.rpc.getTxsBySender(address).then(x => {
txs.value = x.tx_responses
})
blockchain.rpc.getDistributionDelegatorRewards(address).then(x => {
rewards.value = x
})
blockchain.rpc.getStakingDelegations(address).then(x => {
delegations.value = x.delegation_responses
})
blockchain.rpc.getBankBalances(address).then(x => {
balances.value = x.balances
})
blockchain.rpc.getStakingDelegatorUnbonding(address).then(x => {
unbonding.value = x.unbonding_responses
x.unbonding_responses?.forEach(y => {
y.entries.forEach(z => {
unbondingTotal.value += Number(z.balance)
})
})
})
blockchain.rpc.getAuthAccount(address).then((x) => {
account.value = x.account;
});
blockchain.rpc.getTxsBySender(address).then((x) => {
txs.value = x.tx_responses;
});
blockchain.rpc.getDistributionDelegatorRewards(address).then((x) => {
rewards.value = x;
});
blockchain.rpc.getStakingDelegations(address).then((x) => {
delegations.value = x.delegation_responses;
});
blockchain.rpc.getBankBalances(address).then((x) => {
balances.value = x.balances;
});
blockchain.rpc.getStakingDelegatorUnbonding(address).then((x) => {
unbonding.value = x.unbonding_responses;
x.unbonding_responses?.forEach((y) => {
y.entries.forEach((z) => {
unbondingTotal.value += Number(z.balance);
});
});
});
}
loadAccount(props.address)
loadAccount(props.address);
</script>
<template>
<div v-if="account">
<VCard>
<VList>
<VListItem>
<div v-if="account">
<VCard>
<VList>
<VListItem>
<template #prepend>
<VAvatar rounded variant="tonal" size="45" color="primary">
<VIcon icon="mdi-qrcode" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
Address:
</VListItemTitle>
<VListItemSubtitle class="text-xs">
{{ address }}
</VListItemSubtitle>
</VListItem>
</VList>
</VCard>
<VCard class="mt-5">
<VCardTitle>Assets</VCardTitle>
<VCardItem>
<VRow>
<VCol cols="12" md="4">
<DonutChart :series="totalAmountByCategory" :labels="labels" />
</VCol>
<VCol cols="12" md="8">
<VList class="card-list">
<VListItem v-for="v in balances">
<template #prepend>
<VAvatar
rounded
variant="tonal"
size="45"
color="primary"
>
<VIcon icon="mdi-qrcode" />
</VAvatar>
<VAvatar rounded variant="tonal" size="35" color="info">
<VIcon icon="mdi-account-cash" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken(v) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs">
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{
format.calculatePercent(v.amount, totalAmount)
}}</VChip>
</template>
</VListItem>
<VListItem v-for="v in delegations">
<template #prepend>
<VAvatar rounded variant="tonal" size="35" color="warning">
<VIcon icon="mdi-user-clock" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
Address:
{{ format.formatToken(v.balance) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs">
{{ address }}
${{ 0 }}
</VListItemSubtitle>
</VListItem>
<template #append>
<VChip color="primary">{{
format.calculatePercent(v.balance.amount, totalAmount)
}}</VChip>
</template>
</VListItem>
<VListItem v-for="v in rewards.total">
<template #prepend>
<VAvatar rounded variant="tonal" size="35" color="success">
<VIcon icon="mdi-account-arrow-up" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken(v) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs">
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{
format.calculatePercent(v.amount, totalAmount)
}}</VChip>
</template>
</VListItem>
<VListItem>
<template #prepend>
<VAvatar rounded variant="tonal" size="35" color="error">
<VIcon icon="mdi-account-arrow-right" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{
format.formatToken({
amount: String(unbondingTotal),
denom: stakingStore.params.bond_denom,
})
}}
</VListItemTitle>
<VListItemSubtitle class="text-xs">
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{
format.calculatePercent(unbondingTotal, totalAmount)
}}</VChip>
</template>
</VListItem>
</VList>
</VCard>
<VDivider class="my-2"></VDivider>
{{ totalAmount }}
</VCol>
</VRow>
</VCardItem>
</VCard>
<VCard class="mt-5">
<VCardTitle>Assets</VCardTitle>
<VCardItem>
<VRow>
<VCol cols="12" md="4">
<DonutChart :series="totalAmountByCategory" :labels="labels"/>
</VCol>
<VCol cols="12" md="8">
<VList class="card-list">
<VListItem v-for="v in balances">
<template #prepend>
<VAvatar
rounded
variant="tonal"
size="35"
color="info"
>
<VIcon icon="mdi-account-cash" size="20"/>
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken(v) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs">
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{ format.calculatePercent(v.amount, totalAmount) }}</VChip>
</template>
</VListItem>
<VListItem v-for="v in delegations">
<template #prepend>
<VAvatar
rounded
variant="tonal"
size="35"
color="warning"
>
<VIcon icon="mdi-user-clock" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken(v.balance) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs">
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{ format.calculatePercent(v.balance.amount, totalAmount) }}</VChip>
</template>
</VListItem>
<VListItem v-for="v in rewards.total">
<template #prepend>
<VAvatar
rounded
variant="tonal"
size="35"
color="success"
>
<VIcon icon="mdi-account-arrow-up" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken(v) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs">
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{ format.calculatePercent(v.amount, totalAmount) }}</VChip>
</template>
</VListItem>
<VListItem>
<template #prepend>
<VAvatar
rounded
variant="tonal"
size="35"
color="error"
>
<VIcon icon="mdi-account-arrow-right" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken({amount: String(unbondingTotal), denom: stakingStore.params.bond_denom}) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs">
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{ format.calculatePercent(unbondingTotal, totalAmount) }}</VChip>
</template>
</VListItem>
</VList>
<VDivider class="my-2"></VDivider>
{{ totalAmount }}
</VCol>
</VRow>
</VCardItem>
</VCard>
<VCard class="my-5">
<VCardItem>
<VCardTitle>Delegations</VCardTitle>
<VTable>
<thead>
<tr><th>Validator</th><th>Delegation</th><th>Rewards</th><th>Action</th></tr>
</thead>
<tbody>
<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>{{ 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>
</tbody>
</VTable>
</VCardItem>
</VCard>
<VCard class="my-5" v-if="unbonding && unbonding.length > 0">
<VCardItem>
<VCardTitle>Unbonding Delegations</VCardTitle>
<VTable>
<thead>
<tr><th>Creation Height</th><th>Initial Balance</th><th>Balance</th><th>Completion Time</th></tr>
</thead>
<tbody>
<div v-for="v in unbonding">
<tr>
<td class="text-caption text-primary"><RouterLink :to="`/${chain}/staking/${v.validator_address}`">{{ format.validatorFromBech32(v.validator_address) }}</RouterLink></td>
</tr>
<tr v-for="entry in v.entries">
<td>{{ entry.creation_height }}</td>
<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>
</div>
</tbody>
</VTable>
</VCardItem>
</VCard>
<VCard class="my-5">
<VCardItem>
<VCardTitle>Transactions</VCardTitle>
<VTable>
<thead>
<tr><th>Height</th><th>Hash</th><th>Messages</th><th>Time</th></tr>
</thead>
<tbody>
<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-truncate" style="max-width: 200px;">{{ v.txhash }} </td>
<td>
{{ format.messages(v.tx.body.messages) }}
<VIcon v-if="v.code === 0" icon="mdi-check" color="success"></VIcon>
<VIcon v-else icon="mdi-multiply" color="error"></VIcon> </td>
<td>{{ format.toDay(v.timestamp, "from") }}</td>
</tr>
</tbody>
</VTable>
</VCardItem>
</VCard>
<VCard>
<VCardItem>
<VCardTitle>Account</VCardTitle>
<DynamicComponent :value="account"/>
</VCardItem>
</VCard>
</div>
<div v-else>
Account does not exists on chain
</div>
<VCard class="my-5">
<VCardItem>
<VCardTitle>Delegations</VCardTitle>
<VTable>
<thead>
<tr>
<th>Validator</th>
<th>Delegation</th>
<th>Rewards</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<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>{{ 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>
</tbody>
</VTable>
</VCardItem>
</VCard>
<VCard class="my-5" v-if="unbonding && unbonding.length > 0">
<VCardItem>
<VCardTitle>Unbonding Delegations</VCardTitle>
<VTable>
<thead>
<tr>
<th>Creation Height</th>
<th>Initial Balance</th>
<th>Balance</th>
<th>Completion Time</th>
</tr>
</thead>
<tbody>
<div v-for="v in unbonding">
<tr>
<td class="text-caption text-primary">
<RouterLink
:to="`/${chain}/staking/${v.validator_address}`"
>{{
format.validatorFromBech32(v.validator_address)
}}</RouterLink
>
</td>
</tr>
<tr v-for="entry in v.entries">
<td>{{ entry.creation_height }}</td>
<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>
</div>
</tbody>
</VTable>
</VCardItem>
</VCard>
<VCard class="my-5">
<VCardItem>
<VCardTitle>Transactions</VCardTitle>
<VTable>
<thead>
<tr>
<th>Height</th>
<th>Hash</th>
<th>Messages</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<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-truncate" style="max-width: 200px">
{{ v.txhash }}
</td>
<td>
{{ format.messages(v.tx.body.messages) }}
<VIcon
v-if="v.code === 0"
icon="mdi-check"
color="success"
></VIcon>
<VIcon v-else icon="mdi-multiply" color="error"></VIcon>
</td>
<td>{{ format.toDay(v.timestamp, 'from') }}</td>
</tr>
</tbody>
</VTable>
</VCardItem>
</VCard>
<VCard>
<VCardItem>
<VCardTitle>Account</VCardTitle>
<DynamicComponent :value="account" />
</VCardItem>
</VCard>
</div>
<div v-else>Account does not exists on chain</div>
</template>
<style lang="scss" scoped>
.card-list {
--v-card-list-gap: 5px;
}
</style>
</style>

View File

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

View File

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

View File

@ -31,7 +31,7 @@ const format = useFormatter();
<table class="table w-full">
<thead>
<tr>
<th style="position: relative;">Height</th>
<th style="position: relative">Height</th>
<th>Hash</th>
<th>Proposer</th>
<th>Txs</th>
@ -57,11 +57,14 @@ const format = useFormatter();
</table>
</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">
<thead>
<tr>
<th style="position: relative;">Hash</th>
<th style="position: relative">Hash</th>
<th>Messages</th>
<th>Fees</th>
</tr>
@ -73,7 +76,7 @@ const format = useFormatter();
item.hash
}}</RouterLink>
</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>
</tr>
</tbody>

View File

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

View File

@ -1,69 +1,77 @@
<script lang="ts" setup>
import { fromHex } from "@cosmjs/encoding";
import { fromHex } from '@cosmjs/encoding';
import { useWasmStore } from '../WasmStore';
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 type CustomRadiosVue from '@/plugins/vuetify/@core/components/CustomRadios.vue';
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()
wasmStore.wasmClient.getWasmCodeContracts(props.code_id).then(x =>{
response.value = x
})
const format = useFormatter()
const infoDialog = ref(false)
const stateDialog = ref(false)
const queryDialog = ref(false)
const info = ref({} as ContractInfo)
const state = ref( {} as PaginabledContractStates)
const selected = ref("")
const wasmStore = useWasmStore();
wasmStore.wasmClient.getWasmCodeContracts(props.code_id).then((x) => {
response.value = x;
});
const format = useFormatter();
const infoDialog = ref(false);
const stateDialog = ref(false);
const queryDialog = ref(false);
const info = ref({} as ContractInfo);
const state = ref({} as PaginabledContractStates);
const selected = ref('');
function showInfo(address: string) {
wasmStore.wasmClient.getWasmContracts(address).then(x => {
info.value = x.contract_info
infoDialog.value = true
})
wasmStore.wasmClient.getWasmContracts(address).then((x) => {
info.value = x.contract_info;
infoDialog.value = true;
});
}
function showState(address: string) {
wasmStore.wasmClient.getWasmContractStates(address).then(x => {
state.value = x
stateDialog.value = true
})
wasmStore.wasmClient.getWasmContractStates(address).then((x) => {
state.value = x;
stateDialog.value = true;
});
}
function showQuery(address: string) {
queryDialog.value = true
selected.value = address
query.value = ""
result.value = ""
queryDialog.value = true;
selected.value = address;
query.value = '';
result.value = '';
}
function queryContract() {
try{
if(selectedRadio.value === 'raw') {
wasmStore.wasmClient.getWasmContractRawQuery(selected.value, query.value).then(x => {
result.value = JSON.stringify(x)
}).catch(err => {
result.value = JSON.stringify(err)
})
} else {
wasmStore.wasmClient.getWasmContractSmartQuery(selected.value, query.value).then(x => {
result.value = JSON.stringify(x)
}).catch(err => {
result.value = JSON.stringify(err)
})
}
} catch(err) {
result.value = JSON.stringify(err) // not works for now
try {
if (selectedRadio.value === 'raw') {
wasmStore.wasmClient
.getWasmContractRawQuery(selected.value, query.value)
.then((x) => {
result.value = JSON.stringify(x);
})
.catch((err) => {
result.value = JSON.stringify(err);
});
} else {
wasmStore.wasmClient
.getWasmContractSmartQuery(selected.value, query.value)
.then((x) => {
result.value = JSON.stringify(x);
})
.catch((err) => {
result.value = JSON.stringify(err);
});
}
// TODO, show error in the result.
} catch (err) {
result.value = JSON.stringify(err); // not works for now
}
// TODO, show error in the result.
}
const radioContent: CustomInputContent[] = [
@ -77,78 +85,90 @@ const radioContent: CustomInputContent[] = [
desc: 'Return structure result if possible',
value: 'smart',
},
]
const selectedRadio = ref('raw')
const query = ref("")
const result = ref("")
];
const selectedRadio = ref('raw');
const query = ref('');
const result = ref('');
</script>
<template>
<div>
<VCard>
<VCardTitle>Contract List of Code: {{ props.code_id }}</VCardTitle>
<VTable>
<thead>
<tr><th>Contract List</th><th>Actions</th></tr>
</thead>
<tbody>
<tr v-for="v in response.contracts">
<td>{{ v }}</td><td>
<VBtn size="small" @click="showInfo(v)">contract</VBtn>
<VBtn size="small" @click="showState(v)" class="ml-2">States</VBtn>
<VBtn size="small" @click="showQuery(v)" class="ml-2">Query</VBtn></td>
</tr>
</tbody>
</VTable>
</VCard>
<v-dialog v-model="infoDialog" width="auto">
<v-card>
<VCardTitle>Contract Detail</VCardTitle>
<v-card-text>
<DynamicComponent :value="info"/>
</v-card-text>
<v-card-actions>
<v-btn color="primary" block @click="infoDialog = false">Close Dialog</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="stateDialog" width="auto">
<v-card>
<VCardTitle>Contract States</VCardTitle>
<VList>
<VListItem v-for="v in state.models">
<VListItemTitle>
{{ format.hexToString(v.key) }}
</VListItemTitle>
<VListItemSubtitle :title="format.base64ToString(v.value)">
{{ format.base64ToString(v.value) }}
</VListItemSubtitle>
</VListItem>
</VList>
<v-card-actions>
<v-btn color="primary" block @click="stateDialog = false">Close Dialog</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="queryDialog" width="auto">
<v-card>
<VCardTitle>Query Contract</VCardTitle>
<v-card-text>
<CustomRadios
v-model:selected-radio="selectedRadio"
:radio-content="radioContent"
:grid-column="{ sm: '6', cols: '12' }"
/>
<VTextarea v-model="query" label="Query String" class="my-2"/>
<VTextarea v-model="result" label="Result"/>
</v-card-text>
<v-card-actions>
<v-btn color="primary" @click="queryDialog = false">Close Dialog</v-btn>
<v-btn color="success" @click="queryContract()">Query Contract</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
<div>
<VCard>
<VCardTitle>Contract List of Code: {{ props.code_id }}</VCardTitle>
<VTable>
<thead>
<tr>
<th>Contract List</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="v in response.contracts">
<td>{{ v }}</td>
<td>
<VBtn size="small" @click="showInfo(v)">contract</VBtn>
<VBtn size="small" @click="showState(v)" class="ml-2"
>States</VBtn
>
<VBtn size="small" @click="showQuery(v)" class="ml-2">Query</VBtn>
</td>
</tr>
</tbody>
</VTable>
</VCard>
<v-dialog v-model="infoDialog" width="auto">
<v-card>
<VCardTitle>Contract Detail</VCardTitle>
<v-card-text>
<DynamicComponent :value="info" />
</v-card-text>
<v-card-actions>
<v-btn color="primary" block @click="infoDialog = false"
>Close Dialog</v-btn
>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="stateDialog" width="auto">
<v-card>
<VCardTitle>Contract States</VCardTitle>
<VList>
<VListItem v-for="v in state.models">
<VListItemTitle>
{{ format.hexToString(v.key) }}
</VListItemTitle>
<VListItemSubtitle :title="format.base64ToString(v.value)">
{{ format.base64ToString(v.value) }}
</VListItemSubtitle>
</VListItem>
</VList>
<v-card-actions>
<v-btn color="primary" block @click="stateDialog = false"
>Close Dialog</v-btn
>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="queryDialog" width="auto">
<v-card>
<VCardTitle>Query Contract</VCardTitle>
<v-card-text>
<CustomRadios
v-model:selected-radio="selectedRadio"
:radio-content="radioContent"
:grid-column="{ sm: '6', cols: '12' }"
/>
<VTextarea v-model="query" label="Query String" class="my-2" />
<VTextarea v-model="result" label="Result" />
</v-card-text>
<v-card-actions>
<v-btn color="primary" @click="queryDialog = false"
>Close Dialog</v-btn
>
<v-btn color="success" @click="queryContract()">Query Contract</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>

View File

@ -4,38 +4,52 @@ import { useWasmStore } from './WasmStore';
import { ref } from 'vue';
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()
wasmStore.wasmClient.getWasmCodeList().then(x =>{
codes.value = x
})
const wasmStore = useWasmStore();
wasmStore.wasmClient.getWasmCodeList().then((x) => {
codes.value = x;
});
</script>
<template>
<div>
<VCard>
<VCardTitle>Cosmos Wasm Smart Contracts</VCardTitle>
<VTable>
<thead>
<tr><th>Code Id</th><th>Code Hash</th><th>Creator</th><th>Permissions</th></tr>
</thead>
<tbody>
<tr v-for="v in codes.code_infos">
<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>{{ v.creator }}</td>
<td>
{{ v.instantiate_permission?.permission }}
<span>{{ v.instantiate_permission?.address }} {{ v.instantiate_permission.addresses.join(", ") }}</span>
</td>
</tr>
</tbody>
</VTable>
</VCard>
</div>
<div>
<VCard>
<VCardTitle>Cosmos Wasm Smart Contracts</VCardTitle>
<VTable>
<thead>
<tr>
<th>Code Id</th>
<th>Code Hash</th>
<th>Creator</th>
<th>Permissions</th>
</tr>
</thead>
<tbody>
<tr v-for="v in codes.code_infos">
<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>{{ v.creator }}</td>
<td>
{{ v.instantiate_permission?.permission }}
<span
>{{ v.instantiate_permission?.address }}
{{ v.instantiate_permission.addresses.join(', ') }}</span
>
</td>
</tr>
</tbody>
</VTable>
</VCard>
</div>
</template>
<route>
@ -44,4 +58,4 @@ wasmStore.wasmClient.getWasmCodeList().then(x =>{
i18n: 'cosmwasm'
}
}
</route>
</route>

View File

@ -1,69 +1,69 @@
import type { PaginatedResponse } from "@/types"
import type { PaginatedResponse } from '@/types';
export interface CodeInfo {
code_id: string,
creator: string,
data_hash: string,
instantiate_permission: {
permission: string,
address: string,
addresses: string[]
}
code_id: string;
creator: string;
data_hash: string;
instantiate_permission: {
permission: string;
address: string;
addresses: string[];
};
}
export interface ContractInfo {
code_id: string,
creator: string,
admin: string,
label: string,
created: {
block_height: string,
tx_index: string
},
ibc_port_id: string,
extension: {
type_url: string,
value: string
}
}
code_id: string;
creator: string;
admin: string;
label: string;
created: {
block_height: string;
tx_index: string;
};
ibc_port_id: string;
extension: {
type_url: string;
value: string;
};
}
export interface WasmParam {
params: {
code_upload_access: {
permission: string,
address: string,
addresses: string[]
},
instantiate_default_permission: string
}
params: {
code_upload_access: {
permission: string;
address: string;
addresses: string[];
};
instantiate_default_permission: string;
};
}
export interface HistoryEntry {
operation: string,
code_id: string,
updated: {
block_height: string,
tx_index: string
},
msg: string
operation: string;
code_id: string;
updated: {
block_height: string;
tx_index: string;
};
msg: string;
}
export interface Models {
key: string,
value: string
key: string;
value: string;
}
export interface PaginabledContractHistory extends PaginatedResponse {
entries: HistoryEntry[]
entries: HistoryEntry[];
}
export interface PaginabledContractStates extends PaginatedResponse {
models: Models[]
models: Models[];
}
export interface PaginabledCodeInfos extends PaginatedResponse {
code_infos: CodeInfo[]
code_infos: CodeInfo[];
}
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 { computed } from '@vue/reactivity';
const props = defineProps(["proposal_id", "chain"]);
const proposal = ref({} as GovProposal)
const format = useFormatter()
@ -147,7 +145,6 @@ const processList = computed(()=>{
{name: 'Abstain', value : abstain.value, class: 'bg-warning' }
]
})
</script>
<template>

View File

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

View File

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

View File

@ -4,44 +4,58 @@ import type { Connection } from '@/types';
import { onMounted } from 'vue';
import { ref } from 'vue';
const props = defineProps(['chain'])
const chainStore = useBlockchain()
const list = ref([] as Connection[])
const props = defineProps(['chain']);
const chainStore = useBlockchain();
const list = ref([] as Connection[]);
onMounted(() => {
chainStore.rpc.getIBCConnections().then(x => {
list.value = x.connections
})
})
chainStore.rpc.getIBCConnections().then((x) => {
list.value = x.connections;
});
});
function color(v: string) {
if(v && v.indexOf("_OPEN") > -1) {
return "success"
}
return "warning"
if (v && v.indexOf('_OPEN') > -1) {
return 'success';
}
return 'warning';
}
</script>
<template>
<div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
<h2 class="card-title">IBC Connections</h2>
<div class="overflow-x-auto mt-4">
<table class="table w-full">
<thead>
<tr><th style="position: relative;">Connection Id</th><th>Connection</th><th>Delay Period</th><th>State</th></tr>
</thead>
<tbody>
<tr v-for="v in list">
<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><VChip :color="color(v.state)">{{ v.state }}</VChip></td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
<h2 class="card-title">IBC Connections</h2>
<div class="overflow-x-auto mt-4">
<table class="table w-full">
<thead>
<tr>
<th style="position: relative">Connection Id</th>
<th>Connection</th>
<th>Delay Period</th>
<th>State</th>
</tr>
</thead>
<tbody>
<tr v-for="v in list">
<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>
<VChip :color="color(v.state)">{{ v.state }}</VChip>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<route>
@ -50,4 +64,4 @@ function color(v: string) {
i18n: 'ibc'
}
}
</route>
</route>

View File

@ -25,12 +25,18 @@ const format = useFormatter();
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
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();
}
});
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>
@ -47,27 +53,51 @@ function shortName(name: string, id: string) {
</VCardTitle>
<VCardSubtitle>
Rank:
<VChip color="error" size="x-small">#{{ coinInfo.market_cap_rank }}</VChip>
<VChip color="error" size="x-small"
>#{{ coinInfo.market_cap_rank }}</VChip
>
</VCardSubtitle>
</VCardItem>
<VDivider />
<VCardItem>
<VBtn variant="text" size="small" :href="store.homepage" prependIcon="mdi-web">
<VBtn
variant="text"
size="small"
:href="store.homepage"
prependIcon="mdi-web"
>
Website
</VBtn>
<VBtn variant="text" size="small" :href="store.twitter" prependIcon="mdi-twitter">
<VBtn
variant="text"
size="small"
:href="store.twitter"
prependIcon="mdi-twitter"
>
Twitter
</VBtn>
<VBtn variant="text" size="small" :href="store.telegram" prependIcon="mdi-telegram">
<VBtn
variant="text"
size="small"
:href="store.telegram"
prependIcon="mdi-telegram"
>
Telegram
</VBtn>
<VBtn variant="text" size="small" :href="store.github" prependIcon="mdi-github">
<VBtn
variant="text"
size="small"
:href="store.github"
prependIcon="mdi-github"
>
Github
</VBtn>
</VCardItem>
<VCardItem>
<!-- 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">
{{ store.priceChange }}
<small>%</small>
@ -102,7 +132,10 @@ function shortName(name: string, id: string) {
}}
</VListItemSubtitle>
<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 }}
</span>
</template>
@ -119,9 +152,13 @@ function shortName(name: string, id: string) {
<span class="text-h5">{{ ticker.converted_last.usd }}</span>
</div>
</div>
<!-- !SECTION -->
<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 || '' }}
</VBtn>
</VCardItem>
@ -134,10 +171,15 @@ function shortName(name: string, id: string) {
</VRow>
<VDivider />
<VCardText style="max-height: 250px; overflow: auto">
<MdEditor :model-value="coinInfo.description?.en" previewOnly></MdEditor>
<MdEditor
:model-value="coinInfo.description?.en"
previewOnly
></MdEditor>
</VCardText>
<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>
</VCard>
@ -154,10 +196,14 @@ function shortName(name: string, id: string) {
<VCardItem>
<ProposalListItem :proposals="store?.proposals" />
</VCardItem>
<VCardText v-if="store.proposals.length === 0">No active proposals</VCardText>
<VCardText v-if="store.proposals.length === 0"
>No active proposals</VCardText
>
</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>
</template>

View File

@ -1,212 +1,234 @@
import { useBlockchain, useCoingecko, useBaseStore, useBankStore, useFormatter, 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";
import {
useBlockchain,
useCoingecko,
useBaseStore,
useBankStore,
useFormatter,
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) {
switch (color) {
case 'yellow':
return 'warning'
case 'green':
return 'success'
default:
return 'secondary'
}
switch (color) {
case 'yellow':
return 'warning';
case 'green':
return 'success';
default:
return 'secondary';
}
}
export const useIndexModule = defineStore('module-index', {
state: () => {
return {
days: 14,
tickerIndex: 0,
coinInfo: {
name: '',
symbol: '',
description: {
en: ''
},
categories: [] as string[],
market_cap_rank: 0,
links: {
twitter_screen_name: '',
homepage: [] as string[],
repos_url: {
github: []
},
telegram_channel_identifier: ''
},
market_data: {
price_change_percentage_24h: 0
},
tickers: [] as {
market: {
name: string,
identifier: string,
},
coin_id: string,
target_coin_id: string,
trust_score: string,
trade_url: string,
converted_last: {
btc: number,
eth: number,
usd: number,
},
base: string,
target: string,
}[]
},
marketData: {
market_caps: [],
prices: [] as number[],
total_volumes: [] as number[],
},
communityPool: [] as {amount: string, denom: string}[],
proposals: {} as PaginatedProposals,
tally: {} as Record<string, Tally>
}
state: () => {
return {
days: 14,
tickerIndex: 0,
coinInfo: {
name: '',
symbol: '',
description: {
en: '',
},
categories: [] as string[],
market_cap_rank: 0,
links: {
twitter_screen_name: '',
homepage: [] as string[],
repos_url: {
github: [],
},
telegram_channel_identifier: '',
},
market_data: {
price_change_percentage_24h: 0,
},
tickers: [] as {
market: {
name: string;
identifier: string;
};
coin_id: string;
target_coin_id: string;
trust_score: string;
trade_url: string;
converted_last: {
btc: number;
eth: number;
usd: number;
};
base: string;
target: string;
}[],
},
marketData: {
market_caps: [],
prices: [] as number[],
total_volumes: [] as number[],
},
communityPool: [] as { amount: string; denom: string }[],
proposals: {} as PaginatedProposals,
tally: {} as Record<string, Tally>,
};
},
getters: {
blockchain() {
const chain = useBlockchain();
return chain.current;
},
getters: {
blockchain() {
const chain = useBlockchain()
return chain.current
},
coingecko() {
return useCoingecko()
},
bankStore() {
return useBankStore()
},
twitter() : string {
return `https://twitter.com/${this.coinInfo.links.twitter_screen_name}`
},
homepage(): string {
const [page1, page2, page3] = this.coinInfo.links?.homepage
return page1 || page2 || page3
},
github(): string {
const [page1, page2, page3] = this.coinInfo.links?.repos_url?.github
return page1 || page2 || page3
},
telegram() : string {
return `https://t.me/${this.coinInfo.links.telegram_channel_identifier}`
},
priceChange(): string {
const change = this.coinInfo.market_data?.price_change_percentage_24h || 0
return numeral(change).format('+0.[00]')
},
priceColor() : string {
const change = this.coinInfo.market_data?.price_change_percentage_24h || 0
switch (true) {
case change > 0:
return 'text-success'
case change < 0:
return 'text-error'
default:
return ''
}
},
trustColor() : string {
const change = this.coinInfo.tickers[this.tickerIndex]?.trust_score
return colorMap(change)
},
pool() {
const staking = useStakingStore()
return staking.pool
},
stats () {
const base = useBaseStore()
const bank = useBankStore()
const staking = useStakingStore()
const mintStore = useMintStore()
const formatter = useFormatter()
return [
{
title: 'Height',
color: 'primary',
icon: 'mdi-pound',
stats: String(base.latest.block?.header?.height || 0),
change: 0,
},
{
title: 'Validators',
color: 'error',
icon: 'mdi-human-queue',
stats: String(base.latest.block?.last_commit?.signatures.length || 0),
change: 0,
},
{
title: 'Supply',
color: 'success',
icon: 'mdi-currency-usd',
stats: formatter.formatTokenAmount(bank.supply),
change: 0,
},
{
title: 'Bonded Tokens',
color: 'warning',
icon: 'mdi-lock',
stats: formatter.formatTokenAmount({amount: this.pool.bonded_tokens, denom: staking.params.bond_denom }),
change: 0,
},
{
title: 'Inflation',
color: 'success',
icon: 'mdi-chart-multiple',
stats: formatter.formatDecimalToPercent(mintStore.inflation),
change: 0,
},
{
title: 'Community Pool',
color: 'primary',
icon: 'mdi-bank',
stats: formatter.formatTokens(this.communityPool?.filter(x => x.denom === staking.params.bond_denom)),
change: 0,
},
]
},
coingecko() {
return useCoingecko();
},
actions: {
async loadDashboard() {
this.$reset()
this.initCoingecko()
useMintStore().fetchInflation()
useDistributionStore().fetchCommunityPool().then(x => {
this.communityPool = x.pool.filter(t => t.denom.length < 10).map(t => ({
amount: String(parseInt(t.amount)),
denom: t.denom
}))
})
const gov = useGovStore()
gov.fetchProposals("2").then(x => {
this.proposals = x
})
bankStore() {
return useBankStore();
},
twitter(): string {
return `https://twitter.com/${this.coinInfo.links.twitter_screen_name}`;
},
homepage(): string {
const [page1, page2, page3] = this.coinInfo.links?.homepage;
return page1 || page2 || page3;
},
github(): string {
const [page1, page2, page3] = this.coinInfo.links?.repos_url?.github;
return page1 || page2 || page3;
},
telegram(): string {
return `https://t.me/${this.coinInfo.links.telegram_channel_identifier}`;
},
priceChange(): string {
const change =
this.coinInfo.market_data?.price_change_percentage_24h || 0;
return numeral(change).format('+0.[00]');
},
priceColor(): string {
const change =
this.coinInfo.market_data?.price_change_percentage_24h || 0;
switch (true) {
case change > 0:
return 'text-success';
case change < 0:
return 'text-error';
default:
return '';
}
},
trustColor(): string {
const change = this.coinInfo.tickers[this.tickerIndex]?.trust_score;
return colorMap(change);
},
pool() {
const staking = useStakingStore();
return staking.pool;
},
stats() {
const base = useBaseStore();
const bank = useBankStore();
const staking = useStakingStore();
const mintStore = useMintStore();
const formatter = useFormatter();
return [
{
title: 'Height',
color: 'primary',
icon: 'mdi-pound',
stats: String(base.latest.block?.header?.height || 0),
change: 0,
},
tickerColor(color: string) {
return colorMap(color)
},
initCoingecko() {
this.tickerIndex = 0
const [firstAsset] = this.blockchain?.assets || []
if (firstAsset && firstAsset.coingecko_id) {
this.coingecko.getCoinInfo(firstAsset.coingecko_id).then(x => {
this.coinInfo = x
})
this.coingecko.getMarketChart(this.days, firstAsset.coingecko_id).then(x => {
this.marketData = x
})
}
{
title: 'Validators',
color: 'error',
icon: 'mdi-human-queue',
stats: String(base.latest.block?.last_commit?.signatures.length || 0),
change: 0,
},
selectTicker(i: number) {
this.tickerIndex = i
}
}
})
{
title: 'Supply',
color: 'success',
icon: 'mdi-currency-usd',
stats: formatter.formatTokenAmount(bank.supply),
change: 0,
},
{
title: 'Bonded Tokens',
color: 'warning',
icon: 'mdi-lock',
stats: formatter.formatTokenAmount({
amount: this.pool.bonded_tokens,
denom: staking.params.bond_denom,
}),
change: 0,
},
{
title: 'Inflation',
color: 'success',
icon: 'mdi-chart-multiple',
stats: formatter.formatDecimalToPercent(mintStore.inflation),
change: 0,
},
{
title: 'Community Pool',
color: 'primary',
icon: 'mdi-bank',
stats: formatter.formatTokens(
this.communityPool?.filter(
(x) => x.denom === staking.params.bond_denom
)
),
change: 0,
},
];
},
},
actions: {
async loadDashboard() {
this.$reset();
this.initCoingecko();
useMintStore().fetchInflation();
useDistributionStore()
.fetchCommunityPool()
.then((x) => {
this.communityPool = x.pool
.filter((t) => t.denom.length < 10)
.map((t) => ({
amount: String(parseInt(t.amount)),
denom: t.denom,
}));
});
const gov = useGovStore();
gov.fetchProposals('2').then((x) => {
this.proposals = x;
});
},
tickerColor(color: string) {
return colorMap(color);
},
initCoingecko() {
this.tickerIndex = 0;
const [firstAsset] = this.blockchain?.assets || [];
if (firstAsset && firstAsset.coingecko_id) {
this.coingecko.getCoinInfo(firstAsset.coingecko_id).then((x) => {
this.coinInfo = x;
});
this.coingecko
.getMarketChart(this.days, firstAsset.coingecko_id)
.then((x) => {
this.marketData = x;
});
}
},
selectTicker(i: number) {
this.tickerIndex = i;
},
},
});

View File

@ -1,15 +1,14 @@
<script lang="ts" setup>
import { useParamStore } from '@/stores';
import { ref, onMounted } from 'vue'
import CardParameter from '@/components/CardParameter.vue'
import { ref, onMounted } from 'vue';
import CardParameter from '@/components/CardParameter.vue';
import ArrayObjectElement from '@/components/dynamic/ArrayObjectElement.vue';
const store = useParamStore()
const chain = ref(store.chain)
const store = useParamStore();
const chain = ref(store.chain);
onMounted(() => {
// fetch the data
store.initial()
})
// fetch the data
store.initial();
});
</script>
<template>
<div class="overflow-hidden">
@ -29,28 +28,27 @@ onMounted(() => {
</div>
</div>
</div>
<!-- minting Parameters -->
<CardParameter :cardItem="store.mint"/>
<!-- Staking Parameters -->
<CardParameter :cardItem="store.staking"/>
<!-- Governance Parameters -->
<CardParameter :cardItem="store.gov"/>
<!-- Distribution Parameters -->
<CardParameter :cardItem="store.distribution"/>
<!-- Slashing Parameters -->
<CardParameter :cardItem="store.slashing"/>
<!-- Application Version -->
<div class="bg-base-100 px-4 pt-3 pb-4 rounded-sm mt-6">
<div class="text-base mb-3 text-main">{{ store.appVersion?.title }}</div>
<ArrayObjectElement :value="store.appVersion?.items" :thead="false"/>
</div>
<!-- Node Information -->
<div class="bg-base-100 px-4 pt-3 pb-4ß rounded-sm mt-6">
<div class="text-base mb-3 text-main">{{ store.nodeVersion?.title }}</div>
<ArrayObjectElement :value="store.nodeVersion?.items" :thead="false"/>
</div>
<!-- minting Parameters -->
<CardParameter :cardItem="store.mint" />
<!-- Staking Parameters -->
<CardParameter :cardItem="store.staking" />
<!-- Governance Parameters -->
<CardParameter :cardItem="store.gov" />
<!-- Distribution Parameters -->
<CardParameter :cardItem="store.distribution" />
<!-- Slashing Parameters -->
<CardParameter :cardItem="store.slashing" />
<!-- Application Version -->
<div class="bg-base-100 px-4 pt-3 pb-4 rounded-sm mt-6">
<div class="text-base mb-3 text-main">{{ store.appVersion?.title }}</div>
<ArrayObjectElement :value="store.appVersion?.items" :thead="false" />
</div>
<!-- Node Information -->
<div class="bg-base-100 px-4 pt-3 pb-4ß rounded-sm mt-6">
<div class="text-base mb-3 text-main">{{ store.nodeVersion?.title }}</div>
<ArrayObjectElement :value="store.nodeVersion?.items" :thead="false" />
</div>
</div>
</template>

View File

@ -1,275 +1,399 @@
<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 ValidatorCommissionRate from '@/components/ValidatorCommissionRate.vue'
import { consensusPubkeyToHexAddress, operatorAddressToAccount, pubKeyToValcons, valoperToPrefix } from '@/libs';
import ValidatorCommissionRate from '@/components/ValidatorCommissionRate.vue';
import {
consensusPubkeyToHexAddress,
operatorAddressToAccount,
pubKeyToValcons,
valoperToPrefix,
} from '@/libs';
import type { Coin, Delegation, PaginatedTxs, Validator } from '@/types';
const props = defineProps(['validator', 'chain'])
const props = defineProps(['validator', 'chain']);
const staking = useStakingStore()
const blockchain = useBlockchain()
const format = useFormatter()
const staking = useStakingStore();
const blockchain = useBlockchain();
const format = useFormatter();
const validator: string = props.validator
const validator: string = props.validator;
const v = ref({} as Validator)
const cache = JSON.parse(localStorage.getItem('avatars')||'{}')
const avatars = ref( cache || {} )
const identity = ref("")
const rewards = ref([] as Coin[]|undefined)
const commission = ref([] as Coin[]|undefined)
const addresses = ref({} as {
account: string
operAddress: string
hex: string
valCons: string,
})
const selfBonded = ref({} as Delegation)
const v = ref({} as Validator);
const cache = JSON.parse(localStorage.getItem('avatars') || '{}');
const avatars = ref(cache || {});
const identity = ref('');
const rewards = ref([] as Coin[] | undefined);
const commission = ref([] as Coin[] | undefined);
const addresses = ref(
{} as {
account: string;
operAddress: string;
hex: string;
valCons: string;
}
);
const selfBonded = ref({} as Delegation);
addresses.value.account = operatorAddressToAccount(validator)
addresses.value.account = operatorAddressToAccount(validator);
// load self bond
staking.fetchValidatorDelegation(validator, addresses.value.account).then(x => {
if(x) {
selfBonded.value = x.delegation_response
staking
.fetchValidatorDelegation(validator, addresses.value.account)
.then((x) => {
if (x) {
selfBonded.value = x.delegation_response;
}
})
});
const txs = ref({} as PaginatedTxs)
const txs = ref({} as PaginatedTxs);
blockchain.rpc.getTxsBySender(addresses.value.account).then(x => {
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 rate = v.value.commission?.commission_rates.rate || 0
const inflation = useMintStore().inflation
if(Number(inflation)) {
return format.percent((1 - Number(rate)) * Number(inflation))
}
return "-"
})
const apr = computed(() => {
const rate = v.value.commission?.commission_rates.rate || 0;
const inflation = useMintStore().inflation;
if (Number(inflation)) {
return format.percent((1 - Number(rate)) * Number(inflation));
}
return '-';
});
const selfRate = computed(()=> {
if(selfBonded.value.balance?.amount) {
return format.calculatePercent(selfBonded.value.balance.amount, v.value.tokens)
}
return "-"
})
const selfRate = computed(() => {
if (selfBonded.value.balance?.amount) {
return format.calculatePercent(
selfBonded.value.balance.amount,
v.value.tokens
);
}
return '-';
});
onMounted(()=> {
if(validator) {
staking.fetchValidator(validator).then(res => {
v.value = res.validator
identity.value = res.validator?.description?.identity || ''
if(identity.value && !avatars.value[identity.value]) {
console.log(identity.value, avatars)
staking.keybase(identity.value).then(d => {
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/", "")
if(uri) {
avatars.value[identity.value] = uri
localStorage.setItem('avatars', JSON.stringify(avatars.value))
}
}
})
onMounted(() => {
if (validator) {
staking.fetchValidator(validator).then((res) => {
v.value = res.validator;
identity.value = res.validator?.description?.identity || '';
if (identity.value && !avatars.value[identity.value]) {
console.log(identity.value, avatars);
staking.keybase(identity.value).then((d) => {
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/',
''
);
if (uri) {
avatars.value[identity.value] = uri;
localStorage.setItem('avatars', JSON.stringify(avatars.value));
}
const prefix = valoperToPrefix(v.value.operator_address) || '<Invalid>'
addresses.value.hex = consensusPubkeyToHexAddress(v.value.consensus_pubkey)
addresses.value.valCons = pubKeyToValcons(v.value.consensus_pubkey, prefix)
})
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 => {
commission.value = res.commission?.commission?.sort((a, b) => Number(b.amount) - Number(a.amount))
res.commission?.commission?.forEach(x => {
if(x.denom.startsWith("ibc/")) {
format.fetchDenomTrace(x.denom)
}
})
})
}
})
}
});
}
const prefix = valoperToPrefix(v.value.operator_address) || '<Invalid>';
addresses.value.hex = consensusPubkeyToHexAddress(
v.value.consensus_pubkey
);
addresses.value.valCons = pubKeyToValcons(
v.value.consensus_pubkey,
prefix
);
});
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) => {
commission.value = res.commission?.commission?.sort(
(a, b) => Number(b.amount) - Number(a.amount)
);
res.commission?.commission?.forEach((x) => {
if (x.denom.startsWith('ibc/')) {
format.fetchDenomTrace(x.denom);
}
});
});
}
});
</script>
<template>
<div>
<div>
<VCard class="card-box">
<VCardItem>
<VRow>
<VCol cols="12" md="6">
<div class="d-flex pl-2">
<VAvatar icon="mdi-help-circle-outline" :image="avatars[identity]" size="90" rounded variant="outlined" color="secondary"/>
<div class="mx-2">
<h4>{{ v.description?.moniker }}</h4>
<div class="text-sm mb-2">{{ v.description?.identity || '-'}}</div>
<VBtn>Delegate</VBtn>
</div>
</div>
<VSpacer/>
<VCardText>
<p class="text-md">
About Us
</p>
<VList class="card-list">
<VListItem prepend-icon="mdi-web">
<span>Website: </span><span> {{ v.description?.website || '-' }}</span>
</VListItem>
<VListItem prepend-icon="mdi-email-outline">
<span>Contact: </span><span> {{ v.description?.security_contact }}</span>
</VListItem>
</VList>
<p class="text-md mt-3">
Validator Status
</p>
<VList class="card-list">
<VListItem prepend-icon="mdi-shield-account-outline">
<span>Status: </span><span> {{ String(v.status).replace('BOND_STATUS_', '') }} </span>
</VListItem>
<VListItem prepend-icon="mdi-shield-alert-outline">
<span>Jailed: </span><span> {{ v.jailed || '-' }} </span>
</VListItem>
</VList>
</VCardText>
</VCol>
<VCol cols="12" md="6">
<div class="d-flex flex-column py-3 justify-space-between">
<div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-coin"></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ format.formatToken2({amount: v.tokens, denom: staking.params.bond_denom}) }}</h4>
<span class="text-sm">Total Bonded Tokens</span>
</div>
</div>
<div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-percent"></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ format.formatToken(selfBonded.balance) }} ({{ selfRate }})</h4>
<span class="text-sm">Self Bonded</span>
</div>
</div>
<VCardItem>
<VRow>
<VCol cols="12" md="6">
<div class="d-flex pl-2">
<VAvatar
icon="mdi-help-circle-outline"
:image="avatars[identity]"
size="90"
rounded
variant="outlined"
color="secondary"
/>
<div class="mx-2">
<h4>{{ v.description?.moniker }}</h4>
<div class="text-sm mb-2">
{{ v.description?.identity || '-' }}
</div>
<VBtn>Delegate</VBtn>
</div>
</div>
<VSpacer />
<VCardText>
<p class="text-md">About Us</p>
<VList class="card-list">
<VListItem prepend-icon="mdi-web">
<span>Website: </span
><span> {{ v.description?.website || '-' }}</span>
</VListItem>
<VListItem prepend-icon="mdi-email-outline">
<span>Contact: </span
><span> {{ v.description?.security_contact }}</span>
</VListItem>
</VList>
<div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-account-tie"></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ v.min_self_delegation }} {{ staking.params.bond_denom }}</h4>
<span class="text-sm">Min Self Delegation:</span>
</div>
</div>
<div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-finance"></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ apr }}</h4>
<span class="text-sm">Annual Profit</span>
</div>
</div>
<p class="text-md mt-3">Validator Status</p>
<VList class="card-list">
<VListItem prepend-icon="mdi-shield-account-outline">
<span>Status: </span
><span>
{{ String(v.status).replace('BOND_STATUS_', '') }}
</span>
</VListItem>
<VListItem prepend-icon="mdi-shield-alert-outline">
<span>Jailed: </span><span> {{ v.jailed || '-' }} </span>
</VListItem>
</VList>
</VCardText>
</VCol>
<VCol cols="12" md="6">
<div class="d-flex flex-column py-3 justify-space-between">
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-coin"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>
{{
format.formatToken2({
amount: v.tokens,
denom: staking.params.bond_denom,
})
}}
</h4>
<span class="text-sm">Total Bonded Tokens</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-percent"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>
{{ format.formatToken(selfBonded.balance) }} ({{
selfRate
}})
</h4>
<span class="text-sm">Self Bonded</span>
</div>
</div>
<div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-stairs-up"></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ v.unbonding_height }}</h4>
<span class="text-sm">Unbonding Height</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-account-tie"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>
{{ v.min_self_delegation }} {{ staking.params.bond_denom }}
</h4>
<span class="text-sm">Min Self Delegation:</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-finance"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ apr }}</h4>
<span class="text-sm">Annual Profit</span>
</div>
</div>
<div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-clock"></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ format.toDay(v.unbonding_time, 'from') }}</h4>
<span class="text-sm">Unbonding Time</span>
</div>
</div>
</div>
</VCol>
</VRow>
<VDivider />
<VCardText>{{ v.description?.details }}</VCardText>
</VCardItem>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-stairs-up"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ v.unbonding_height }}</h4>
<span class="text-sm">Unbonding Height</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-clock"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ format.toDay(v.unbonding_time, 'from') }}</h4>
<span class="text-sm">Unbonding Time</span>
</div>
</div>
</div>
</VCol>
</VRow>
<VDivider />
<VCardText>{{ v.description?.details }}</VCardText>
</VCardItem>
</VCard>
<VRow class="mt-3">
<VCol md="4" sm="12" class="h-100">
<ValidatorCommissionRate :commission="v.commission"></ValidatorCommissionRate>
</VCol>
<VCol md="4" sm="12">
<VCard class="h-100">
<VCardTitle>Commissions & Rewards</VCardTitle>
<VCardItem class="pt-0 pb-0">
<div class="overflow-auto" style="max-height: 280px;">
<VCardSubtitle>Commissions <VBtn size="small" class="float-right" variant="text">Withdraw</VBtn></VCardSubtitle>
<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">
{{ format.formatToken2(i) }}
</VChip>
<VCardSubtitle class="mt-2">Outstanding Rewards</VCardSubtitle>
<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">
{{ format.formatToken2(i) }}
</VChip>
</div>
</VCardItem>
</VCard>
</VCol>
<VCol md="4" sm="12">
<VCard title="Addresses" class="h-100">
<VList class="pt-0">
<VListItem>
<VListItemTitle>Account</VListItemTitle>
<VListItemSubtitle class="text-caption text-primary"><RouterLink :to="`/${chain}/account/${addresses.account}`">{{ addresses.account }}</RouterLink></VListItemSubtitle>
</VListItem>
<VListItem>
<VListItemTitle>Operator Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{ v.operator_address }}</VListItemSubtitle>
</VListItem>
<VListItem>
<VListItemTitle>Hex Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{ addresses.hex }}</VListItemSubtitle>
</VListItem>
<VListItem>
<VListItemTitle>Signer Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{ addresses.valCons }}</VListItemSubtitle>
</VListItem>
</VList>
</VCard>
</VCol>
<VCol md="4" sm="12" class="h-100">
<ValidatorCommissionRate
:commission="v.commission"
></ValidatorCommissionRate>
</VCol>
<VCol md="4" sm="12">
<VCard class="h-100">
<VCardTitle>Commissions & Rewards</VCardTitle>
<VCardItem class="pt-0 pb-0">
<div class="overflow-auto" style="max-height: 280px">
<VCardSubtitle
>Commissions
<VBtn size="small" class="float-right" variant="text"
>Withdraw</VBtn
></VCardSubtitle
>
<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"
>
{{ format.formatToken2(i) }}
</VChip>
<VCardSubtitle class="mt-2">Outstanding Rewards</VCardSubtitle>
<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"
>
{{ format.formatToken2(i) }}
</VChip>
</div>
</VCardItem>
</VCard>
</VCol>
<VCol md="4" sm="12">
<VCard title="Addresses" class="h-100">
<VList class="pt-0">
<VListItem>
<VListItemTitle>Account</VListItemTitle>
<VListItemSubtitle class="text-caption text-primary"
><RouterLink :to="`/${chain}/account/${addresses.account}`">{{
addresses.account
}}</RouterLink></VListItemSubtitle
>
</VListItem>
<VListItem>
<VListItemTitle>Operator Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{
v.operator_address
}}</VListItemSubtitle>
</VListItem>
<VListItem>
<VListItemTitle>Hex Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{
addresses.hex
}}</VListItemSubtitle>
</VListItem>
<VListItem>
<VListItemTitle>Signer Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{
addresses.valCons
}}</VListItemSubtitle>
</VListItem>
</VList>
</VCard>
</VCol>
</VRow>
<VCard title="Transactions" class="mt-5">
<VCardItem class="pt-0">
<VTable>
<thead>
<th class="text-left pl-4">Height</th>
<th class="text-left pl-4">Hash</th>
<th class="text-left pl-4" width="40%">Messages</th>
<th class="text-left pl-4">Time</th>
</thead>
<tbody>
<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-truncate" style="max-width: 200px;">{{ item.txhash }}</td>
<td>
{{ format.messages(item.tx.body.messages) }}
<VIcon 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>
</tr>
</tbody>
</VTable>
</VCardItem>
<VCardItem class="pt-0">
<VTable>
<thead>
<th class="text-left pl-4">Height</th>
<th class="text-left pl-4">Hash</th>
<th class="text-left pl-4" width="40%">Messages</th>
<th class="text-left pl-4">Time</th>
</thead>
<tbody>
<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-truncate" style="max-width: 200px">
{{ item.txhash }}
</td>
<td>
{{ format.messages(item.tx.body.messages) }}
<VIcon
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>
</tr>
</tbody>
</VTable>
</VCardItem>
</VCard>
</div>
</div>
</template>
<style>
.card-list {
--v-card-list-gap: 10px;
--v-card-list-gap: 10px;
}
</style>

View File

@ -5,76 +5,97 @@ import { fromBase64, toHex } from '@cosmjs/encoding';
import { onMounted, ref } from 'vue';
import { computed } from 'vue';
const props = defineProps(['hash', 'chain'])
const blockchain = useBlockchain()
const base = useBaseStore()
const nodeInfo = ref({} as NodeInfo)
const props = defineProps(['hash', 'chain']);
const blockchain = useBlockchain();
const base = useBaseStore();
const nodeInfo = ref({} as NodeInfo);
const state = computed(()=> {
const rpcs = blockchain.current?.endpoints?.rpc?.map(x => x.address).join(',')
return `[statesync]
const state = computed(() => {
const rpcs = blockchain.current?.endpoints?.rpc
?.map((x) => x.address)
.join(',');
return `[statesync]
enable = true
rpc_servers = "${rpcs}"
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"
`
})
`;
});
const appName = computed(()=> {
return nodeInfo.value.application_version?.app_name || "gaiad"
})
const appName = computed(() => {
return nodeInfo.value.application_version?.app_name || 'gaiad';
});
onMounted(() => {
blockchain.rpc.getBaseNodeInfo().then(x => {
console.log('node info', x)
nodeInfo.value = x
})
})
blockchain.rpc.getBaseNodeInfo().then((x) => {
console.log('node info', x);
nodeInfo.value = x;
});
});
</script>
<template>
<div>
<VCard>
<VCardTitle>What's State Sync?</VCardTitle>
<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.
</VCardText>
</VCard>
<div>
<VCard>
<VCardTitle>What's State Sync?</VCardTitle>
<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.
</VCardText>
</VCard>
<VCard class="my-5">
<VCardTitle>Starting New Node From State Sync</VCardTitle>
<VCardItem>
1. Install Binary ({{ appName }} Version: {{ nodeInfo.application_version?.version || "" }})
<br>
We need to install the binary first and make sure that the version is the one currently in use on mainnet.
<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>
3. Start the daemon: <code>{{ appName }} start</code>
<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.
</VCardItem>
</VCard>
<VCard class="my-5">
<VCardTitle>Starting New Node From State Sync</VCardTitle>
<VCardItem>
1. Install Binary ({{ appName }} Version:
{{ nodeInfo.application_version?.version || '' }})
<br />
We need to install the binary first and make sure that the version is
the one currently in use on mainnet.
<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>
3. Start the daemon: <code>{{ appName }} start</code>
<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.
</VCardItem>
</VCard>
<VCard>
<VCardTitle>Enable Snapshot For State Sync</VCardTitle>
<VCardItem>
To make state sync works, we can enable snapshot in $DAEMON_HOME/config/app.toml
<VTextarea auto-grow model-value="[state-sync]
<VCard>
<VCardTitle>Enable Snapshot For State Sync</VCardTitle>
<VCardItem>
To make state sync works, we can enable snapshot in
$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
# taken (0 to disable). Must be a multiple of pruning-keep-every.
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 = 2">
</VTextarea>
</VCardItem>
</VCard>
</div>
snapshot-keep-recent = 2"
>
</VTextarea>
</VCardItem>
</VCard>
</div>
</template>
<route>
@ -83,4 +104,4 @@ snapshot-keep-recent = 2">
i18n: 'state-sync'
}
}
</route>
</route>

View File

@ -6,58 +6,99 @@ import type { Tx, TxResponse } from '@/types';
import VueJsonPretty from 'vue-json-pretty';
import 'vue-json-pretty/lib/styles.css';
const props = defineProps(['hash', 'chain'])
const props = defineProps(['hash', 'chain']);
const blockchain = useBlockchain()
const format = useFormatter()
const tx = ref({} as {
const blockchain = useBlockchain();
const format = useFormatter();
const tx = ref(
{} as {
tx: Tx;
tx_response: TxResponse
})
if(props.hash) {
blockchain.rpc.getTx(props.hash).then(x => tx.value = x)
tx_response: TxResponse;
}
);
if (props.hash) {
blockchain.rpc.getTx(props.hash).then((x) => (tx.value = x));
}
const messages = computed(() => {
return tx.value.tx?.body?.messages||[]
})
return tx.value.tx?.body?.messages || [];
});
</script>
<template>
<div>
<VCard v-if="tx.tx_response" title="Summary">
<VCardItem class="pt-0">
<VTable>
<tbody>
<tr><td>Tx Hash</td><td>{{ tx.tx_response.txhash }}</td></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>
</td></tr>
<tr><td>Time</td><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>
</VTable>
</VCardItem>
</VCard>
<div>
<VCard v-if="tx.tx_response" title="Summary">
<VCardItem class="pt-0">
<VTable>
<tbody>
<tr>
<td>Tx Hash</td>
<td>{{ tx.tx_response.txhash }}</td>
</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>
</td>
</tr>
<tr>
<td>Time</td>
<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>
</VTable>
</VCardItem>
</VCard>
<VCard :title="`Messages: (${messages.length})`" class="my-5">
<VCardItem class="pt-0" style="border-top: 2px dotted gray;">
<div v-for="(msg, i) in messages">
<div><DynamicComponent :value="msg" /></div>
</div>
<div v-if="messages.length === 0">
No messages
</div>
</VCardItem>
</VCard>
<VCard :title="`Messages: (${messages.length})`" class="my-5">
<VCardItem class="pt-0" style="border-top: 2px dotted gray">
<div v-for="(msg, i) in messages">
<div><DynamicComponent :value="msg" /></div>
</div>
<div v-if="messages.length === 0">No messages</div>
</VCardItem>
</VCard>
<VCard title="JSON">
<VCardItem class="pt-0">
<vue-json-pretty :data="tx" :deep="3"/>
</VCardItem>
</VCard>
</div>
</template>
<VCard title="JSON">
<VCardItem class="pt-0">
<vue-json-pretty :data="tx" :deep="3" />
</VCardItem>
</VCard>
</div>
</template>

View File

@ -1,107 +1,162 @@
<script lang="ts" setup>
import { ref, onMounted, computed, watchEffect } from 'vue';
import { fromHex, toBase64 } from '@cosmjs/encoding'
import { useFormatter, useStakingStore, useBaseStore, useBlockchain } from '@/stores';
import { fromHex, toBase64 } from '@cosmjs/encoding';
import {
useFormatter,
useStakingStore,
useBaseStore,
useBlockchain,
} from '@/stores';
import UptimeBar from '@/components/UptimeBar.vue';
import type { Block, Commit } from '@/types'
import { consensusPubkeyToHexAddress, valconsToBase64 } from "@/libs";
import type { Block, Commit } from '@/types';
import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
import type { SigningInfo } from '@/types/slashing';
const props = defineProps(['chain'])
const props = defineProps(['chain']);
const stakingStore = useStakingStore();
const baseStore = useBaseStore();
const chainStore = useBlockchain()
const latest = ref({} as Block)
const chainStore = useBlockchain();
const latest = ref({} as Block);
const commits = ref([] as Commit[]);
const keyword = ref("")
const keyword = ref('');
const live = ref(true);
// storage local favorite validator ids
const local = ref((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 local = ref(
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
const validators = computed(()=> {
if(keyword) return stakingStore.validators.filter(x => x.description.moniker.indexOf(keyword.value) > -1)
return stakingStore.validators
})
const validators = computed(() => {
if (keyword)
return stakingStore.validators.filter(
(x) => x.description.moniker.indexOf(keyword.value) > -1
);
return stakingStore.validators;
});
onMounted(() => {
live.value = true
baseStore.fetchLatest().then(block => {
latest.value = block
commits.value.unshift(block.block.last_commit)
const height = Number(block.block.header?.height|| 0)
if(height > 0) {
live.value = true;
baseStore.fetchLatest().then((block) => {
latest.value = block;
commits.value.unshift(block.block.last_commit);
const height = Number(block.block.header?.height || 0);
if (height > 0) {
// constructs sequence for loading blocks
let promise = Promise.resolve()
let promise = Promise.resolve();
for (let i = height - 1; i > height - 50; i -= 1) {
if (i > height - 48) {
promise = promise.then(() => new Promise((resolve, reject) => {
if(live.value) { // continue only if the page is living
baseStore.fetchBlock(i).then((x) => {
commits.value.unshift(x.block.last_commit)
resolve()
promise = promise.then(
() =>
new Promise((resolve, reject) => {
if (live.value) {
// continue only if the page is living
baseStore.fetchBlock(i).then((x) => {
commits.value.unshift(x.block.last_commit);
resolve();
});
}
})
}
}))
);
}
}
}
});
chainStore.rpc.getSlashingSigningInfos().then((x)=> {
x.info?.forEach(i => {
signingInfo.value[valconsToBase64(i.address)] = i
})
})
})
chainStore.rpc.getSlashingSigningInfos().then((x) => {
x.info?.forEach((i) => {
signingInfo.value[valconsToBase64(i.address)] = i;
});
});
});
onUnmounted(() => {
live.value = false
})
live.value = false;
});
watchEffect(() => {
local.value[chainStore.chainName] = selected.value
localStorage.setItem("uptime-validators", JSON.stringify(local.value))
})
local.value[chainStore.chainName] = selected.value;
localStorage.setItem('uptime-validators', JSON.stringify(local.value));
});
</script>
<template>
<div>
<VRow>
<VCol cols="12" md="4" >
<VCol cols="12" md="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>
</VCol>
<VCol cols="12" md="8" class="">
<VTextField v-model="keyword" label="Keywords to filter validators" variant="outlined">
<VCol cols="12" md="8" class="">
<VTextField
v-model="keyword"
label="Keywords to filter validators"
variant="outlined"
>
<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>
</VTextField>
</VTextField>
</VCol>
</VRow>
<VRow>
<VCol v-for="(v, i) in validators" cols="12" md="3" xl="2" class="py-0">
<div class="d-flex justify-between">
<VCheckbox v-model="selected" color="warning" :value="v.operator_address">
<template v-slot:label>
<span class="text-truncate">{{i + 1}}. {{v.description.moniker}}</span>
</template>
</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 v-else size="small" class="mt-1" label color="success">{{ signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]?.missed_blocks_counter }}</VChip>
<VCheckbox
v-model="selected"
color="warning"
:value="v.operator_address"
>
<template v-slot:label>
<span class="text-truncate"
>{{ i + 1 }}. {{ v.description.moniker }}</span
>
</template>
</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 v-else size="small" class="mt-1" label color="success">{{
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
?.missed_blocks_counter
}}</VChip>
</div>
<UptimeBar :blocks="commits" :validator="toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey)))" />
<UptimeBar
:blocks="commits"
:validator="
toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey)))
"
/>
</VCol>
</VRow>
</VRow>
</div>
</template>
<route>
@ -113,7 +168,7 @@ watchEffect(() => {
</route>
<style lang="scss">
.v-field--variant-outlined .v-field__outline__notch{
.v-field--variant-outlined .v-field__outline__notch {
border-width: 0 !important;
}
</style>
</style>

View File

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

View File

@ -1,35 +1,50 @@
<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 miscMaskDark from '@images/pages/misc-mask-dark.png'
import miscMaskLight from '@images/pages/misc-mask-light.png'
import miscObj from '@images/pages/misc-404-object.png';
import miscMaskDark from '@images/pages/misc-mask-dark.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>
<template>
<div class="misc-wrapper">
<ErrorHeader error-code="404" error-title="Page Not Found "
error-description="We couldn't find the page you are looking for." />
<ErrorHeader
error-code="404"
error-title="Page Not Found ⚠️"
error-description="We couldn't find the page you are looking for."
/>
<!-- 👉 Image -->
<div class="misc-avatar w-100 text-center">
<VImg :src="misc404" alt="Coming Soon" :height="$vuetify.display.xs ? 400 : 500" 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="misc404"
alt="Coming Soon"
:height="$vuetify.display.xs ? 400 : 500"
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>
</template>
<style lang="scss">
@use "@core/scss/template/pages/misc.scss";
@use '@core/scss/template/pages/misc.scss';
</style>
<route lang="yaml">

View File

@ -4,15 +4,14 @@
<VCardText>This is your second page.</VCardText>
<VCardText>
Chocolate sesame snaps pie carrot cake pastry pie lollipop muffin.
Carrot cake dragée chupa chups jujubes. Macaroon liquorice cookie
wafer tart marzipan bonbon. Gingerbread jelly-o dragée
chocolate.
Carrot cake dragée chupa chups jujubes. Macaroon liquorice cookie wafer
tart marzipan bonbon. Gingerbread jelly-o dragée chocolate.
</VCardText>
</VCard>
</div>
</template>
<route lang="yaml">
meta:
layout: blank
bgColor: yellow
</route>
meta:
layout: blank
bgColor: yellow
</route>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,195 +1,246 @@
import { defineStore } from "pinia";
import { useBlockchain } from "./useBlockchain";
import { percent,formatNumber,formatTokenAmount } from '@/libs/utils'
import { defineStore } from 'pinia';
import { useBlockchain } from './useBlockchain';
import { percent, formatNumber, formatTokenAmount } from '@/libs/utils';
export interface stakingItem {
unbonding_time: string
max_validators: number
max_entries:number
historical_entries:number
bond_denom: string
min_commission_rate: string
min_self_delegation:string
unbonding_time: string;
max_validators: number;
max_entries: number;
historical_entries: number;
bond_denom: string;
min_commission_rate: string;
min_self_delegation: string;
}
export const useParamStore = defineStore("paramstore", {
state: () => ({
latestTime: '',
chain: {
title: '',
class: 'border-primary',
items: [
{ subtitle: 'height', icon: 'BoxIcon', 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: '-' },
],
export const useParamStore = defineStore('paramstore', {
state: () => ({
latestTime: '',
chain: {
title: '',
class: 'border-primary',
items: [
{
subtitle: 'height',
icon: 'BoxIcon',
color: 'light-success',
value: '-',
},
mint: {
title: 'Mint Parameters',
items: [] as Array<any>,
{
subtitle: 'bonded_and_supply',
icon: 'DollarSignIcon',
color: 'light-danger',
value: '-',
},
staking: {
title: 'Staking Parameters',
items: [] as Array<any>,
{
subtitle: 'bonded_ratio',
icon: 'PercentIcon',
color: 'light-warning',
value: '-',
},
distribution: {
title: 'Distribution Parameters',
items: [] as Array<any>,
},
slashing: {
title: 'Slashing Parameters',
items: [] as Array<any>,
},
gov: {
title: 'Governance Parameters',
items: [] as Array<any>,
},
appVersion: {
title: 'Application Version',
items: {},
},
nodeVersion: {
title: 'Node Information',
items: {},
},
}),
getters: {
blockchain() {
return useBlockchain()
{
subtitle: 'inflation',
icon: 'TrendingUpIcon',
color: 'light-primary',
value: '-',
},
],
},
actions: {
initial() {
this.handleBaseBlockLatest()
// this.handleMintParam()
this.handleStakingParams()
this.handleSlashingParams()
this.handleDistributionParams()
this.handleGovernanceParams()
this.handleAbciInfo()
},
async handleBaseBlockLatest() {
try {
const res = await this.getBaseTendermintBlockLatest()
const height = this.chain.items.findIndex(x => x.subtitle === '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')) {
// this.syncing = true
// } else {
// this.syncing = false
// }
// this.latestTime = toDay(res.block.header.time, 'long')
this.latestTime = res.block.header.time
} catch (error) {
console.warn(error)
}
},
async handleStakingParams() {
const res = await this.getStakingParams()
const bond_denom = res?.params.bond_denom
this.staking.items = Object.entries(res.params).map(([key, value]) => ({ subtitle:key,
value: value })).filter((item: any) => {
if (!['min_commission_rate','min_self_delegation'].includes(item.subtitle)) return item
})
Promise.all([this.getStakingPool(), this.getBankTotal(bond_denom)])
.then(resArr => {
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() {
const excludes = this.blockchain.current?.excludes
if(excludes && excludes.indexOf('mint') > -1){
return
}
// this.getMintingInflation().then(res => {
// const chainIndex = this.chain.items.findIndex(x => x.subtitle === 'inflation')
// this.chain.items[chainIndex].value = `${percent(res)}%`
// })
const res = await this.getMintParam()
console.log(res, 'mint')
},
async handleSlashingParams(){
const res = await this.getSlashingParams()
this.slashing.items = Object.entries(res.params).map(([key, value]) => ({ subtitle:key,
value: value }))
},
async handleDistributionParams(){
const res = await this.getDistributionParams()
this.distribution.items = Object.entries(res.params).map(([key, value]) => ({ subtitle: key,
value: value }))
},
async handleGovernanceParams() {
const excludes = this.blockchain.current?.excludes
if(excludes && excludes.indexOf('governance') > -1){
return
}
Promise.all([this.getGovParamsVoting(),this.getGovParamsDeposit(),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(){
const res = await this.fetchAbciInfo()
this.appVersion.items = Object.entries(res.application_version).map(([key, value]) => ({ subtitle:key,
value: value }))
this.nodeVersion.items = Object.entries(res.default_node_info).map(([key, value]) => ({ subtitle:key,
value: value }))
console.log('handleAbciInfo', this.nodeVersion.items)
},
async getBaseTendermintBlockLatest() {
return await this.blockchain.rpc.getBaseBlockLatest()
},
async getMintParam() {
return await this.blockchain.rpc.getMintParam()
},
async getStakingParams() {
return await this.blockchain.rpc.getStakingParams()
},
async getStakingPool(){
return await this.blockchain.rpc.getStakingPool()
},
async getBankTotal(denom: string){
return await this.blockchain.rpc.getBankSupplyByDenom(denom)
// 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)
// }
// if (compareVersions(this.config.sdk_version, '0.40') < 0) {
// return this.get(`/supply/total/${denom}`).then(data => ({ amount: commonProcess(data), denom }))
// }
// return this.get(`/cosmos/bank/v1beta1/supply/${denom}`).then(data => commonProcess(data).amount)
},
async getSlashingParams() {
return await this.blockchain.rpc.getSlashingParams()
},
async getDistributionParams() {
return await this.blockchain.rpc.getDistributionParams()
},
async getGovParamsVoting() {
return await this.blockchain.rpc.getGovParamsVoting()
},
async getGovParamsDeposit() {
return await this.blockchain.rpc.getGovParamsDeposit()
},
async getGovParamsTally() {
return await this.blockchain.rpc.getGovParamsTally()
},
async fetchAbciInfo() {
return this.blockchain.rpc.getBaseNodeInfo()
mint: {
title: 'Mint Parameters',
items: [] as Array<any>,
},
staking: {
title: 'Staking Parameters',
items: [] as Array<any>,
},
distribution: {
title: 'Distribution Parameters',
items: [] as Array<any>,
},
slashing: {
title: 'Slashing Parameters',
items: [] as Array<any>,
},
gov: {
title: 'Governance Parameters',
items: [] as Array<any>,
},
appVersion: {
title: 'Application Version',
items: {},
},
nodeVersion: {
title: 'Node Information',
items: {},
},
}),
getters: {
blockchain() {
return useBlockchain();
},
},
actions: {
initial() {
this.handleBaseBlockLatest();
// this.handleMintParam()
this.handleStakingParams();
this.handleSlashingParams();
this.handleDistributionParams();
this.handleGovernanceParams();
this.handleAbciInfo();
},
async handleBaseBlockLatest() {
try {
const res = await this.getBaseTendermintBlockLatest();
const height = this.chain.items.findIndex(
(x) => x.subtitle === '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')) {
// this.syncing = true
// } else {
// this.syncing = false
// }
// this.latestTime = toDay(res.block.header.time, 'long')
this.latestTime = res.block.header.time;
} catch (error) {
console.warn(error);
}
},
async handleStakingParams() {
const res = await this.getStakingParams();
const bond_denom = res?.params.bond_denom;
this.staking.items = Object.entries(res.params)
.map(([key, value]) => ({ subtitle: key, value: value }))
.filter((item: any) => {
if (
!['min_commission_rate', 'min_self_delegation'].includes(
item.subtitle
)
)
return item;
});
Promise.all([this.getStakingPool(), this.getBankTotal(bond_denom)]).then(
(resArr) => {
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() {
const excludes = this.blockchain.current?.excludes;
if (excludes && excludes.indexOf('mint') > -1) {
return;
}
// this.getMintingInflation().then(res => {
// const chainIndex = this.chain.items.findIndex(x => x.subtitle === 'inflation')
// this.chain.items[chainIndex].value = `${percent(res)}%`
// })
const res = await this.getMintParam();
console.log(res, 'mint');
},
async handleSlashingParams() {
const res = await this.getSlashingParams();
this.slashing.items = Object.entries(res.params).map(([key, value]) => ({
subtitle: key,
value: value,
}));
},
async handleDistributionParams() {
const res = await this.getDistributionParams();
this.distribution.items = Object.entries(res.params).map(
([key, value]) => ({ subtitle: key, value: value })
);
},
async handleGovernanceParams() {
const excludes = this.blockchain.current?.excludes;
if (excludes && excludes.indexOf('governance') > -1) {
return;
}
Promise.all([
this.getGovParamsVoting(),
this.getGovParamsDeposit(),
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() {
const res = await this.fetchAbciInfo();
this.appVersion.items = Object.entries(res.application_version).map(
([key, value]) => ({ subtitle: key, value: value })
);
this.nodeVersion.items = Object.entries(res.default_node_info).map(
([key, value]) => ({ subtitle: key, value: value })
);
console.log('handleAbciInfo', this.nodeVersion.items);
},
async getBaseTendermintBlockLatest() {
return await this.blockchain.rpc.getBaseBlockLatest();
},
async getMintParam() {
return await this.blockchain.rpc.getMintParam();
},
async getStakingParams() {
return await this.blockchain.rpc.getStakingParams();
},
async getStakingPool() {
return await this.blockchain.rpc.getStakingPool();
},
async getBankTotal(denom: string) {
return await this.blockchain.rpc.getBankSupplyByDenom(denom);
// 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)
// }
// if (compareVersions(this.config.sdk_version, '0.40') < 0) {
// return this.get(`/supply/total/${denom}`).then(data => ({ amount: commonProcess(data), denom }))
// }
// return this.get(`/cosmos/bank/v1beta1/supply/${denom}`).then(data => commonProcess(data).amount)
},
async getSlashingParams() {
return await this.blockchain.rpc.getSlashingParams();
},
async getDistributionParams() {
return await this.blockchain.rpc.getDistributionParams();
},
async getGovParamsVoting() {
return await this.blockchain.rpc.getGovParamsVoting();
},
async getGovParamsDeposit() {
return await this.blockchain.rpc.getGovParamsDeposit();
},
async getGovParamsTally() {
return await this.blockchain.rpc.getGovParamsTally();
},
async fetchAbciInfo() {
return this.blockchain.rpc.getBaseNodeInfo();
},
},
});

View File

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

View File

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

View File

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