forked from cerc-io/cosmos-explorer
feat: format code
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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"> <><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"
|
||||
>
|
||||
<><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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
+225
-203
@@ -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;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user