add uptime, transaction
This commit is contained in:
parent
6c053ac065
commit
d7472e30eb
@ -23,6 +23,7 @@
|
||||
"summary": "Summary",
|
||||
"blocks": "Blocks",
|
||||
"blockchains": "Blockchains",
|
||||
"uptime": "Uptime",
|
||||
|
||||
"proposal_id": "Proposal ID",
|
||||
"proposal_type": "Proposal Type",
|
||||
|
@ -189,8 +189,11 @@ export function abbrMessage(msg) {
|
||||
if (Array.isArray(msg)) {
|
||||
return msg.map(x => abbrMessage(x)).join(', ')
|
||||
}
|
||||
if (msg.typeUrl) {
|
||||
return msg.typeUrl.substring(msg.typeUrl.lastIndexOf('.') + 1).replace('Msg', '')
|
||||
}
|
||||
return msg.type.substring(msg.type.lastIndexOf('/') + 1).replace('Msg', '')
|
||||
}
|
||||
|
||||
export function abbrAddress(address, length = 10) {
|
||||
return address.substring(0, length).concat('...', address.substring(address.length - length))
|
||||
|
@ -51,6 +51,10 @@ const chainAPI = class ChainFetch {
|
||||
return this.get(`/blocks/${height}`).then(data => Block.create(data))
|
||||
}
|
||||
|
||||
async getSlashingSigningInfo() {
|
||||
return this.get('/cosmos/slashing/v1beta1/signing_infos')
|
||||
}
|
||||
|
||||
async getTxs(hash) {
|
||||
const ver = this.getSelectedConfig() ? this.config.sdk_version : '0.41'
|
||||
// /cosmos/tx/v1beta1/txs/{hash}
|
||||
@ -60,6 +64,18 @@ const chainAPI = class ChainFetch {
|
||||
return this.get(`/cosmos/tx/v1beta1/txs/${hash}`).then(data => WrapStdTx.create(data, ver))
|
||||
}
|
||||
|
||||
async getTxsBySender(sender, page = 1) {
|
||||
return this.get(`/txs?message.sender=${sender}&page=${page}&limit=20`)
|
||||
}
|
||||
|
||||
async getTxsByRecipient(recipient) {
|
||||
return this.get(`/txs?message.recipient=${recipient}`)
|
||||
}
|
||||
|
||||
async getTxsByHeight(height) {
|
||||
return this.get(`/txs?tx.height=${height}`)
|
||||
}
|
||||
|
||||
async getValidatorDistribution(address) {
|
||||
return this.get(`/distribution/validators/${address}`).then(data => {
|
||||
const ret = ValidatorDistribution.create(commonProcess(data))
|
||||
|
@ -21,6 +21,11 @@ const modules = [
|
||||
title: 'governance',
|
||||
route: 'governance',
|
||||
},
|
||||
{
|
||||
scope: 'normal',
|
||||
title: 'uptime',
|
||||
route: 'uptime',
|
||||
},
|
||||
]
|
||||
|
||||
function processMenu() {
|
||||
|
@ -112,6 +112,20 @@ const router = new VueRouter({
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/:chain/uptime',
|
||||
name: 'uptime',
|
||||
component: () => import('@/views/Uptime.vue'),
|
||||
meta: {
|
||||
pageTitle: 'Uptime',
|
||||
breadcrumb: [
|
||||
{
|
||||
text: 'Uptime',
|
||||
active: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/:chain/account/:address',
|
||||
name: 'chain-account',
|
||||
|
@ -236,28 +236,34 @@
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-card title="Uptime">
|
||||
<b-card-body class="border-top">
|
||||
<b-button
|
||||
v-for="(b,i) in blocks"
|
||||
:key="i"
|
||||
v-b-tooltip.hover.v-second
|
||||
:variant="b[0]?'primary':'outline-danger'"
|
||||
:title="b"
|
||||
rounded
|
||||
size="sm"
|
||||
class="btn-icon mb-25 mr-25"
|
||||
> </b-button>
|
||||
</b-card-body>
|
||||
<b-card-footer>
|
||||
<router-link :to="`../staking`">
|
||||
<b-button
|
||||
variant="outline-primary"
|
||||
<b-card title="Transactions">
|
||||
<b-table
|
||||
:items="txs"
|
||||
striped
|
||||
hover
|
||||
responsive="sm"
|
||||
stacked="sm"
|
||||
>
|
||||
{{ $t('btn_back_list') }}
|
||||
</b-button>
|
||||
<template #cell(height)="data">
|
||||
<router-link :to="`../blocks/${data.item.height}`">
|
||||
{{ data.item.height }}
|
||||
</router-link>
|
||||
</b-card-footer>
|
||||
</template>
|
||||
<template #cell(txhash)="data">
|
||||
<router-link :to="`../tx/${data.item.txhash}`">
|
||||
{{ formatHash(data.item.txhash) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</b-table>
|
||||
<b-pagination
|
||||
v-if="Number(transactions.page_total) > 1"
|
||||
:total-rows="transactions.total_count"
|
||||
:per-page="transactions.limit"
|
||||
:value="transactions.page_number"
|
||||
align="center"
|
||||
class="mt-1"
|
||||
@change="pageload"
|
||||
/>
|
||||
</b-card>
|
||||
</b-col>
|
||||
</b-row>
|
||||
@ -268,11 +274,11 @@
|
||||
|
||||
<script>
|
||||
import {
|
||||
BCard, BButton, BAvatar, BRow, BCol, BCardBody, BCardFooter, VBTooltip, VBModal, BBadge,
|
||||
BCard, BButton, BAvatar, BRow, BCol, BTable, BCardFooter, VBTooltip, VBModal, BBadge, BPagination,
|
||||
} from 'bootstrap-vue'
|
||||
|
||||
import {
|
||||
percent, formatToken, StakingParameters, Validator, operatorAddressToAccount, consensusPubkeyToHexAddress, toDay,
|
||||
percent, formatToken, StakingParameters, Validator, operatorAddressToAccount, consensusPubkeyToHexAddress, toDay, abbrMessage, abbrAddress,
|
||||
} from '@/libs/data'
|
||||
import { keybase } from '@/libs/fetch'
|
||||
import StakingAddressComponent from './StakingAddressComponent.vue'
|
||||
@ -287,9 +293,10 @@ export default {
|
||||
BRow,
|
||||
BCol,
|
||||
BAvatar,
|
||||
BCardBody,
|
||||
BCardFooter,
|
||||
BBadge,
|
||||
BPagination,
|
||||
BTable,
|
||||
StakingAddressComponent,
|
||||
StakingCommissionComponent,
|
||||
StakingRewardComponent,
|
||||
@ -319,8 +326,22 @@ export default {
|
||||
userData: {},
|
||||
blocks: Array.from('0'.repeat(100)).map(x => [Boolean(x), Number(x)]),
|
||||
distribution: {},
|
||||
transactions: {},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
txs() {
|
||||
if (this.transactions.txs) {
|
||||
return this.transactions.txs.map(x => ({
|
||||
height: Number(x.height),
|
||||
txhash: x.txhash,
|
||||
msgs: abbrMessage(x.tx.value ? x.tx.value.msg : x.tx.msg),
|
||||
time: toDay(x.timestamp),
|
||||
}))
|
||||
}
|
||||
return []
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$http.getStakingPool().then(res => { this.stakingPool = res })
|
||||
this.$http.getStakingParameters().then(res => { this.stakingParameter = res })
|
||||
@ -331,6 +352,9 @@ export default {
|
||||
this.validator = data
|
||||
|
||||
this.processAddress(data.operator_address, data.consensus_pubkey)
|
||||
this.$http.getTxsBySender(this.accountAddress).then(res => {
|
||||
this.transactions = res
|
||||
})
|
||||
|
||||
const { identity } = data.description
|
||||
keybase(identity).then(d => {
|
||||
@ -340,12 +364,14 @@ export default {
|
||||
}
|
||||
})
|
||||
})
|
||||
this.initBlocks()
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.timer)
|
||||
},
|
||||
methods: {
|
||||
pageload(v) {
|
||||
this.$http.getTxsBySender(this.accountAddress, v).then(res => {
|
||||
this.transactions = res
|
||||
})
|
||||
},
|
||||
formatHash: abbrAddress,
|
||||
timeFormat(value) {
|
||||
return toDay(value)
|
||||
},
|
||||
@ -365,30 +391,6 @@ export default {
|
||||
apr(rate) {
|
||||
return `${percent((1 - rate) * this.mintInflation)} %`
|
||||
},
|
||||
initBlocks() {
|
||||
this.$http.getLatestBlock().then(d => {
|
||||
const { height } = d.block.last_commit
|
||||
|
||||
// update height
|
||||
const blocks = []
|
||||
for (let i = height - 99; i < height; i += 1) {
|
||||
blocks.push([false, i])
|
||||
}
|
||||
const sig = d.block.last_commit.signatures.find((s => s.validator_address === this.hexAddress))
|
||||
const exist = typeof sig !== 'undefined'
|
||||
blocks.push([exist, height])
|
||||
this.blocks = blocks
|
||||
|
||||
// update uptime status
|
||||
const previous = []
|
||||
blocks.forEach(item => {
|
||||
previous.push(this.fetch_status(item))
|
||||
})
|
||||
Promise.allSettled(previous).then(() => {
|
||||
this.timer = setInterval(this.fetch_latest, 6000)
|
||||
})
|
||||
})
|
||||
},
|
||||
fetch_status(item, lastHeight) {
|
||||
return this.$http.getBlockByHeight(item[1]).then(res => {
|
||||
if (item[1] !== lastHeight) {
|
||||
|
136
src/views/Uptime.vue
Normal file
136
src/views/Uptime.vue
Normal file
@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<div class="container-md px-0">
|
||||
<b-card>
|
||||
<b-card
|
||||
no-body
|
||||
class="mb-1"
|
||||
>
|
||||
<b-form-input
|
||||
v-model="query"
|
||||
placeholder="Keywords to filter validators"
|
||||
/>
|
||||
</b-card>
|
||||
<b-row>
|
||||
<b-col
|
||||
v-for="x in uptime"
|
||||
:key="x.moniker"
|
||||
sm="12"
|
||||
md="6"
|
||||
>
|
||||
<span class="font-weight-bold">{{ x.validator.moniker?x.validator.moniker:x.address }}</span>
|
||||
<div class="d-flex justify-content-between align-self-stretch flex-wrap">
|
||||
<div
|
||||
v-for="(b,i) in blocks"
|
||||
:key="i"
|
||||
style="width:1.5%; margin:0.5px; border-radius: 3px; display: inline-block;"
|
||||
><router-link :to="`./blocks/${b.height}`">
|
||||
<div
|
||||
v-b-tooltip.hover.v-second
|
||||
:title="b.height"
|
||||
:class="b.sigs && b.sigs[x.address] ? 'bg-success':'bg-danger'"
|
||||
> </div>
|
||||
</router-link>
|
||||
</div>
|
||||
</div></b-col>
|
||||
</b-row>
|
||||
</b-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
BRow, BCol, VBTooltip, BFormInput, BCard,
|
||||
} from 'bootstrap-vue'
|
||||
|
||||
import { consensusPubkeyToHexAddress } from '@/libs/data'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BRow,
|
||||
BCol,
|
||||
BFormInput,
|
||||
BCard,
|
||||
},
|
||||
directives: {
|
||||
'b-tooltip': VBTooltip,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
query: '',
|
||||
validators: [],
|
||||
missing: {},
|
||||
blocks: Array.from('0'.repeat(100)).map(x => [Boolean(x), Number(x)]),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
uptime() {
|
||||
const vals = this.query ? this.validators.filter(x => String(x.description.moniker).indexOf(this.query) > -1) : this.validators
|
||||
return vals.map(x => ({
|
||||
validator: x.description,
|
||||
address: consensusPubkeyToHexAddress(x.consensus_pubkey),
|
||||
}))
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$http.getValidatorList().then(res => {
|
||||
this.validators = res
|
||||
})
|
||||
this.initBlocks()
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.timer)
|
||||
},
|
||||
methods: {
|
||||
initBlocks() {
|
||||
this.$http.getLatestBlock().then(d => {
|
||||
const { height } = d.block.last_commit
|
||||
|
||||
// update height
|
||||
const blocks = []
|
||||
for (let i = height - 49; i < height; i += 1) {
|
||||
blocks.push({ sigs: {}, height: i })
|
||||
this.fetch_status(i, height)
|
||||
}
|
||||
const sigs = {}
|
||||
d.block.last_commit.signatures.forEach(x => {
|
||||
sigs[x.validator_address] = !!x.signature
|
||||
})
|
||||
blocks.push({ sigs, height })
|
||||
this.blocks = blocks
|
||||
|
||||
this.timer = setInterval(this.fetch_latest, 6000)
|
||||
})
|
||||
},
|
||||
fetch_status(height, lastHeight) {
|
||||
return this.$http.getBlockByHeight(height).then(res => {
|
||||
if (height !== lastHeight) {
|
||||
const sigs = {}
|
||||
res.block.last_commit.signatures.forEach(x => {
|
||||
sigs[x.validator_address] = !!x.signature
|
||||
})
|
||||
const block = this.blocks.find(b => b.height === height)
|
||||
if (typeof block !== 'undefined') {
|
||||
this.$set(block, 'sigs', sigs)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
fetch_latest() {
|
||||
this.$http.getLatestBlock().then(res => {
|
||||
const sigs = {}
|
||||
res.block.last_commit.signatures.forEach(x => {
|
||||
sigs[x.validator_address] = !!x.signature
|
||||
})
|
||||
const block = this.blocks.find(b => b[1] === res.block.last_commit.height)
|
||||
if (typeof block === 'undefined') { // mei
|
||||
// this.$set(block, 0, typeof sigs !== 'undefined')
|
||||
if (this.blocks.length > 50) this.blocks.shift()
|
||||
this.blocks.push({ sigs, height: res.block.last_commit.height })
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -189,6 +189,37 @@
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
|
||||
<b-card title="Transactions">
|
||||
<b-table
|
||||
:items="txs"
|
||||
striped
|
||||
hover
|
||||
responsive="sm"
|
||||
stacked="sm"
|
||||
>
|
||||
<template #cell(height)="data">
|
||||
<router-link :to="`../blocks/${data.item.height}`">
|
||||
{{ data.item.height }}
|
||||
</router-link>
|
||||
</template>
|
||||
<template #cell(txhash)="data">
|
||||
<router-link :to="`../tx/${data.item.txhash}`">
|
||||
{{ formatHash(data.item.txhash) }}
|
||||
</router-link>
|
||||
</template>
|
||||
</b-table>
|
||||
|
||||
<b-pagination
|
||||
v-if="Number(transactions.page_total) > 1"
|
||||
:total-rows="transactions.total_count"
|
||||
:per-page="transactions.limit"
|
||||
:value="transactions.page_number"
|
||||
align="center"
|
||||
class="mt-1"
|
||||
@change="pageload"
|
||||
/>
|
||||
</b-card>
|
||||
|
||||
<b-card
|
||||
v-if="account"
|
||||
title="Profile"
|
||||
@ -324,7 +355,7 @@
|
||||
<script>
|
||||
import {
|
||||
BCard, BAvatar, BPopover, BTable, BRow, BCol, BTableSimple, BTr, BTd, BTbody, BCardHeader, BCardTitle, BButton, BCardBody, VBModal,
|
||||
BButtonGroup, VBTooltip,
|
||||
BButtonGroup, VBTooltip, BPagination,
|
||||
} from 'bootstrap-vue'
|
||||
import FeatherIcon from '@/@core/components/feather-icon/FeatherIcon.vue'
|
||||
import ToastificationContent from '@core/components/toastification/ToastificationContent.vue'
|
||||
@ -332,7 +363,7 @@ import Ripple from 'vue-ripple-directive'
|
||||
import VueQr from 'vue-qr'
|
||||
import chainAPI from '@/libs/fetch'
|
||||
import {
|
||||
formatToken, formatTokenAmount, formatTokenDenom, getStakingValidatorOperator, percent, tokenFormatter, toDay, toDuration,
|
||||
formatToken, formatTokenAmount, formatTokenDenom, getStakingValidatorOperator, percent, tokenFormatter, toDay, toDuration, abbrMessage, abbrAddress,
|
||||
} from '@/libs/data'
|
||||
import { $themeColors } from '@themeConfig'
|
||||
import ObjectFieldComponent from './ObjectFieldComponent.vue'
|
||||
@ -363,6 +394,7 @@ export default {
|
||||
BButtonGroup,
|
||||
BTr,
|
||||
BTd,
|
||||
BPagination,
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
ToastificationContent,
|
||||
ObjectFieldComponent,
|
||||
@ -393,6 +425,7 @@ export default {
|
||||
redelegations: [],
|
||||
unbonding: [],
|
||||
quotes: {},
|
||||
transactions: [],
|
||||
doughnutChart: {
|
||||
options: {
|
||||
responsive: true,
|
||||
@ -435,6 +468,17 @@ export default {
|
||||
}
|
||||
return 'Profile'
|
||||
},
|
||||
txs() {
|
||||
if (this.transactions.txs) {
|
||||
return this.transactions.txs.map(x => ({
|
||||
height: Number(x.height),
|
||||
txhash: x.txhash,
|
||||
msgs: abbrMessage(x.tx.value.msg),
|
||||
time: toDay(x.timestamp),
|
||||
}))
|
||||
}
|
||||
return []
|
||||
},
|
||||
assetTable() {
|
||||
let total = []
|
||||
let sum = 0
|
||||
@ -606,14 +650,24 @@ export default {
|
||||
this.$http.getStakingUnbonding(this.address).then(res => {
|
||||
this.unbonding = res.unbonding_responses
|
||||
})
|
||||
this.$http.getTxsBySender(this.address).then(res => {
|
||||
this.transactions = res
|
||||
})
|
||||
|
||||
// this.$http.getStakingValidators(this.address).then(res => {
|
||||
// console.log(res)
|
||||
// })
|
||||
},
|
||||
methods: {
|
||||
pageload(v) {
|
||||
this.$http.getTxsBySender(this.address, v).then(res => {
|
||||
this.transactions = res
|
||||
})
|
||||
},
|
||||
selectValue(v) {
|
||||
this.selectedValidator = v
|
||||
},
|
||||
formatHash: abbrAddress,
|
||||
formatDenom(v) {
|
||||
return formatTokenDenom(this.denoms[v] ? this.denoms[v] : v)
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user