commit
add8a3466b
@ -8,6 +8,7 @@ import { ref } from 'vue';
|
|||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
proposals: { type: Object as PropType<PaginatedProposals> },
|
proposals: { type: Object as PropType<PaginatedProposals> },
|
||||||
|
votable: { type: Boolean, default: false }
|
||||||
});
|
});
|
||||||
|
|
||||||
const format = useFormatter();
|
const format = useFormatter();
|
||||||
@ -30,12 +31,12 @@ const proposalInfo = ref();
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-white dark:bg-[#28334e] rounded text-sm">
|
<div class="bg-white dark:bg-[#28334e] rounded text-sm">
|
||||||
<table class="table-compact w-full table-fixed hidden lg:table">
|
<table class="table-compact w-full table-fixed lg:table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(item, index) in proposals?.proposals" :key="index">
|
<tr v-for="(item, index) in proposals?.proposals" :key="index">
|
||||||
<td class="px-4 w-20">
|
<td class="px-4 w-20">
|
||||||
<label
|
<label
|
||||||
for="proposal-detail-modal"
|
for=""
|
||||||
class="text-main text-base hover:text-indigo-400 cursor-pointer"
|
class="text-main text-base hover:text-indigo-400 cursor-pointer"
|
||||||
@click="proposalInfo = item"
|
@click="proposalInfo = item"
|
||||||
>
|
>
|
||||||
@ -97,7 +98,7 @@ const proposalInfo = ref();
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td v-if="votable">
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-xs btn-primary rounded-sm">Vote</button>
|
<button class="btn btn-xs btn-primary rounded-sm">Vote</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@ const baseStore = useBaseStore();
|
|||||||
chainStore.initial();
|
chainStore.initial();
|
||||||
chainStore.$subscribe((m, s) => {
|
chainStore.$subscribe((m, s) => {
|
||||||
if (!Array.isArray(m.events) && m.events.key === 'endpoint') {
|
if (!Array.isArray(m.events) && m.events.key === 'endpoint') {
|
||||||
chainStore.initial();
|
// chainStore.initial();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -98,7 +98,7 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
|||||||
async getGovParamsTally() {
|
async getGovParamsTally() {
|
||||||
return this.request(this.registry.gov_params_tally, {});
|
return this.request(this.registry.gov_params_tally, {});
|
||||||
}
|
}
|
||||||
async getGovProposals(status: string, limit = 50) {
|
async getGovProposals(status: string, limit = 20) {
|
||||||
const query =
|
const query =
|
||||||
'?proposal_status={status}&pagination.limit={limit}&pagination.reverse=true&pagination.key=';
|
'?proposal_status={status}&pagination.limit={limit}&pagination.reverse=true&pagination.key=';
|
||||||
return this.request(this.registry.gov_proposals, { status, limit }, query);
|
return this.request(this.registry.gov_proposals, { status, limit }, query);
|
||||||
|
@ -41,7 +41,7 @@ const changeTab = (val: '2' | '3' | '4') => {
|
|||||||
>Rejected</a
|
>Rejected</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<ProposalListItem :proposals="store?.proposals[tab]" />
|
<ProposalListItem :proposals="store?.proposals[tab]" :votable="tab === '2'" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<route>
|
<route>
|
||||||
|
@ -16,23 +16,14 @@ import type { SigningInfo } from '@/types/slashing';
|
|||||||
const props = defineProps(['chain']);
|
const props = defineProps(['chain']);
|
||||||
|
|
||||||
const stakingStore = useStakingStore();
|
const stakingStore = useStakingStore();
|
||||||
|
const format = useFormatter();
|
||||||
const baseStore = useBaseStore();
|
const baseStore = useBaseStore();
|
||||||
const chainStore = useBlockchain();
|
const chainStore = useBlockchain();
|
||||||
const latest = ref({} as Block);
|
const latest = ref(0);
|
||||||
const commits = ref([] as Commit[]);
|
const commits = ref([] as Commit[]);
|
||||||
const keyword = ref('');
|
const keyword = ref('');
|
||||||
const live = ref(true);
|
const live = ref(true);
|
||||||
|
|
||||||
// storage local favorite validator ids
|
|
||||||
const local = ref(
|
|
||||||
JSON.parse(localStorage.getItem('uptime-validators') || '{}') as Record<
|
|
||||||
string,
|
|
||||||
string[]
|
|
||||||
>
|
|
||||||
);
|
|
||||||
const currentPined = local.value[chainStore.chainName]
|
|
||||||
const selected = ref(currentPined || []); // favorite validators on selected blockchain
|
|
||||||
|
|
||||||
const signingInfo = ref({} as Record<string, SigningInfo>);
|
const signingInfo = ref({} as Record<string, SigningInfo>);
|
||||||
|
|
||||||
// filter validators by keywords
|
// filter validators by keywords
|
||||||
@ -44,10 +35,18 @@ const validators = computed(() => {
|
|||||||
return stakingStore.validators;
|
return stakingStore.validators;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const list = computed(() => {
|
||||||
|
return validators.value.map(v => ({
|
||||||
|
v,
|
||||||
|
signing: signingInfo.value[consensusPubkeyToHexAddress(v.consensus_pubkey)],
|
||||||
|
hex: toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey)))
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
live.value = true;
|
live.value = true;
|
||||||
baseStore.fetchLatest().then(b => {
|
baseStore.fetchLatest().then(b => {
|
||||||
latest.value = b;
|
latest.value = Number(b.block.header.height);
|
||||||
commits.value.unshift(b.block.last_commit);
|
commits.value.unshift(b.block.last_commit);
|
||||||
const height = Number(b.block.header?.height || 0);
|
const height = Number(b.block.header?.height || 0);
|
||||||
if (height === 0) {
|
if (height === 0) {
|
||||||
@ -82,78 +81,103 @@ onMounted(() => {
|
|||||||
const commits2 = computed(() => {
|
const commits2 = computed(() => {
|
||||||
const la = baseStore.recents.map(b => b.block.last_commit)
|
const la = baseStore.recents.map(b => b.block.last_commit)
|
||||||
const all = [...commits.value, ...la]
|
const all = [...commits.value, ...la]
|
||||||
return all.length > 50 ? all.slice(all.length - 50): all
|
return all.length > 50 ? all.slice(all.length - 50) : all
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
live.value = false;
|
live.value = false;
|
||||||
});
|
});
|
||||||
|
const tab = ref("3")
|
||||||
|
function changeTab(v: string) {
|
||||||
|
tab.value = v
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
local.value[chainStore.chainName] = selected.value;
|
|
||||||
localStorage.setItem('uptime-validators', JSON.stringify(local.value));
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||||
|
<a
|
||||||
|
class="tab text-gray-400 capitalize"
|
||||||
|
:class="{ 'tab-active': tab === '3' }"
|
||||||
|
@click="changeTab('3')"
|
||||||
|
>Overall</a
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="tab text-gray-400 capitalize"
|
||||||
|
:class="{ 'tab-active': tab === '2' }"
|
||||||
|
@click="changeTab('2')"
|
||||||
|
>Blocks</a
|
||||||
|
>
|
||||||
|
<RouterLink :to="`/${chain}/uptime/overview`">
|
||||||
|
<a
|
||||||
|
class="tab text-gray-400 capitalize"
|
||||||
|
>Customize</a
|
||||||
|
></RouterLink>
|
||||||
|
</div>
|
||||||
<div class="bg-base-100 px-5 pt-5">
|
<div class="bg-base-100 px-5 pt-5">
|
||||||
<div class="flex items-center gap-x-4">
|
<div class="flex items-center gap-x-4">
|
||||||
<input
|
<input type="text" v-model="keyword" placeholder="Keywords to filter validators"
|
||||||
type="text"
|
class="input input-sm w-full flex-1" />
|
||||||
v-model="keyword"
|
<RouterLink class="btn btn-primary btn-sm" :to="`/${chain}/uptime/overview`">
|
||||||
placeholder="Keywords to filter validators"
|
|
||||||
class="input input-sm w-full flex-1"
|
|
||||||
/>
|
|
||||||
<button class="btn btn-primary btn-sm">
|
|
||||||
<Icon icon="mdi-star" class="mr-2 text-lg" />
|
<Icon icon="mdi-star" class="mr-2 text-lg" />
|
||||||
<span class="">Favorite</span>
|
<span class="">Favorite</span>
|
||||||
</button>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-x-4 mt-4" :class="tab === '2'?'':'hidden'">
|
||||||
class="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-x-4 mt-4"
|
<div v-for="({v, signing, hex}, i) in list" :key="i">
|
||||||
>
|
|
||||||
<div v-for="(v, i) in validators" :key="i" >
|
|
||||||
<div class="flex items-center justify-between py-0">
|
<div class="flex items-center justify-between py-0">
|
||||||
<label class="text-truncate text-sm">
|
<label class="text-truncate text-sm">
|
||||||
<input type="checkbox"
|
|
||||||
v-model="selected"
|
|
||||||
:value="v.operator_address"
|
|
||||||
/>
|
|
||||||
<span class="ml-1 text-black dark:text-white">{{ i + 1 }}.{{ v.description.moniker }}</span>
|
<span class="ml-1 text-black dark:text-white">{{ i + 1 }}.{{ v.description.moniker }}</span>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div v-if="Number(signing?.missed_blocks_counter || 0) > 10" class="badge badge-error badge-sm text-white">
|
||||||
v-if="
|
{{ signing?.missed_blocks_counter }}
|
||||||
Number(
|
|
||||||
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
|
|
||||||
?.missed_blocks_counter || 0
|
|
||||||
) > 0
|
|
||||||
"
|
|
||||||
class="badge badge-error badge-sm text-white"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
|
|
||||||
?.missed_blocks_counter
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="mt-1 badge badge-sm text-white bg-yes border-0">
|
<div v-else class="mt-1 badge badge-sm text-white bg-yes border-0">
|
||||||
{{
|
{{ signing?.missed_blocks_counter }}
|
||||||
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
|
|
||||||
?.missed_blocks_counter
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<UptimeBar
|
<UptimeBar :blocks="commits2" :validator="hex" />
|
||||||
:blocks="commits2"
|
|
||||||
:validator="
|
|
||||||
toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey)))
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div :class="tab === '3'?'':'hidden'">
|
||||||
|
<table class="table table-compact w-full mt-5">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="2">Validator</td>
|
||||||
|
<td rowspan="2">Start Height</td>
|
||||||
|
<td rowspan="2">Signed Blocks</td>
|
||||||
|
<td colspan="2">Missing blocks</td>
|
||||||
|
<td rowspan="2">Last Jailed Time</td>
|
||||||
|
<td rowspan="2">Tombstoned</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>In Window</td>
|
||||||
|
<td>Over All</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr v-for="({v, signing}, i) in list">
|
||||||
|
<td>{{ i+1 }}. {{ v.description.moniker }}</td>
|
||||||
|
<td>{{ signing?.start_height }}</td>
|
||||||
|
<td>{{ signing?.index_offset }}</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge badge-sm text-white" :class="Number(signing?.missed_blocks_counter) < 10?'badge-success':'badge-error'">{{ signing?.missed_blocks_counter }}</span>
|
||||||
|
</td>
|
||||||
|
<td><span v-if="signing && signing.jailed_until.startsWith('1970')">{{ format.percent(Number(signing.index_offset)/(latest-Number(signing.start_height))) }}</span></td>
|
||||||
|
<td><span v-if="signing && !signing.jailed_until.startsWith('1970')">
|
||||||
|
<div class="tooltip" :data-tip="format.toDay(signing?.jailed_until, 'long')">
|
||||||
|
<span>{{ format.toDay(signing?.jailed_until, "from") }}</span>
|
||||||
|
</div>
|
||||||
|
</span></td>
|
||||||
|
<td class=" capitalize">{{ signing?.tombstoned }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="h-6"></div>
|
<div class="h-6"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<route>
|
<route>
|
||||||
{
|
{
|
||||||
|
211
src/modules/[chain]/uptime/overview.vue
Normal file
211
src/modules/[chain]/uptime/overview.vue
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted, computed, watchEffect } from 'vue';
|
||||||
|
import { fromHex, toBase64 } from '@cosmjs/encoding';
|
||||||
|
import { Icon } from '@iconify/vue';
|
||||||
|
import {
|
||||||
|
useFormatter,
|
||||||
|
useStakingStore,
|
||||||
|
useBaseStore,
|
||||||
|
useBlockchain,
|
||||||
|
useDashboard,
|
||||||
|
} from '@/stores';
|
||||||
|
import UptimeBar from '@/components/UptimeBar.vue';
|
||||||
|
import type { Block, Commit } from '@/types';
|
||||||
|
import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
|
||||||
|
import type { SigningInfo } from '@/types/slashing';
|
||||||
|
import { CosmosRestClient } from '@/libs/client';
|
||||||
|
|
||||||
|
const props = defineProps(['chain']);
|
||||||
|
|
||||||
|
const stakingStore = useStakingStore();
|
||||||
|
const format = useFormatter();
|
||||||
|
const chainStore = useBlockchain();
|
||||||
|
const dashboard = useDashboard()
|
||||||
|
// storage local favorite validator ids
|
||||||
|
const local = ref(JSON.parse(localStorage.getItem('uptime-validators') || '{}') as Record<string, {name: string, address: string}[]>)
|
||||||
|
const signingInfo = ref({} as Record<string, SigningInfo[]>);
|
||||||
|
const selected = ref([] as string[])
|
||||||
|
const selectChain = ref(chainStore.chainName)
|
||||||
|
const validators = ref(stakingStore.validators)
|
||||||
|
const keyword = ref("")
|
||||||
|
|
||||||
|
if(local.value) Object.keys(local.value).map(chainName => {
|
||||||
|
const chain = dashboard.chains[chainName]
|
||||||
|
if(chain && chain.endpoints.rest) {
|
||||||
|
const client = CosmosRestClient.newDefault(chain.endpoints.rest[0].address)
|
||||||
|
client.getSlashingSigningInfos().then( resp => {
|
||||||
|
signingInfo.value[chainName] = resp.info
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if(chainName === selectChain.value) {
|
||||||
|
const vals = local.value[chainName]
|
||||||
|
if(vals) {
|
||||||
|
selected.value = vals.map(x => x.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain
|
||||||
|
})
|
||||||
|
|
||||||
|
const filterValidators = computed(() => {
|
||||||
|
if(keyword.value) {
|
||||||
|
return validators.value.filter(x => x.description.moniker.indexOf(keyword.value) > -1)
|
||||||
|
}
|
||||||
|
return validators.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const list = computed(() => {
|
||||||
|
const list = [] as any[]
|
||||||
|
if(local.value) Object.keys(local.value).map( chainName => {
|
||||||
|
const vals = local.value[chainName]
|
||||||
|
const info = signingInfo.value[chainName]
|
||||||
|
if(vals && info) {
|
||||||
|
vals.forEach(v => {
|
||||||
|
const sigingInfo = info.find(x => valconsToBase64(x.address) === v.address)
|
||||||
|
list.push( {
|
||||||
|
chainName,
|
||||||
|
v,
|
||||||
|
sigingInfo,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
})
|
||||||
|
|
||||||
|
function add() {
|
||||||
|
const newList = [] as { name: string; address: string; }[]
|
||||||
|
selected.value.forEach(x => {
|
||||||
|
const validator = validators.value.find(v => (consensusPubkeyToHexAddress(v.consensus_pubkey) === x))
|
||||||
|
if(validator) newList.push({
|
||||||
|
name: validator.description.moniker || x,
|
||||||
|
address: x
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if(!local.value) local.value = {}
|
||||||
|
local.value[selectChain.value] = newList
|
||||||
|
localStorage.setItem("uptime-validators", JSON.stringify(local.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeChain() {
|
||||||
|
validators.value = []
|
||||||
|
const endpoint = dashboard.chains[selectChain.value].endpoints.rest?.at(0)?.address
|
||||||
|
if(!endpoint) return
|
||||||
|
|
||||||
|
const client = CosmosRestClient.newDefault(endpoint)
|
||||||
|
client.getStakingValidators("BOND_STATUS_BONDED").then(x => {
|
||||||
|
validators.value = x.validators
|
||||||
|
})
|
||||||
|
|
||||||
|
const vals = local.value[selectChain.value]
|
||||||
|
if(vals) {
|
||||||
|
selected.value = vals.map(x => x.address)
|
||||||
|
} else {
|
||||||
|
selected.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function color(v: string) {
|
||||||
|
if(v) {
|
||||||
|
const n = Number(v)
|
||||||
|
if(n < 10) return " badge-success"
|
||||||
|
if(n > 1000) return " badge-error"
|
||||||
|
if(n > 0) return " badge-warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="overflow-x-auto w-full card ">
|
||||||
|
<div class="lg:flex lg:items-center lg:justify-between bg-base-100 p-5">
|
||||||
|
<div class="min-w-0 flex-1">
|
||||||
|
<h2 class="text-2xl font-bold leading-7 sm:truncate sm:text-3xl sm:tracking-tight">My Validators</h2>
|
||||||
|
<div class="mt-1 flex flex-col sm:mt-0 sm:flex-row sm:flex-wrap sm:space-x-6">
|
||||||
|
<div class="mt-2 flex items-center text-sm text-gray-500">
|
||||||
|
<svg class="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400" viewBox="0 0 20 20" fill="currentColor"
|
||||||
|
aria-hidden="true">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M6 3.75A2.75 2.75 0 018.75 1h2.5A2.75 2.75 0 0114 3.75v.443c.572.055 1.14.122 1.706.2C17.053 4.582 18 5.75 18 7.07v3.469c0 1.126-.694 2.191-1.83 2.54-1.952.599-4.024.921-6.17.921s-4.219-.322-6.17-.921C2.694 12.73 2 11.665 2 10.539V7.07c0-1.321.947-2.489 2.294-2.676A41.047 41.047 0 016 4.193V3.75zm6.5 0v.325a41.622 41.622 0 00-5 0V3.75c0-.69.56-1.25 1.25-1.25h2.5c.69 0 1.25.56 1.25 1.25zM10 10a1 1 0 00-1 1v.01a1 1 0 001 1h.01a1 1 0 001-1V11a1 1 0 00-1-1H10z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
<path
|
||||||
|
d="M3 15.055v-.684c.126.053.255.1.39.142 2.092.642 4.313.987 6.61.987 2.297 0 4.518-.345 6.61-.987.135-.041.264-.089.39-.142v.684c0 1.347-.985 2.53-2.363 2.686a41.454 41.454 0 01-9.274 0C3.985 17.585 3 16.402 3 15.055z" />
|
||||||
|
</svg>
|
||||||
|
Add validators you want to monitor
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 flex lg:ml-4 lg:mt-0">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="table w-full">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Blockchain</th>
|
||||||
|
<th>Validator</th>
|
||||||
|
<th>Missing Blocks</th>
|
||||||
|
<th>Signed Blocks</th>
|
||||||
|
<th>Last Jailed Time</th>
|
||||||
|
<th>Tombstoned</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="v in list">
|
||||||
|
<td class=" capitalize">{{ v.chainName }}</td>
|
||||||
|
<td>{{ v.v.name }}</td>
|
||||||
|
<td><span class="badge " :class="color( v.sigingInfo?.missed_blocks_counter)">{{ v.sigingInfo?.missed_blocks_counter }}</span></td>
|
||||||
|
<td><span v-if="v.sigingInfo">{{ Number(v.sigingInfo.index_offset) - Number(v.sigingInfo.start_height) }}</span></td>
|
||||||
|
<td>
|
||||||
|
<div v-if="v.sigingInfo && !v.sigingInfo?.jailed_until.startsWith('1970')" class="text-xs flex flex-col">
|
||||||
|
<div class="badge">{{ format.toDay(v.sigingInfo.jailed_until, 'from') }}</div>
|
||||||
|
<div>{{v.sigingInfo?.jailed_until }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class=" capitalize">{{ v.sigingInfo?.tombstoned }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="add-validator" class="btn btn-primary mt-5">Add Validators</label>
|
||||||
|
|
||||||
|
<!-- Put this part before </body> tag -->
|
||||||
|
<input type="checkbox" id="add-validator" class="modal-toggle" />
|
||||||
|
<div class="modal">
|
||||||
|
<div class="modal-box relative">
|
||||||
|
<label for="add-validator" class="btn btn-sm btn-circle absolute right-2 top-2">✕</label>
|
||||||
|
<h3 class="text-lg font-bold">Add Validators</h3>
|
||||||
|
<div class="py-4 max-h-60 overflow-y-auto">
|
||||||
|
<div class="form-control my-5 border-2">
|
||||||
|
<div class="input-group input-group-md">
|
||||||
|
<select v-model="selectChain" class="select select-bordered capitalize" @change="changeChain">
|
||||||
|
<option v-for="v in dashboard.chains" :value="v.chainName">
|
||||||
|
{{ v.chainName }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<input v-model="keyword" type="text" class="input w-full" placeholder="keywords to filter validator">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="table table-compact w-full hover">
|
||||||
|
<thead>
|
||||||
|
<tr><th>Validator</th><th></th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="v in filterValidators">
|
||||||
|
<td><label :for="v.operator_address"><div class=" w-full">{{ v.description.moniker }}</div></label></td>
|
||||||
|
<td><input :id="v.operator_address" v-model="selected" class="checkbox" type="checkbox" :value="consensusPubkeyToHexAddress(v.consensus_pubkey)"/></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="modal-action">
|
||||||
|
<label for="add-validator" class="btn" @click="add">add</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="h-6"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -13,8 +13,10 @@ router.beforeEach((to) => {
|
|||||||
const { chain } = to.params
|
const { chain } = to.params
|
||||||
if(chain){
|
if(chain){
|
||||||
const blockchain = useBlockchain()
|
const blockchain = useBlockchain()
|
||||||
|
if(chain !== blockchain.chainName) {
|
||||||
blockchain.setCurrent(chain.toString())
|
blockchain.setCurrent(chain.toString())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Docs: https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
|
// Docs: https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
|
||||||
|
@ -122,21 +122,29 @@ export const useBlockchain = defineStore('blockchain', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async randomSetupEndpoint() {
|
async randomSetupEndpoint() {
|
||||||
|
const end = localStorage.getItem(`endpoint-${this.chainName}`)
|
||||||
|
if(end) {
|
||||||
|
this.setRestEndpoint(JSON.parse(end))
|
||||||
|
} else {
|
||||||
const all = this.current?.endpoints?.rest;
|
const all = this.current?.endpoints?.rest;
|
||||||
if (all) {
|
if (all) {
|
||||||
const rn = Math.random();
|
const rn = Math.random();
|
||||||
const endpoint = all[Math.floor(rn * all.length)];
|
const endpoint = all[Math.floor(rn * all.length)];
|
||||||
await this.setRestEndpoint(endpoint);
|
await this.setRestEndpoint(endpoint);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async setRestEndpoint(endpoint: Endpoint) {
|
async setRestEndpoint(endpoint: Endpoint) {
|
||||||
this.connErr = '';
|
this.connErr = '';
|
||||||
this.endpoint = endpoint;
|
this.endpoint = endpoint;
|
||||||
this.rpc = new CosmosRestClient(endpoint.address, DEFAULT);
|
this.rpc = new CosmosRestClient(endpoint.address, DEFAULT);
|
||||||
|
localStorage.setItem(`endpoint-${this.chainName}`, JSON.stringify(endpoint))
|
||||||
},
|
},
|
||||||
setCurrent(name: string) {
|
setCurrent(name: string) {
|
||||||
|
if(name !== this.chainName) {
|
||||||
this.chainName = name;
|
this.chainName = name;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user