finish tx detail

This commit is contained in:
liangping
2023-04-06 08:17:40 +08:00
parent 8e3c9eea0c
commit 70351f3833
31 changed files with 303 additions and 252 deletions
+3 -3
View File
@@ -16,7 +16,7 @@ const height = computed(() => {
onBeforeRouteUpdate(async (to, from, next) => {
if (from.path !== to.path) {
store.fetchBlock(Number(to.params.height))
store.fetchBlock(String(to.params.height))
next()
}
})
@@ -33,7 +33,7 @@ onBeforeRouteUpdate(async (to, from, next) => {
</span>
</VCardTitle>
<VCardItem class="pt-0">
<DynamicComponent :value="store.current.blockId"/>
<DynamicComponent :value="store.current.block_id"/>
</VCardItem>
</VCard>
@@ -51,7 +51,7 @@ onBeforeRouteUpdate(async (to, from, next) => {
<VCard title="Last Commit" class="mt-5">
<VCardItem class="pt-0">
<DynamicComponent :value="store.current.block?.lastCommit"/>
<DynamicComponent :value="store.current.block?.last_commit"/>
</VCardItem>
</VCard>
</div>
+4 -4
View File
@@ -22,7 +22,7 @@ export const useBlockModule = defineStore('blockModule', {
},
txsInRecents() {
const txs = [] as {hash:string, tx: DecodedTxRaw}[]
this.recents.forEach((x:GetLatestBlockResponse) => x.block?.data?.txs.forEach((tx:Uint8Array) => txs.push({
this.recents.forEach((x) => x.block?.data?.txs.forEach((tx:Uint8Array) => txs.push({
hash: hashTx(tx),
tx :decodeTxRaw(tx)
})))
@@ -47,13 +47,13 @@ export const useBlockModule = defineStore('blockModule', {
})
},
async fetchLatest() {
this.latest = await this.blockchain.rpc.block()
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?: number) {
this.current = await this.blockchain.rpc.block(height)
async fetchBlock(height: string) {
this.current = await this.blockchain.rpc.getBaseBlockAt(height)
return this.current
},
}
+4 -7
View File
@@ -1,14 +1,11 @@
<script lang="ts" setup>
import TxsElement from '@/components/dynamic/TxsElement.vue';
import { useBlockModule } from './block'
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import { computed } from '@vue/reactivity';
import { toBase64, toHex } from '@cosmjs/encoding';
import { computed, ref } from '@vue/reactivity';
import { useFormatter } from '@/stores';
const props = defineProps(["height", "chain"]);
const store = useBlockModule()
store.fetchBlock(props.height)
// store.fetchBlock(props.height)
const tab = ref('blocks')
const format = useFormatter()
@@ -33,8 +30,8 @@ const format = useFormatter()
<tbody>
<tr v-for="item in store.recents">
<td><RouterLink :to="`/${props.chain}/block/${item.block?.header?.height}`">{{ item.block?.header?.height }}</RouterLink></td>
<td>{{ toBase64(item.blockId?.hash) }}</td>
<td>{{ format.validator(item.block?.header?.proposerAddress) }}</td>
<td>{{ item.block_id?.hash }}</td>
<td>{{ format.validator(item.block?.header?.proposer_address) }}</td>
<td>{{ item.block?.data?.txs.length }}</td>
<td>{{ format.toDay(item.block?.header?.time, 'from') }}</td>
</tr>
+3 -3
View File
@@ -146,11 +146,11 @@ function shortName(name: string, id: string) {
<VExpansionPanels variant="accordion">
<VExpansionPanel v-for="(x, i) in store.proposals">
<VExpansionPanelTitle disable-icon-rotate>
<VChip label color="primary" class="mr-2">{{x.proposalId}}</VChip>
<VChip label color="primary" class="mr-2">{{x.proposal_id}}</VChip>
<div class="w-100">{{ x.content?.title }}
<div class="d-flex mt-1">
<small class="text-secondary me-auto"> {{ format.toDay(x.votingEndTime, 'from') }}</small>
<ProposalProcess style="width:300px;" :pool="store.pool" :tally="store.tally[Number(x.proposalId)]"></ProposalProcess>
<small class="text-secondary me-auto"> {{ format.toDay(x.voting_end_time, 'from') }}</small>
<ProposalProcess style="width:300px;" :pool="store.pool" :tally="store.tally[x.proposal_id]"></ProposalProcess>
<span></span>
</div>
</div>
+9 -13
View File
@@ -2,6 +2,7 @@ import { useBlockchain, useCoingecko, useBaseStore, useBankStore, useFormatter,
import { useDistributionStore } from "@/stores/useDistributionStore";
import { useMintStore } from "@/stores/useMintStore";
import { useStakingStore } from "@/stores/useStakingStore";
import type { GovProposal, Tally } from "@/types";
import numeral from "numeral";
import { defineStore } from "pinia";
@@ -64,13 +65,8 @@ export const useIndexModule = defineStore('module-index', {
total_volumes: [] as number[],
},
communityPool: [] as {amount: string, denom: string}[],
proposals: [] as Proposal[],
tally: {} as Record<number, {
yes: string;
abstain: string;
no: string;
noWithVeto: string;
}>
proposals: [] as GovProposal[],
tally: {} as Record<string, Tally>
}
},
getters: {
@@ -144,7 +140,7 @@ export const useIndexModule = defineStore('module-index', {
title: 'Validators',
color: 'error',
icon: 'mdi-human-queue',
stats: String(base.latest.block?.lastCommit?.signatures.length || 0),
stats: String(base.latest.block?.last_commit?.signatures.length || 0),
change: 0,
},
{
@@ -158,7 +154,7 @@ export const useIndexModule = defineStore('module-index', {
title: 'Bonded Tokens',
color: 'warning',
icon: 'mdi-lock',
stats: formatter.formatTokenAmount({amount: this.pool.bondedTokens, denom: staking.params.bondDenom }),
stats: formatter.formatTokenAmount({amount: this.pool.bonded_tokens, denom: staking.params.bond_denom }),
change: 0,
},
{
@@ -185,18 +181,18 @@ export const useIndexModule = defineStore('module-index', {
this.initCoingecko()
useMintStore().fetchInflation()
useDistributionStore().fetchCommunityPool().then(x => {
this.communityPool = x.pool.filter(t=> t.denom.length < 10).map(t => ({
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(ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD).then(x => {
gov.fetchProposals(2).then(x => {
this.proposals = x.proposals
x.proposals.forEach(x1 => {
gov.fetchTally(Number(x1.proposalId)).then(t => {
gov.fetchTally(x1.proposal_id).then(t => {
console.log("log: ", t)
if(t.tally) this.tally[Number(x1.proposalId)] = t.tally
if(t.tally) this.tally[x1.proposal_id] = t.tally
})
})
})
+24 -28
View File
@@ -1,11 +1,9 @@
<script setup lang="ts">
import { useBlockchain, useFormatter, useMintStore, useStakingStore } from '@/stores';
import { onMounted } from 'vue';
import { onMounted, computed, ref } from 'vue';
import ValidatorCommissionRate from '@/components/ValidatorCommissionRate.vue'
import { consensusPubkeyToHexAddress, operatorAddressToAccount, pubKeyToValcons, valoperToPrefix } from '@/libs';
import { computed } from '@vue/reactivity';
import type { Coin } from '@/types';
import type { Txs } from '@/types/Txs';
import type { Coin, Delegation, PaginatedTxs, Validator } from '@/types';
const props = defineProps(['validator', 'chain'])
@@ -15,45 +13,44 @@ const format = useFormatter()
const validator: string = props.validator
const v = ref({})
const v = ref({} as Validator)
const cache = JSON.parse(localStorage.getItem('avatars')||'{}')
const avatars = ref( cache || {} )
const identity = ref("")
const rewards = ref([] as Coin[])
const rewards = ref([] as Coin[]|undefined)
const addresses = ref({} as {
account: string
operAddress: string
hex: string
valCons: string,
})
const selfBonded = ref({} as Coin)
const selfBonded = ref({} as Delegation)
addresses.value.account = operatorAddressToAccount(validator)
// load self bond
staking.fetchValidatorDelegation(validator, addresses.value.account).then(x => {
if(x) {
selfBonded.value = x
selfBonded.value = x.delegation_response
}
})
const txs = ref({} as Txs)
const txs = ref({} as PaginatedTxs)
blockchain.rpc.txs([`message.sender='${addresses.value.account}'`]).then(x => {
blockchain.rpc.getTxsBySender(addresses.value.account).then(x => {
console.log("txs", x)
txs.value = x
})
const apr = computed(()=> {
const rate = v.value.commission?.commissionRates.rate || 0
const rate = v.value.commission?.commission_rates.rate || 0
const inflation = useMintStore().inflation
if(Number(inflation)) {
return format.percent((1 - rate) * Number(inflation))
return format.percent((1 - Number(rate)) * Number(inflation))
}
return "-"
})
const selfRate = computed(()=> {
console.log("self rate", selfBonded.value.balance?.amount, v.value.tokens)
if(selfBonded.value.balance?.amount) {
return format.calculatePercent(selfBonded.value.balance.amount, v.value.tokens)
}
@@ -62,9 +59,9 @@ const selfRate = computed(()=> {
onMounted(()=> {
if(validator) {
staking.fetchValidator(validator.toString()).then(res => {
staking.fetchValidator(validator).then(res => {
v.value = res.validator
identity.value = res.validator?.description?.identity
identity.value = res.validator?.description?.identity || ''
if(identity.value && !avatars.value[identity.value]) {
console.log(identity.value, avatars)
staking.keybase(identity.value).then(d => {
@@ -77,12 +74,11 @@ onMounted(()=> {
}
})
}
const prefix = valoperToPrefix(v.value.operatorAddress) || '<Invalid>'
addresses.value.hex = consensusPubkeyToHexAddress(v.value.consensusPubkey)
addresses.value.valCons = pubKeyToValcons(v.value.consensusPubkey, prefix)
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.validatorOutstandingRewards(validator).then(res => {
console.log(res)
blockchain.rpc.getDistributionValidatorOutstandingRewards(validator).then(res => {
rewards.value = res.rewards?.rewards
})
}
@@ -114,7 +110,7 @@ onMounted(()=> {
<span>Website: </span><span> {{ v.description?.website || '-' }}</span>
</VListItem>
<VListItem prepend-icon="mdi-phone">
<span>Contact: </span><span> {{ v.description?.securityContact }}</span>
<span>Contact: </span><span> {{ v.description?.security_contact }}</span>
</VListItem>
</VList>
@@ -137,7 +133,7 @@ onMounted(()=> {
<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.bondDenom}) }}</h4>
<h4>{{ format.formatToken2({amount: v.tokens, denom: staking.params.bond_denom}) }}</h4>
<span class="text-sm">Bonded Tokens</span>
</div>
</div>
@@ -152,7 +148,7 @@ onMounted(()=> {
<div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-flag"></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ v.minSelfDelegation }} {{ staking.params.bondDenom }}</h4>
<h4>{{ v.minSelfDelegation }} {{ staking.params.bond_denom }}</h4>
<span class="text-sm">Min Self Delegation:</span>
</div>
</div>
@@ -167,7 +163,7 @@ onMounted(()=> {
<div class="d-flex">
<VAvatar color="secondary" rounded variant="outlined" icon="mdi-pound"></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ v.unbondingHeight }}</h4>
<h4>{{ v.unbonding_height }}</h4>
<span class="text-sm">Unbonding Height</span>
</div>
</div>
@@ -175,7 +171,7 @@ onMounted(()=> {
<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.unbondingTime, 'from') }}</h4>
<h4>{{ format.toDay(v.unbonding_time, 'from') }}</h4>
<span class="text-sm">Unbonding Time</span>
</div>
</div>
@@ -209,7 +205,7 @@ onMounted(()=> {
</VListItem>
<VListItem>
<VListItemTitle>Operator Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{ v.operatorAddress }}</VListItemSubtitle>
<VListItemSubtitle class="text-caption">{{ v.operator_address }}</VListItemSubtitle>
</VListItem>
<VListItem>
<VListItemTitle>Hex Address</VListItemTitle>
@@ -233,10 +229,10 @@ onMounted(()=> {
<th class="text-left pl-4">Time</th>
</thead>
<tbody>
<tr v-for="(item, i) in txs.txResponses">
<tr v-for="(item, i) in txs.tx_responses">
<td>{{ item.height }}</td>
<td class="text-truncate" style="max-width: 200px;">{{ item.txhash }}</td>
<td>{{ format.messages(txs.txs[i]?.body.messages) }}</td>
<td>{{ format.messages(item.tx.body.messages) }}</td>
<td width="150">{{ format.toDay(item.timestamp,'from') }}</td>
</tr>
</tbody>
+32 -32
View File
@@ -1,10 +1,10 @@
<script lang=ts setup>
import { useBaseStore, useFormatter, useStakingStore } from '@/stores';
import { toBase64, toHex } from '@cosmjs/encoding';
import { pubkeyToAddress } from '@cosmjs/tendermint-rpc';
import { computed } from '@vue/reactivity';
import { onMounted, ref, type DebuggerEvent } from 'vue';
import { consensusPubkeyToHexAddress } from '@/libs'
import type { Key, Validator } from '@/types';
const staking = useStakingStore()
const format = useFormatter()
@@ -13,7 +13,7 @@ const avatars = ref( cache || {} )
const latest = ref({} as Record<string, number>)
const yesterday = ref({} as Record<string, number>)
const tab = ref('active')
const unbondList = ref([])
const unbondList = ref([] as Validator[])
const base = useBaseStore()
onMounted(()=> {
fetchChange(0)
@@ -25,39 +25,39 @@ onMounted(()=> {
function fetchChange(offset: number) {
const base = useBaseStore()
const diff = 86400000 / base.blocktime
base.fetchAbciInfo().then(h => {
// console.log('block:', h)
base.fetchValidatorByHeight(h.lastBlockHeight, offset).then(x => {
x.validators.forEach(v => {
if(v.pubkey) latest.value[pubkeyToAddress(v.pubkey.algorithm, v.pubkey.data)] = Number(v.votingPower)
})
})
const height = Number(h.lastBlockHeight) - diff
base.fetchValidatorByHeight(height > 0 ? height : 1, offset).then(old => {
old.validators.forEach(v => {
if(v.pubkey) yesterday.value[pubkeyToAddress(v.pubkey.algorithm, v.pubkey.data)] = Number(v.votingPower)
})
// console.log(Object.keys(yesterday.value).map(x => x.toUpperCase()))
})
})
// base.fetchAbciInfo().then(h => {
// // console.log('block:', h)
// base.fetchValidatorByHeight(h.lastBlockHeight, offset).then(x => {
// x.validators.forEach(v => {
// if(v.pubkey) latest.value[pubkeyToAddress(v.pubkey.algorithm, v.pubkey.data)] = Number(v.votingPower)
// })
// })
// const height = Number(h.lastBlockHeight) - diff
// base.fetchValidatorByHeight(height > 0 ? height : 1, offset).then(old => {
// old.validators.forEach(v => {
// if(v.pubkey) yesterday.value[pubkeyToAddress(v.pubkey.algorithm, v.pubkey.data)] = Number(v.votingPower)
// })
// // console.log(Object.keys(yesterday.value).map(x => x.toUpperCase()))
// })
// })
}
const change24 = (key: {typeUrl: string, value: Uint8Array}) => {
const change24 = (key: Key) => {
// console.log('hex key:', consensusPubkeyToHexAddress(key))
const txt = toBase64(key.value)
const txt = key.key
const n : number = latest.value[txt];
const o : number = yesterday.value[txt]
// console.log( txt, n, o)
return n >0 && o > 0 ? n - o : 0
}
const change24Text = (key?: {typeUrl: string, value: Uint8Array}) => {
const change24Text = (key?: Key) => {
if(!key) return ''
const v = change24(key)
return v!==0 ? format.numberAndSign(v) : ''
}
const change24Color = (key?: {typeUrl: string, value: Uint8Array}) => {
const change24Color = (key?: Key) => {
if(!key) return ''
const v = change24(key)
if(v > 0) return 'text-success'
@@ -71,8 +71,8 @@ const update = (m: DebuggerEvent) => {
}
const list = computed(() => {
// return tab.value === 'active' ? staking.validators: unbondList.value
return staking.validators
return tab.value === 'active' ? staking.validators: unbondList.value
// return staking.validators
})
const loadAvatars = () => {
@@ -116,7 +116,7 @@ const logo = (identity?: string) => {
const rank = function(position: number) {
let sum = 0
for(let i = 0;i < position; i++) {
sum += Number(staking.validators[i]?.delegatorShares)
sum += Number(staking.validators[i]?.delegator_shares)
}
const percent = (sum / staking.totalPower)
@@ -135,7 +135,7 @@ const rank = function(position: number) {
<VBtn value="active" variant="outlined" >Active</VBtn>
<VBtn value="inactive" variant="outlined">Inactive</VBtn>
</VBtnToggle>
<span class="mt-2">{{ list.length }}/{{ staking.params.maxValidators }}</span>
<span class="mt-2">{{ list.length }}/{{ staking.params.max_validators }}</span>
</VCardTitle>
<VTable class="text-no-wrap table-header-bg rounded-0">
<thead>
@@ -164,7 +164,7 @@ const rank = function(position: number) {
<tbody>
<tr
v-for="(v, i) in list"
:key="v.operatorAddress"
:key="v.operator_address"
>
<!-- 👉 rank -->
<td>
@@ -186,7 +186,7 @@ const rank = function(position: number) {
<div class="d-flex flex-column">
<h6 class="text-sm">
<RouterLink
:to="{name: 'chain-staking-validator', params: {validator: v.operatorAddress}}"
:to="{name: 'chain-staking-validator', params: {validator: v.operator_address}}"
class="font-weight-medium user-list-name"
>
{{ v.description?.moniker }}
@@ -202,18 +202,18 @@ const rank = function(position: number) {
<td class="text-right">
<div class="d-flex flex-column">
<h6 class="text-sm font-weight-medium">
{{ format.formatToken( {amount: parseInt(v.tokens).toString(), denom: staking.params.bondDenom }, true, "0,0") }}
{{ format.formatToken( {amount: parseInt(v.tokens).toString(), denom: staking.params.bond_denom }, true, "0,0") }}
</h6>
<span class="text-xs">{{ format.calculatePercent(v.delegatorShares, staking.totalPower) }}</span>
<span class="text-xs">{{ format.calculatePercent(v.delegator_shares, staking.totalPower) }}</span>
</div>
</td>
<!-- 👉 24h Changes -->
<td class="text-right text-xs" :class="change24Color(v.consensusPubkey)">
{{ change24Text(v.consensusPubkey) }} <VChip label v-if="v.jailed" color="error">Jailed</VChip>
<td class="text-right text-xs" :class="change24Color(v.consensus_pubkey)">
{{ change24Text(v.consensus_pubkey) }} <VChip label v-if="v.jailed" color="error">Jailed</VChip>
</td>
<!-- 👉 commission -->
<td class="text-right">
{{ format.formatCommissionRate(v.commission?.commissionRates?.rate) }}
{{ format.formatCommissionRate(v.commission?.commission_rates?.rate) }}
</td>
<!-- 👉 Action -->
<td>
+17 -16
View File
@@ -1,17 +1,19 @@
<script lang="ts" setup>
import { useBlockchain, useFormatter } from '@/stores';
import type { GetTxResponse } from 'cosmjs-types/cosmos/tx/v1beta1/service';
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import { computed } from '@vue/reactivity';
import { fromBase64, toBase64 } from '@cosmjs/encoding';
import { computed, ref } from '@vue/reactivity';
import type { Tx, TxResponse } from '@/types';
const props = defineProps(['hash', 'chain'])
const blockchain = useBlockchain()
const format = useFormatter()
const tx = ref({} as GetTxResponse)
const tx = ref({} as {
tx: Tx;
tx_response: TxResponse
})
if(props.hash) {
blockchain.rpc.tx(props.hash).then(x => tx.value = x)
blockchain.rpc.getTx(props.hash).then(x => tx.value = x)
}
const messages = computed(() => {
return tx.value.tx?.body?.messages||[]
@@ -19,19 +21,19 @@ const messages = computed(() => {
</script>
<template>
<div>
<VCard v-if="tx.txResponse" title="Summary">
<VCard v-if="tx.tx_response" title="Summary">
<VCardItem class="pt-0">
<VTable>
<tbody>
<tr><td>Tx Hash</td><td>{{ tx.txResponse.txhash }}</td></tr>
<tr><td>Height</td><td><RouterLink :to="`/${props.chain}/block/${tx.txResponse.height}`">{{ tx.txResponse.height }}</RouterLink></td></tr>
<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.txResponse.code === 0" color="success">Success</VChip>
<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.txResponse.timestamp }} ({{ format.toDay(tx.txResponse.timestamp, "from") }})</td></tr>
<tr><td>Gas</td><td>{{ tx.txResponse.gasUsed }} / {{ tx.txResponse.gasWanted }}</td></tr>
<tr><td>Fee</td><td>{{ format.formatTokens(tx.tx?.authInfo?.fee?.amount, true, '0,0.[00]') }}</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>
@@ -39,10 +41,9 @@ const messages = computed(() => {
</VCard>
<VCard title="Messages" class="my-5">
<VCardItem>
<VCardItem style="border-top: 2px dotted gray;">
<div v-for="(msg, i) in messages">
<div><VChip label color="primary">#{{ i+1 }}</VChip>{{ msg.typeUrl }}</div>
<div>{{ toBase64(msg.value) }}</div>
<div><DynamicComponent :value="msg" /></div>
</div>
</VCardItem>
</VCard>
@@ -56,7 +57,7 @@ const messages = computed(() => {
</VExpansionPanel>
<VExpansionPanel title="Transaction Response">
<v-expansion-panel-text>
<DynamicComponent :value="tx.txResponse" />
<DynamicComponent :value="tx.tx_response" />
</v-expansion-panel-text>
</VExpansionPanel>
</VExpansionPanels>
+1 -1
View File
@@ -5,7 +5,7 @@ async function tt() {
const address = "echelon1uattqtrtv8944qkmh44ll97qjacj6tgrekqzm9"
const validator = "echelonvaloper1uattqtrtv8944qkmh44ll97qjacj6tgr2cupk4"
const client = new CosmosRestClient("https://api.ech.network")
let response = await client.getSlashingSigningInfos();
let response = await client.getBaseBlockLatest();
console.log('response:', response)
}
tt()