forked from cerc-io/cosmos-explorer
change to rpc endpoint
This commit is contained in:
parent
91aea18f61
commit
d02c0c646e
@ -3,7 +3,50 @@
|
||||
"api": [
|
||||
"https://api-cosmoshub-ia.cosmosia.notional.ventures"
|
||||
],
|
||||
"rpc": ["https://rpc.cosmos.network:443", "https://cosmos-rpc.icycro.org", "https://rpc.cosmos.dragonstake.io"],
|
||||
"rpc": [{
|
||||
"address": "http://rpc-cosmoshub.freshstaking.com:26657/",
|
||||
"provider": "FreshSTAKING"
|
||||
},
|
||||
{
|
||||
"address": "https://rpc.cosmos.bh.rocks/",
|
||||
"provider": "BlockHunters 🎯"
|
||||
},
|
||||
{
|
||||
"address": "https://cosmoshub-rpc.lavenderfive.com/",
|
||||
"provider": "Lavender.Five Nodes 🐝"
|
||||
},
|
||||
{
|
||||
"address": "https://cosmos-rpc.polkachu.com/",
|
||||
"provider": "Polkachu"
|
||||
},
|
||||
{
|
||||
"address": "https://rpc.cosmos.dragonstake.io/",
|
||||
"provider": "DragonStake"
|
||||
},
|
||||
{
|
||||
"address": "https://cosmos-rpc.rockrpc.net/",
|
||||
"provider": "RockawayX Infra"
|
||||
},
|
||||
{
|
||||
"address": "https://rpc-cosmoshub.pupmos.network/",
|
||||
"provider": "PUPMØS"
|
||||
},
|
||||
{
|
||||
"address": "https://cosmos-rpc.icycro.org/",
|
||||
"provider": "IcyCRO 🧊"
|
||||
},
|
||||
{
|
||||
"address": "https://rpc.cosmos.interbloc.org/",
|
||||
"provider": "Interbloc"
|
||||
},
|
||||
{
|
||||
"address": "https://rpc.cosmoshub.strange.love/",
|
||||
"provider": "strangelove-ventures"
|
||||
},
|
||||
{
|
||||
"address": "https://rpc-cosmoshub.whispernode.com/",
|
||||
"provider": "WhisperNode🤐"
|
||||
}],
|
||||
"sdk_version": "0.45.1",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "800",
|
||||
|
@ -2,6 +2,7 @@
|
||||
"name": "framework",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"target": "",
|
||||
"scripts": {
|
||||
"serve": "vite",
|
||||
"build": "run-p type-check build-only",
|
||||
@ -13,6 +14,8 @@
|
||||
"dependencies": {
|
||||
"@casl/ability": "^6.3.3",
|
||||
"@casl/vue": "^2.2.1",
|
||||
"@cosmjs/crypto": "^0.29.5",
|
||||
"@cosmjs/encoding": "^0.29.5",
|
||||
"@floating-ui/dom": "^1.2.0",
|
||||
"@iconify/vue": "^4.1.0",
|
||||
"@intlify/unplugin-vue-i18n": "^0.8.2",
|
||||
|
@ -23,8 +23,8 @@ const series = computed(() => {
|
||||
<VTabs v-model="kind" align-tabs="end"><VTab value="price">Price</VTab><VTab value="volume">Volume</VTab></VTabs>
|
||||
<VueApexCharts
|
||||
type="area"
|
||||
height="261"
|
||||
:options="chartConfig"
|
||||
:series="series"
|
||||
style="max-height: 280px;"
|
||||
/>
|
||||
</template>
|
@ -18,9 +18,8 @@ export const getMarketPriceChartConfig = (themeColors: ThemeInstance['themes']['
|
||||
|
||||
return {
|
||||
chart: {
|
||||
redrawOnParentResize: false,
|
||||
redrawOnParentResize: true,
|
||||
width: '100%',
|
||||
height: '260px;',
|
||||
parentHeightOffset: 0,
|
||||
toolbar: { show: false },
|
||||
},
|
||||
|
@ -3,7 +3,7 @@
|
||||
import { useBlockchain, useBaseStore } from '@/stores';
|
||||
const chainStore = useBlockchain()
|
||||
const baseStore = useBaseStore()
|
||||
|
||||
chainStore.initial()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -33,14 +33,14 @@ const baseStore = useBaseStore()
|
||||
>
|
||||
<VList>
|
||||
<!-- 👉 Rest -->
|
||||
<VListSubheader v-if="chainStore.current.endpoints?.rest" title="Rest Endpoint" />
|
||||
<VListItem v-for="i in chainStore.current.endpoints?.rest" link @click="chainStore.setRestEndpoint(i.address)">
|
||||
<VListItemTitle>{{ i.provider }} <VIcon v-if="chainStore.availableEndpoint && i.address === chainStore.availableEndpoint" icon="mdi-check" color="success" /></VListItemTitle>
|
||||
<VListSubheader v-if="chainStore.current?.endpoints?.rpc" title="Rest Endpoint" />
|
||||
<VListItem v-for="i in chainStore.current?.endpoints?.rpc" link @click="chainStore.setRestEndpoint(i)">
|
||||
<VListItemTitle>{{ i.provider }} <VIcon v-if="i.address === chainStore.endpoint?.address" icon="mdi-check" color="success" /></VListItemTitle>
|
||||
<VListItemSubtitle>{{ i.address }}</VListItemSubtitle>
|
||||
</VListItem>
|
||||
|
||||
<VListSubheader v-if="chainStore.current.endpoints?.grpc" title="gRPC Endpoint" />
|
||||
<VListItem v-for="i in chainStore.current.endpoints?.grpc" link>
|
||||
<VListSubheader v-if="chainStore.current?.endpoints?.grpc" title="gRPC Endpoint" />
|
||||
<VListItem v-for="i in chainStore.current?.endpoints?.grpc" link>
|
||||
<VListItemTitle>{{ i.provider }}</VListItemTitle>
|
||||
<VListItemSubtitle>{{ i.address }}</VListItemSubtitle>
|
||||
</VListItem>
|
||||
@ -50,7 +50,7 @@ const baseStore = useBaseStore()
|
||||
</VAvatar>
|
||||
</VBadge>
|
||||
</template>
|
||||
<VListItemTitle>{{ baseStore.latest.block?.header?.chain_id || '' }}</VListItemTitle>
|
||||
<VListItemSubtitle> {{ chainStore.availableEndpoint }}</VListItemSubtitle>
|
||||
<VListItemTitle>{{ baseStore.latest.block?.header?.chain_id || chainStore.chainName || '' }}</VListItemTitle>
|
||||
<VListItemSubtitle> {{ chainStore.connErr|| chainStore.endpoint.address }}</VListItemSubtitle>
|
||||
</VListItem>
|
||||
</template>
|
||||
|
@ -32,7 +32,7 @@ dashboard.initial()
|
||||
const blockchain = useBlockchain()
|
||||
// blockchain.initial()
|
||||
blockchain.$subscribe((m, s) => {
|
||||
if(m.events.key === 'rest') {
|
||||
if(!Array.isArray(m.events) && m.events.key === 'chainName') {
|
||||
blockchain.initial()
|
||||
}
|
||||
})
|
||||
|
36
packages/dashboard/src/libs/address.ts
Normal file
36
packages/dashboard/src/libs/address.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {fromBase64, fromBech32, toBech32, toHex} from '@cosmjs/encoding'
|
||||
import { sha256 } from '@cosmjs/crypto'
|
||||
|
||||
export function decodeAddress(address: string) {
|
||||
return fromBech32(address)
|
||||
}
|
||||
|
||||
export function valoperToPrefix(valoper: string) {
|
||||
const prefixIndex = valoper.indexOf('valoper')
|
||||
if (prefixIndex === -1) return null
|
||||
return valoper.slice(0, prefixIndex)
|
||||
}
|
||||
|
||||
export function operatorAddressToAccount(operAddress: string) {
|
||||
const { prefix, data } = fromBech32(operAddress)
|
||||
if (prefix === 'iva') { // handle special cases
|
||||
return toBech32('iaa', data)
|
||||
}
|
||||
if (prefix === 'crocncl') { // handle special cases
|
||||
return toBech32('cro', data)
|
||||
}
|
||||
return toBech32(prefix.replace('valoper', ''), data)
|
||||
}
|
||||
|
||||
export function pubKeyToValcons(pubkey: string, prefix: string) {
|
||||
const addressData = sha256(fromBase64(pubkey.key)).slice(0, 20)
|
||||
return toBech32(`${prefix}valcons`, addressData)
|
||||
}
|
||||
|
||||
export function toETHAddress(cosmosAddress: string) {
|
||||
return `0x${toHex(fromBech32(cosmosAddress).data)}`
|
||||
}
|
||||
|
||||
export function addressEnCode(prefix: string, pubkey: Uint8Array) {
|
||||
return toBech32(prefix, pubkey)
|
||||
}
|
114
packages/dashboard/src/libs/client.rpc.ts
Normal file
114
packages/dashboard/src/libs/client.rpc.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
|
||||
import { QueryClient, setupBankExtension, setupDistributionExtension, setupGovExtension, setupMintExtension, setupStakingExtension } from "@cosmjs/stargate";
|
||||
import { cosmos } from '@ping-pub/codegen/src/index'
|
||||
import type { BondStatus } from "@ping-pub/codegen/src/cosmos/staking/v1beta1/staking";
|
||||
import long from "long";
|
||||
import type { ProposalStatus } from "@ping-pub/codegen/src/cosmos/gov/v1/gov";
|
||||
|
||||
export declare type BondStatusString = keyof Pick<typeof BondStatus, "BOND_STATUS_BONDED" | "BOND_STATUS_UNBONDED" | "BOND_STATUS_UNBONDING"> | "";
|
||||
|
||||
export class RPCClient {
|
||||
tmClient?: Tendermint34Client;
|
||||
queryClient?: QueryClient;
|
||||
endpoint: string;
|
||||
constructor(endpoint: string) {
|
||||
this.endpoint = endpoint
|
||||
}
|
||||
async getTMClient() {
|
||||
if(this.tmClient) {
|
||||
return this.tmClient
|
||||
} else {
|
||||
return await Tendermint34Client.connect(this.endpoint)
|
||||
}
|
||||
}
|
||||
async getQueryClient() {
|
||||
if(this.queryClient) {
|
||||
return this.queryClient
|
||||
} else {
|
||||
return new QueryClient(await this.getTMClient())
|
||||
}
|
||||
}
|
||||
async supplyOf(denom: string) {
|
||||
return cosmos.bank.v1beta1.createRpcQueryExtension(await this.getQueryClient()).supplyOf({denom})
|
||||
}
|
||||
async balance(address: string, denom: string) {
|
||||
return cosmos.bank.v1beta1.createRpcQueryExtension(await this.getQueryClient()).balance({address, denom})
|
||||
}
|
||||
async allBalance(address: string) {
|
||||
return cosmos.bank.v1beta1.createRpcQueryExtension(await this.getQueryClient()).allBalances({address})
|
||||
}
|
||||
async block(height?: number) {
|
||||
return (await this.getTMClient()).block(height)
|
||||
}
|
||||
async abciInfo() {
|
||||
return (await this.getTMClient()).abciInfo()
|
||||
}
|
||||
async status() {
|
||||
return (await this.getTMClient()).status()
|
||||
}
|
||||
async validatorsAtHeight(height?: number) {
|
||||
return (await this.getTMClient()).validatorsAll(height)
|
||||
}
|
||||
|
||||
async proposal(id: number) {
|
||||
return cosmos.gov.v1beta1.createRpcQueryExtension(await this.getQueryClient()).proposal({proposalId: long.fromNumber(id)})
|
||||
}
|
||||
async tally(id: number) {
|
||||
return cosmos.gov.v1beta1.createRpcQueryExtension(await this.getQueryClient()).tallyResult({proposalId: long.fromNumber(id)})
|
||||
}
|
||||
async proposals(proposalStatus: ProposalStatus, depositor: string, voter: string, ) {
|
||||
return cosmos.gov.v1beta1.createRpcQueryExtension(await this.getQueryClient()).proposals({proposalStatus, depositor, voter, })
|
||||
}
|
||||
async govParam() {
|
||||
const gov = cosmos.gov.v1beta1.createRpcQueryExtension(await this.getQueryClient())
|
||||
const deposit = (await gov.params({paramsType: 'deposit'})).depositParams
|
||||
const voting = (await gov.params({paramsType: 'voting'})).votingParams
|
||||
const tally = (await gov.params({paramsType: 'tallying'})).tallyParams
|
||||
return {
|
||||
deposit, voting, tally
|
||||
}
|
||||
}
|
||||
async communityPool() {
|
||||
return cosmos.distribution.v1beta1.createRpcQueryExtension(await this.getQueryClient()).communityPool()
|
||||
}
|
||||
async distributionParam() {
|
||||
return cosmos.distribution.v1beta1.createRpcQueryExtension(await this.getQueryClient()).params()
|
||||
}
|
||||
async validatorCommission(validatorAddress: string) {
|
||||
return cosmos.distribution.v1beta1.createRpcQueryExtension(await this.getQueryClient()).validatorCommission({validatorAddress})
|
||||
}
|
||||
async validatorOutstandingRewards(validatorAddress: string) {
|
||||
return cosmos.distribution.v1beta1.createRpcQueryExtension(await this.getQueryClient()).validatorOutstandingRewards({validatorAddress})
|
||||
}
|
||||
async validatorSlashes(validatorAddress: string, start: number, end: number) {
|
||||
const startingHeight = long.fromNumber(start)
|
||||
const endingHeight = long.fromNumber(end)
|
||||
return cosmos.distribution.v1beta1.createRpcQueryExtension(await this.getQueryClient()).validatorSlashes({validatorAddress, startingHeight, endingHeight})
|
||||
}
|
||||
async delegationRewards(delegatorAddress: string, validatorAddress: string) {
|
||||
return cosmos.distribution.v1beta1.createRpcQueryExtension(await this.getQueryClient()).delegationRewards({delegatorAddress, validatorAddress})
|
||||
}
|
||||
async stakingPool() {
|
||||
return cosmos.staking.v1beta1.createRpcQueryExtension(await this.getQueryClient()).pool()
|
||||
}
|
||||
async validator(validatorAddr: string) {
|
||||
return cosmos.staking.v1beta1.createRpcQueryExtension(await this.getQueryClient()).validator({validatorAddr})
|
||||
}
|
||||
async validators(status: BondStatusString, key?: Uint8Array) {
|
||||
return cosmos.staking.v1beta1.createRpcQueryExtension(await this.getQueryClient()).validators({status})
|
||||
}
|
||||
async stakingParams() {
|
||||
return cosmos.staking.v1beta1.createRpcQueryExtension(await this.getQueryClient()).params()
|
||||
}
|
||||
async historicalInfo(height: number) {
|
||||
return cosmos.staking.v1beta1.createRpcQueryExtension(await this.getQueryClient()).historicalInfo({height: long.fromNumber(height)})
|
||||
}
|
||||
async mintParams() {
|
||||
return cosmos.mint.v1beta1.createRpcQueryExtension(await this.getQueryClient()).params()
|
||||
}
|
||||
async inflation() {
|
||||
return cosmos.mint.v1beta1.createRpcQueryExtension(await this.getQueryClient()).inflation()
|
||||
}
|
||||
|
||||
|
||||
}
|
4
packages/dashboard/src/libs/index.ts
Normal file
4
packages/dashboard/src/libs/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './address'
|
||||
export * from './client'
|
||||
export * from './http'
|
||||
export * from './misc'
|
13
packages/dashboard/src/libs/misc.ts
Normal file
13
packages/dashboard/src/libs/misc.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { PageRequest } from "@ping-pub/codegen/src/cosmos/base/query/v1beta1/pagination";
|
||||
|
||||
export function newPageRequest(param: {
|
||||
key?: Uint8Array,
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
countTotal?: boolean,
|
||||
reverse?: boolean
|
||||
}) {
|
||||
return PageRequest.fromPartial(
|
||||
param
|
||||
)
|
||||
}
|
@ -17,7 +17,6 @@ loadFonts();
|
||||
|
||||
// Create vue app
|
||||
const app = createApp(App);
|
||||
|
||||
// Use plugins
|
||||
app.use(i18n)
|
||||
app.use(vuetify);
|
||||
|
@ -23,14 +23,11 @@ onMounted(() => {
|
||||
|
||||
const format = useFormatter()
|
||||
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex])
|
||||
const desc = ref('')
|
||||
const detailId = ref('')
|
||||
|
||||
store.$subscribe((m, s) => {
|
||||
desc.value = s.coinInfo.description?.en || ''
|
||||
})
|
||||
blockchain.$subscribe((m, s) => {
|
||||
if(m.events.key === 'rest') {
|
||||
console.log('index:', m)
|
||||
if(!Array.isArray(m.events) && ['chainName', 'endpoint'].includes(m.events.key)) {
|
||||
console.log(m.events.key)
|
||||
store.loadDashboard()
|
||||
}
|
||||
})
|
||||
@ -44,7 +41,7 @@ function shortName(name: string, id: string) {
|
||||
<div>
|
||||
<VCard v-if="coinInfo && coinInfo.name" class="mb-5">
|
||||
<VRow>
|
||||
<VCol md="4">
|
||||
<VCol md="5">
|
||||
<VCardItem>
|
||||
<VCardTitle>
|
||||
{{ coinInfo.name }} (<span class="text-uppercase">{{ coinInfo.symbol }}</span>)
|
||||
@ -120,7 +117,7 @@ function shortName(name: string, id: string) {
|
||||
</VBtn>
|
||||
</VCardItem>
|
||||
</VCol>
|
||||
<VCol md="8">
|
||||
<VCol md="7">
|
||||
<VCardItem>
|
||||
<PriceMarketChart />
|
||||
</VCardItem>
|
||||
@ -129,7 +126,7 @@ function shortName(name: string, id: string) {
|
||||
<VDivider />
|
||||
<VCardText style="max-height: 250px; overflow:scroll;"><MdEditor :model-value="coinInfo.description?.en" previewOnly></MdEditor></VCardText>
|
||||
<VCardItem>
|
||||
<VChip v-for="tag in coinInfo.categories" size="x-small">{{ tag }}</VChip>
|
||||
<VChip v-for="tag in coinInfo.categories" size="x-small" class="mr-2">{{ tag }}</VChip>
|
||||
</VCardItem>
|
||||
</VCard>
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { useBlockchain, useCoingecko, useBaseStore, useBankStore, useFormatter,
|
||||
import { useDistributionStore } from "@/stores/useDistributionStore";
|
||||
import { useMintStore } from "@/stores/useMintStore";
|
||||
import { useStakingStore } from "@/stores/useStakingStore";
|
||||
import { ProposalStatus, type ProposalSDKType } from "@ping-pub/codegen/src/cosmos/gov/v1beta1/gov";
|
||||
import { ProposalStatus, type ProposalSDKType, Proposal } from "@ping-pub/codegen/src/cosmos/gov/v1beta1/gov";
|
||||
import numeral from "numeral";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
@ -65,12 +65,12 @@ export const useIndexModule = defineStore('module-index', {
|
||||
total_volumes: [] as number[],
|
||||
},
|
||||
communityPool: [] as {amount: string, denom: string}[],
|
||||
proposals: [] as ProposalSDKType[],
|
||||
proposals: [] as Proposal[],
|
||||
tally: {} as Record<number, {
|
||||
yes: string;
|
||||
abstain: string;
|
||||
no: string;
|
||||
no_with_veto: string;
|
||||
noWithVeto: string;
|
||||
}>
|
||||
}
|
||||
},
|
||||
@ -102,7 +102,6 @@ export const useIndexModule = defineStore('module-index', {
|
||||
|
||||
priceChange(): string {
|
||||
const change = this.coinInfo.market_data?.price_change_percentage_24h || 0
|
||||
console.log(change, 'change')
|
||||
return numeral(change).format('+0.[00]')
|
||||
},
|
||||
|
||||
@ -122,10 +121,6 @@ export const useIndexModule = defineStore('module-index', {
|
||||
return colorMap(change)
|
||||
},
|
||||
|
||||
mintStore() {
|
||||
return useMintStore()
|
||||
},
|
||||
|
||||
pool() {
|
||||
const staking = useStakingStore()
|
||||
return staking.pool
|
||||
@ -135,7 +130,9 @@ export const useIndexModule = defineStore('module-index', {
|
||||
const base = useBaseStore()
|
||||
const bank = useBankStore()
|
||||
const staking = useStakingStore()
|
||||
const mintStore = useMintStore()
|
||||
const formatter = useFormatter()
|
||||
|
||||
return [
|
||||
{
|
||||
title: 'Height',
|
||||
@ -148,7 +145,7 @@ export const useIndexModule = defineStore('module-index', {
|
||||
title: 'Validators',
|
||||
color: 'error',
|
||||
icon: 'mdi-human-queue',
|
||||
stats: String(base.latest.block?.last_commit?.signatures.length || 0),
|
||||
stats: String(base.latest.block?.lastCommit?.signatures.length || 0),
|
||||
change: 0,
|
||||
},
|
||||
{
|
||||
@ -162,14 +159,14 @@ export const useIndexModule = defineStore('module-index', {
|
||||
title: 'Bonded Tokens',
|
||||
color: 'warning',
|
||||
icon: 'mdi-lock',
|
||||
stats: formatter.formatTokenAmount({amount: this.pool.bonded_tokens, denom: staking.params.bond_denom }),
|
||||
stats: formatter.formatTokenAmount({amount: this.pool.bondedTokens, denom: staking.params.bondDenom }),
|
||||
change: 0,
|
||||
},
|
||||
{
|
||||
title: 'Inflation',
|
||||
color: 'success',
|
||||
icon: 'mdi-chart-multiple',
|
||||
stats: formatter.formatDecimalToPercent(this.mintStore.inflation),
|
||||
stats: formatter.formatDecimalToPercent(mintStore.inflation),
|
||||
change: 0,
|
||||
},
|
||||
{
|
||||
@ -184,25 +181,25 @@ export const useIndexModule = defineStore('module-index', {
|
||||
},
|
||||
actions: {
|
||||
async loadDashboard() {
|
||||
console.log('initial dashboard')
|
||||
this.$reset()
|
||||
this.initCoingecko()
|
||||
this.mintStore.fetchInflation()
|
||||
const dist = useDistributionStore()
|
||||
dist.fetchCommunityPool().then(x => {
|
||||
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(ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD).then(x => {
|
||||
this.proposals = x.proposals
|
||||
x.proposals.forEach(x1 => {
|
||||
gov.fetchTally(x1.proposal_id).then(t => {
|
||||
if(t.tally) this.tally[Number(x1.proposal_id)] = t.tally
|
||||
})
|
||||
})
|
||||
})
|
||||
// const gov = useGovStore()
|
||||
// gov.fetchProposals(ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD).then(x => {
|
||||
// this.proposals = x.proposals
|
||||
// x.proposals.forEach(x1 => {
|
||||
// gov.fetchTally(Number(x1.proposalId)).then(t => {
|
||||
// if(t.tally) this.tally[Number(x1.proposalId)] = t.tally
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
},
|
||||
tickerColor(color: string) {
|
||||
return colorMap(color)
|
||||
|
@ -0,0 +1,55 @@
|
||||
<script setup lang="ts">
|
||||
import { useStakingStore } from '@/stores';
|
||||
import type { QueryValidatorResponseSDKType } from '@ping-pub/codegen/src/cosmos/staking/v1beta1/query';
|
||||
import { onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const route = useRoute()
|
||||
const staking = useStakingStore()
|
||||
const { validator } = route.params
|
||||
const v = ref({} as QueryValidatorResponseSDKType)
|
||||
const cache = JSON.parse(localStorage.getItem('avatars')||'{}')
|
||||
const avatars = ref( cache || {} )
|
||||
const identity = ref("")
|
||||
|
||||
onMounted(()=> {
|
||||
if(validator) {
|
||||
staking.fetchValidator(validator.toString()).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))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<VCard class="card-box">
|
||||
<VCardItem>
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<VAvatar icon="mdi-help-circle-outline" :image="avatars[identity]" size="80" rounded variant="outlined" color="secondary"/>
|
||||
<VList>
|
||||
<VListItem>
|
||||
<VListItemTitle>{{ v.description?.moniker }}</VListItemTitle>
|
||||
<VListItemSubtitle> {{ v.description?.website || v.description?.identity || '-'}}</VListItemsubtitle>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">2</VCol>
|
||||
</VRow>
|
||||
</VCardItem>
|
||||
</VCard>
|
||||
</template>
|
226
packages/dashboard/src/modules/[chain]/staking/index.vue
Normal file
226
packages/dashboard/src/modules/[chain]/staking/index.vue
Normal file
@ -0,0 +1,226 @@
|
||||
<script lang=ts setup>
|
||||
import { useBaseStore, useFormatter, useStakingStore } from '@/stores';
|
||||
import type { ValidatorSDKType } from '@ping-pub/codegen/src/cosmos/staking/v1beta1/staking';
|
||||
import { computed } from '@vue/reactivity';
|
||||
import { onMounted, ref, type DebuggerEvent } from 'vue';
|
||||
|
||||
const staking = useStakingStore()
|
||||
const format = useFormatter()
|
||||
|
||||
const cache = JSON.parse(localStorage.getItem('avatars')||'{}')
|
||||
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([] as ValidatorSDKType[])
|
||||
|
||||
onMounted(()=> {
|
||||
fetchChange(0)
|
||||
staking.fetchInacitveValdiators().then(x => {
|
||||
unbondList.value = x
|
||||
})
|
||||
})
|
||||
|
||||
function fetchChange(offset: number) {
|
||||
const base = useBaseStore()
|
||||
const diff = 86400000 / base.blocktime
|
||||
base.fetchLatestValidators(offset).then(x => {
|
||||
const height = Number(x.block_height) - diff
|
||||
x.validators.forEach(v => {
|
||||
latest.value[v.pub_key?.key] = Number(v.voting_power)
|
||||
})
|
||||
const len = Object.keys(latest.value).length
|
||||
if(x.pagination && len < Number(x.pagination.total) ) {
|
||||
fetchChange(len)
|
||||
}
|
||||
base.fetchValidatorByHeight(height > 0 ? height : 1, offset).then(old => {
|
||||
old.validators.forEach(v => {
|
||||
yesterday.value[v.pub_key?.key] = Number(v.voting_power)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const change24 = (key: string) => {
|
||||
const n : number = latest.value[key];
|
||||
const o : number = yesterday.value[key]
|
||||
return n >0 && o > 0 ? n - o : 0
|
||||
}
|
||||
|
||||
const change24Text = (key?: string) => {
|
||||
if(!key) return ''
|
||||
const v = change24(key)
|
||||
return v!==0 ? format.numberAndSign(v) : ''
|
||||
}
|
||||
|
||||
const change24Color = (key?: string) => {
|
||||
if(!key) return ''
|
||||
const v = change24(key)
|
||||
if(v > 0) return 'text-success'
|
||||
if(v < 0) return 'text-error'
|
||||
}
|
||||
|
||||
const update = (m: DebuggerEvent) => {
|
||||
if(m.key === 'validators') {
|
||||
loadAvatars()
|
||||
}
|
||||
}
|
||||
|
||||
const list = computed(() => {
|
||||
return tab.value === 'active' ? staking.validators: unbondList.value
|
||||
})
|
||||
|
||||
const loadAvatars = () => {
|
||||
// fetch avatar from keybase
|
||||
let promise = Promise.resolve()
|
||||
staking.validators.forEach(item => {
|
||||
promise = promise.then(() => new Promise(resolve => {
|
||||
const identity = item.description?.identity
|
||||
if(identity && !avatars.value[identity]){
|
||||
staking.keybase(identity).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] = uri
|
||||
localStorage.setItem('avatars', JSON.stringify(avatars.value))
|
||||
}
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
}else{
|
||||
resolve()
|
||||
}
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
staking.$subscribe((m, s)=> {
|
||||
if (Array.isArray(m.events)) {
|
||||
m.events.forEach(x => {
|
||||
update(x)
|
||||
})
|
||||
} else {
|
||||
update(m.events)
|
||||
}
|
||||
})
|
||||
const logo = (identity?: string) => {
|
||||
if(!identity) return ''
|
||||
const url = avatars.value[identity] || ''
|
||||
return url.startsWith('http')? url: `https://s3.amazonaws.com/keybase_processed_uploads/${url}`
|
||||
}
|
||||
const rank = function(position: number) {
|
||||
let sum = 0
|
||||
for(let i = 0;i < position; i++) {
|
||||
sum += Number(staking.validators[i]?.delegator_shares)
|
||||
}
|
||||
const percent = (sum / staking.totalPower)
|
||||
|
||||
switch (true) {
|
||||
case tab.value ==='active' && percent < 0.33: return 'error'
|
||||
case tab.value ==='active' && percent < 0.67: return 'warning'
|
||||
default: return 'primary'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<VCard>
|
||||
<VCardTitle class="d-flex justify-space-between">
|
||||
<VBtnToggle v-model="tab" size="small" color="primary">
|
||||
<VBtn value="active" variant="outlined" >Active</VBtn>
|
||||
<VBtn value="inactive" variant="outlined">Inactive</VBtn>
|
||||
</VBtnToggle>
|
||||
<span class="mt-2">{{ list.length }}/{{ staking.params.max_validators }}</span>
|
||||
</VCardTitle>
|
||||
<VTable class="text-no-wrap table-header-bg rounded-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
style="width: 3rem;"
|
||||
>#</th>
|
||||
<th scope="col">
|
||||
VALIDATOR
|
||||
</th>
|
||||
<th scope="col" class="text-right">
|
||||
VOTING POWER
|
||||
</th>
|
||||
<th scope="col" class="text-right">
|
||||
24h CHANGES
|
||||
</th>
|
||||
<th scope="col" class="text-right">
|
||||
COMMISSION
|
||||
</th>
|
||||
<th scope="col">
|
||||
ACTIONS
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(v, i) in list"
|
||||
:key="v.operator_address"
|
||||
>
|
||||
<!-- 👉 rank -->
|
||||
<td>
|
||||
<VChip label :color="rank(i)">
|
||||
{{ i + 1 }}
|
||||
</VChip>
|
||||
</td>
|
||||
|
||||
<!-- 👉 Validator -->
|
||||
<td>
|
||||
<div class="d-flex align-center overflow-hidden" style="max-width: 400px;">
|
||||
<VAvatar
|
||||
variant="tonal"
|
||||
class="me-3"
|
||||
size="34"
|
||||
icon="mdi-help-circle-outline"
|
||||
:image="logo(v.description?.identity)"
|
||||
/>
|
||||
<div class="d-flex flex-column">
|
||||
<h6 class="text-sm">
|
||||
<RouterLink
|
||||
:to="{name: 'chain-staking-validator', params: {validator: v.operator_address}}"
|
||||
class="font-weight-medium user-list-name"
|
||||
>
|
||||
{{ v.description?.moniker }}
|
||||
</RouterLink>
|
||||
|
||||
</h6>
|
||||
<span class="text-xs">{{ v.description?.website || v.description?.identity || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- 👉 Voting Power -->
|
||||
<td class="text-right">
|
||||
<div class="d-flex flex-column">
|
||||
<h6 class="text-sm font-weight-medium">
|
||||
{{ format.formatToken( {amount: parseInt(v.delegator_shares).toString(), denom: staking.params.bond_denom }, true, "0,0") }}
|
||||
</h6>
|
||||
<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.consensus_pubkey?.key)">
|
||||
{{ change24Text(v.consensus_pubkey?.key) }} <VChip label v-if="v.jailed" color="error">Jailed</VChip>
|
||||
</td>
|
||||
<!-- 👉 commission -->
|
||||
<td class="text-right">
|
||||
{{ format.percent(v.commission?.commission_rates?.rate) }}
|
||||
</td>
|
||||
<!-- 👉 Action -->
|
||||
<td>
|
||||
{{ 2 }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</VTable>
|
||||
<VDivider/>
|
||||
<VCardActions class="py-2">
|
||||
<VChip label color="error">Top 33%</VChip> <VChip label color="warning" class="mx-2">Top 67%</VChip>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
</div>
|
||||
</template>
|
@ -4,6 +4,9 @@ import ChainSummary from '@/components/ChainSummary.vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useBlockchain } from '@/stores';
|
||||
|
||||
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
|
||||
import { QueryClient, setupBankExtension, setupDistributionExtension, setupGovExtension, setupMintExtension, setupStakingExtension } from "@cosmjs/stargate";
|
||||
|
||||
const dashboard = useDashboard()
|
||||
|
||||
dashboard.$subscribe((mutation, state) => {
|
||||
@ -18,6 +21,7 @@ const chains = computed(()=> {
|
||||
}
|
||||
})
|
||||
const chain = useBlockchain()
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="d-flex flex-column justify-center">
|
||||
|
11
packages/dashboard/src/plugins/pinia/ClientProperties.ts
Normal file
11
packages/dashboard/src/plugins/pinia/ClientProperties.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { RPCClient } from '@/libs/client.rpc'
|
||||
import 'pinia'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
declare module 'pinia' {
|
||||
export interface PiniaCustomProperties {
|
||||
// by using a setter we can allow both strings and refs
|
||||
set rpc(value: RPCClient | Ref<RPCClient>)
|
||||
get rpc(): RPCClient
|
||||
}
|
||||
}
|
10
packages/dashboard/src/plugins/pinia/dashboardPlugin.ts
Normal file
10
packages/dashboard/src/plugins/pinia/dashboardPlugin.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { PiniaPluginContext } from "pinia"
|
||||
|
||||
export function DashboardPlugin(context: PiniaPluginContext) {
|
||||
context.pinia // the pinia created with `createPinia()`
|
||||
context.app // the current app created with `createApp()` (Vue 3 only)
|
||||
context.store // the store the plugin is augmenting
|
||||
context.options // the options object defining the store passed to `defineStore()`
|
||||
// ...
|
||||
context.store.$subscribe((m, s) => console.log(m, s))
|
||||
}
|
@ -1 +1,4 @@
|
||||
// Write your overrides
|
||||
.card-box {
|
||||
border: 1px solid rgb(var(--v-theme-primary));
|
||||
}
|
@ -1,65 +1,48 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
import { useBlockchain } from "./useBlockchain";
|
||||
import { createBankClientForChain } from "@/libs/client";
|
||||
import { QuerySupplyOfRequest, type QueryTotalSupplyRequest, type QueryTotalSupplyResponseSDKType } from "@ping-pub/codegen/src/cosmos/bank/v1beta1/query";
|
||||
import type { CoinSDKType } from "@ping-pub/codegen/src/cosmos/base/v1beta1/coin";
|
||||
import type { QueryTotalSupplyResponse, QueryTotalSupplyRequest } from "@ping-pub/codegen/src/cosmos/bank/v1beta1/query";
|
||||
import type { Coin } from "@ping-pub/codegen/src/cosmos/base/v1beta1/coin";
|
||||
import { useStakingStore } from "./useStakingStore";
|
||||
import { createRpcQueryExtension } from '@ping-pub/codegen/src/cosmos/bank/v1beta1/query.rpc.Query'
|
||||
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
|
||||
import { QueryClient } from "@cosmjs/stargate";
|
||||
|
||||
export const useBankStore = defineStore('bankstore', {
|
||||
state: () => {
|
||||
return {
|
||||
supply: {} as CoinSDKType,
|
||||
balances: {} as Record<string, CoinSDKType[]>,
|
||||
totalSupply: {supply: []} as QueryTotalSupplyResponseSDKType,
|
||||
supply: {} as Coin,
|
||||
balances: {} as Record<string, Coin[]>,
|
||||
totalSupply: {supply: []} as QueryTotalSupplyResponse,
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
blockchain() {
|
||||
return useBlockchain()
|
||||
},
|
||||
client() {
|
||||
const chain = useBlockchain()
|
||||
return createBankClientForChain(chain.chainName, chain.restClient)
|
||||
},
|
||||
staking() {
|
||||
return useStakingStore()
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
initial() {
|
||||
this.supply = {} as CoinSDKType
|
||||
const denom = this.staking.params.bond_denom || this.blockchain.current.assets[0].base
|
||||
this.fetchSupply(denom).then(res => {
|
||||
this.$reset()
|
||||
this.supply = {} as Coin
|
||||
const denom = this.staking.params.bondDenom || this.blockchain.current?.assets[0].base
|
||||
if(denom) {
|
||||
this.blockchain.rpc.supplyOf(denom).then(res => {
|
||||
if(res.amount) this.supply = res.amount
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// cacheBalance(address: string, balances: CoinSDKType[]) {
|
||||
// if(this.balances[address]) {
|
||||
// this.balances[address] = [...this.balances[address], ... balances]
|
||||
// }else {
|
||||
// this.balances[address] = balances
|
||||
// }
|
||||
// },
|
||||
// async fetchBalance(param: QueryBalanceRequest) : Promise<QueryBalanceResponseSDKType> {
|
||||
// const response : QueryBalanceResponseSDKType = await this.lcdClient.balance(param)
|
||||
// if (response.balance) this.cacheBalance(param.address, [response.balance])
|
||||
// async fetchTotalSupply(param: QueryTotalSupplyRequest): Promise<QueryTotalSupplyResponse> {
|
||||
// const response = await this.blockchain.rpc.(param)
|
||||
// this.totalSupply.supply = [...this.totalSupply.supply, ...response.supply]
|
||||
// this.totalSupply.pagination = response.pagination
|
||||
// return response
|
||||
// },
|
||||
// async fetchAllBalance(param: QueryAllBalancesRequest) : Promise<QueryAllBalancesResponseSDKType> {
|
||||
// const response : QueryAllBalancesResponseSDKType = await this.lcdClient.allBalances(param)
|
||||
// if (response.balances) this.cacheBalance(param.address, response.balances)
|
||||
// return response
|
||||
// },
|
||||
async fetchTotalSupply(param: QueryTotalSupplyRequest): Promise<QueryTotalSupplyResponseSDKType> {
|
||||
const response = await this.client.totalSupply(param)
|
||||
this.totalSupply.supply = [...this.totalSupply.supply, ...response.supply]
|
||||
this.totalSupply.pagination = response.pagination
|
||||
return response
|
||||
},
|
||||
async fetchSupply(denom: string) {
|
||||
return this.client.supplyOf( { denom } )
|
||||
return this.blockchain.rpc.supplyOf( denom )
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,19 +1,35 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { createBaseClientForChain } from "@/libs/client";
|
||||
import { useBlockchain } from "@/stores";
|
||||
import type { GetLatestBlockResponseSDKType } from "@ping-pub/codegen/src/cosmos/base/tendermint/v1beta1/query";
|
||||
import dayjs from "dayjs";
|
||||
import long from "long";
|
||||
import { PageRequest } from "@ping-pub/codegen/src/cosmos/base/query/v1beta1/pagination";
|
||||
import { newPageRequest } from "@/libs";
|
||||
|
||||
import { createRpcQueryExtension } from '@ping-pub/codegen/src/cosmos/base/tendermint/v1beta1/query.rpc.Service'
|
||||
import { Tendermint34Client, type BlockResponse } from "@cosmjs/tendermint-rpc";
|
||||
import { QueryClient, createProtobufRpcClient, setupBankExtension, Setup } from "@cosmjs/stargate";
|
||||
|
||||
export const useBaseStore = defineStore('baseStore', {
|
||||
state: () => {
|
||||
return {
|
||||
latest: {} as GetLatestBlockResponseSDKType,
|
||||
recents: [] as GetLatestBlockResponseSDKType[]
|
||||
earlest: {} as BlockResponse,
|
||||
latest: {} as BlockResponse,
|
||||
recents: [] as BlockResponse[]
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
client() {
|
||||
const chain = useBlockchain()
|
||||
return createBaseClientForChain(chain.chainName, chain.restClient)
|
||||
blocktime(): number {
|
||||
if(this.earlest && this.latest) {
|
||||
if(this.latest.block?.header?.height !== this.earlest.block?.header?.height) {
|
||||
const diff = dayjs(this.latest.block?.header?.time).diff(this.earlest.block?.header?.time)
|
||||
return diff
|
||||
}
|
||||
}
|
||||
return 6000
|
||||
},
|
||||
blockchain() {
|
||||
return useBlockchain()
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@ -24,18 +40,30 @@ export const useBaseStore = defineStore('baseStore', {
|
||||
this.recents = []
|
||||
},
|
||||
async fetchLatest() {
|
||||
this.latest = await this.client.getLatestBlock()
|
||||
this.latest = await this.blockchain.rpc.block()
|
||||
if(!this.earlest || this.earlest.block?.header?.chainId != this.latest.block?.header?.chainId) {
|
||||
//reset earlest and recents
|
||||
this.earlest = this.latest
|
||||
this.recents = []
|
||||
}
|
||||
if(this.recents.length>= 50) {
|
||||
this.recents.pop()
|
||||
}
|
||||
this.recents.push(this.latest)
|
||||
return this.latest
|
||||
},
|
||||
async fetchSync() {
|
||||
return this.client.getSyncing()
|
||||
|
||||
async fetchValidatorByHeight(height?: number, offset = 0) {
|
||||
return this.blockchain.rpc.validatorsAtHeight(height)
|
||||
},
|
||||
async fetchNodeInfo() {
|
||||
return this.client.getNodeInfo()
|
||||
}
|
||||
async fetchLatestValidators(offset = 0) {
|
||||
return this.blockchain.rpc.validatorsAtHeight()
|
||||
},
|
||||
async fetchBlock(height: number) {
|
||||
return this.blockchain.rpc.block(height)
|
||||
},
|
||||
// async fetchNodeInfo() {
|
||||
// return this.blockchain.rpc.no()
|
||||
// }
|
||||
}
|
||||
})
|
@ -1,5 +1,5 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { useDashboard, type ChainConfig } from "./useDashboard";
|
||||
import { useDashboard, type ChainConfig, type Endpoint, EndpointType } from "./useDashboard";
|
||||
import { LCDClient } from '@osmonauts/lcd'
|
||||
import type { VerticalNavItems } from '@/@layouts/types'
|
||||
import { useRouter } from "vue-router";
|
||||
@ -7,36 +7,30 @@ import { useStakingStore } from "./useStakingStore";
|
||||
import { useBankStore } from "./useBankStore";
|
||||
import { useBaseStore } from "./useBaseStore";
|
||||
import { useGovStore } from "./useGovStore";
|
||||
import { RPCClient } from '../libs/client.rpc'
|
||||
import { ref } from "vue";
|
||||
|
||||
export const useBlockchain = defineStore("blockchain", {
|
||||
state: () => {
|
||||
return {
|
||||
status: {} as Record<string, string>,
|
||||
rest: '',
|
||||
chainName: ""
|
||||
chainName: "",
|
||||
endpoint: {} as {
|
||||
type?: EndpointType,
|
||||
address: string
|
||||
provider: string
|
||||
},
|
||||
connErr: ""
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
current() : ChainConfig {
|
||||
current() : ChainConfig | undefined {
|
||||
return this.dashboard.chains[this.chainName]
|
||||
},
|
||||
logo(): string {
|
||||
return this.current?.logo || ''
|
||||
},
|
||||
availableEndpoint() : string {
|
||||
const all = this.current?.endpoints?.rest
|
||||
if(all) {
|
||||
if(this.rest || all.findIndex(x => x.address === this.rest) < 0) {
|
||||
const rn = Math.random()
|
||||
const endpoint = all[Math.floor(rn * all.length)]
|
||||
this.rest = endpoint?.address || ''
|
||||
}
|
||||
}
|
||||
return this.rest
|
||||
},
|
||||
restClient() : LCDClient {
|
||||
return new LCDClient({restEndpoint: this.rest})
|
||||
},
|
||||
dashboard() {
|
||||
return useDashboard()
|
||||
},
|
||||
@ -46,6 +40,7 @@ export const useBlockchain = defineStore("blockchain", {
|
||||
|
||||
const router = useRouter()
|
||||
const routes = router?.getRoutes()||[]
|
||||
console.log(routes)
|
||||
if(this.current && routes) {
|
||||
currNavItem = [{
|
||||
title: this.current?.prettyName || this.chainName || '',
|
||||
@ -99,16 +94,35 @@ export const useBlockchain = defineStore("blockchain", {
|
||||
},
|
||||
actions: {
|
||||
async initial() {
|
||||
console.log('begin Setup')
|
||||
await this.randomSetupEndpoint()
|
||||
console.log('rpc setup')
|
||||
await useStakingStore().init()
|
||||
await useBankStore().initial()
|
||||
useBankStore().initial()
|
||||
useBaseStore().initial()
|
||||
useGovStore().initial()
|
||||
|
||||
},
|
||||
setRestEndpoint(endpoint: string) {
|
||||
this.rest = endpoint
|
||||
|
||||
async randomSetupEndpoint() {
|
||||
const all = this.current?.endpoints?.rpc
|
||||
if(all) {
|
||||
const rn = Math.random()
|
||||
const endpoint = all[Math.floor(rn * all.length)]
|
||||
await this.setRestEndpoint(endpoint)
|
||||
}
|
||||
},
|
||||
|
||||
async setRestEndpoint(endpoint: Endpoint) {
|
||||
this.connErr = ''
|
||||
this.endpoint = endpoint
|
||||
this.rpc = new RPCClient(endpoint.address)
|
||||
console.log(this.rpc.endpoint)
|
||||
},
|
||||
setCurrent(name: string) {
|
||||
this.chainName = name
|
||||
}
|
||||
console.log('set current', name)
|
||||
},
|
||||
|
||||
}
|
||||
})
|
||||
|
@ -209,10 +209,6 @@ export const useDashboard = defineStore('dashboard', {
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
current() : string {
|
||||
const blockchain = useBlockchain()
|
||||
return blockchain.chainName || this.favorite[0] || ''
|
||||
},
|
||||
length() : number {
|
||||
return Object.keys(this.chains).length
|
||||
}
|
||||
@ -234,14 +230,29 @@ export const useDashboard = defineStore('dashboard', {
|
||||
}
|
||||
},
|
||||
async loadingFromLocal() {
|
||||
const source = this.networkType === NetworkType.Mainnet
|
||||
const source: Record<string, LocalConfig> = this.networkType === NetworkType.Mainnet
|
||||
? import.meta.glob('../../chains/mainnet/*.json', {eager: true})
|
||||
: import.meta.glob('../../chains/testnet/*.json', {eager: true})
|
||||
Object.values(source).forEach((x: LocalConfig) => {
|
||||
Object.values<LocalConfig>(source).forEach((x: LocalConfig) => {
|
||||
this.chains[x.chain_name] = fromLocal(x)
|
||||
})
|
||||
this.setupDefault()
|
||||
this.status = LoadingStatus.Loaded
|
||||
},
|
||||
setupDefault() {
|
||||
if(this.length > 0) {
|
||||
const blockchain = useBlockchain()
|
||||
for(let i=0; i < this.favorite.length; i++) {
|
||||
if(!blockchain.chainName && this.chains[this.favorite[i]]) {
|
||||
blockchain.setCurrent(this.favorite[i])
|
||||
}
|
||||
}
|
||||
if(!blockchain.chainName) {
|
||||
const [first] = Object.keys(this.chains)
|
||||
blockchain.setCurrent(first)
|
||||
}
|
||||
}
|
||||
},
|
||||
setConfigSource(newSource: ConfigSource) {
|
||||
this.source = newSource
|
||||
this.initial()
|
||||
|
@ -2,20 +2,23 @@ import { defineStore } from "pinia";
|
||||
import { useBlockchain } from "./useBlockchain";
|
||||
import { createDistributionClientForChain } from "@/libs/client";
|
||||
|
||||
import { createRpcQueryExtension } from '@ping-pub/codegen/src/cosmos/distribution/v1beta1/query.rpc.Query'
|
||||
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
|
||||
import { QueryClient } from "@cosmjs/stargate";
|
||||
|
||||
export const useDistributionStore = defineStore('distributionStore', {
|
||||
state: () => {
|
||||
return {
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
client() {
|
||||
const chain = useBlockchain()
|
||||
return createDistributionClientForChain(chain.chainName, chain.restClient)
|
||||
blockchain() {
|
||||
return useBlockchain()
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
fetchCommunityPool() {
|
||||
return this.client.communityPool()
|
||||
async fetchCommunityPool() {
|
||||
return this.blockchain.rpc.communityPool()
|
||||
}
|
||||
}
|
||||
})
|
@ -47,9 +47,12 @@ export const useFormatter = defineStore('formatter', {
|
||||
formatTokenAmount(token: {denom: string, amount: string;}) {
|
||||
return this.formatToken(token, false)
|
||||
},
|
||||
formatToken(token: { denom: string, amount: string;}, withDenom = true) : string {
|
||||
formatToken2(token: { denom: string, amount: string;}, withDenom = true) {
|
||||
return this.formatToken(token, true, '0,0.[00]')
|
||||
},
|
||||
formatToken(token: { denom: string, amount: string;}, withDenom = true, fmt='0.0a') : string {
|
||||
if(token && token.amount) {
|
||||
let amount = Long.fromValue(token.amount)
|
||||
let amount = Number(token.amount)
|
||||
let denom = token.denom
|
||||
const conf = this.blockchain.current?.assets?.find(x => x.base === token.denom || x.base.denom === token.denom)
|
||||
if(conf) {
|
||||
@ -61,11 +64,11 @@ export const useFormatter = defineStore('formatter', {
|
||||
}
|
||||
})
|
||||
if(unit && unit.exponent > 0) {
|
||||
amount = Long.fromValue(token.amount).divide(Math.pow(10, unit?.exponent))
|
||||
amount = amount / Math.pow(10, unit?.exponent)
|
||||
denom = unit.denom.toUpperCase()
|
||||
}
|
||||
}
|
||||
return `${numeral(amount).format('0.0a')} ${withDenom ? denom: ''}`
|
||||
return `${numeral(amount).format(fmt)} ${withDenom ? denom: ''}`
|
||||
}
|
||||
return '-'
|
||||
},
|
||||
@ -82,7 +85,7 @@ export const useFormatter = defineStore('formatter', {
|
||||
}
|
||||
return '-'
|
||||
},
|
||||
calculatePercent(input?: string, total?: string ) {
|
||||
calculatePercent(input?: string, total?: string|number ) {
|
||||
if(!input || !total) return '0'
|
||||
const percent = Number(input)/Number(total)
|
||||
return numeral(percent).format("0.[00]%")
|
||||
@ -90,8 +93,11 @@ export const useFormatter = defineStore('formatter', {
|
||||
formatDecimalToPercent(decimal: string) {
|
||||
return numeral(decimal).format('0.[00]%')
|
||||
},
|
||||
formatDateTo(date: string) {
|
||||
return dayjs(date).to
|
||||
percent(decimal?: string) {
|
||||
return decimal ? numeral(decimal).format('0.[00]%') : '-'
|
||||
},
|
||||
numberAndSign(input: number, fmt="+0,0") {
|
||||
return numeral(input).format(fmt)
|
||||
},
|
||||
toDay(time?: string, format = 'long') {
|
||||
if(!time) return ''
|
||||
|
@ -1,52 +1,47 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { useBlockchain } from "./useBlockchain";
|
||||
import { createGovRestClientForChain } from "@/libs/client";
|
||||
import type { ProposalStatus } from "@ping-pub/codegen/src/cosmos/gov/v1/gov";
|
||||
import type { DepositParams, ProposalStatus } from "@ping-pub/codegen/src/cosmos/gov/v1/gov";
|
||||
import type { PageRequest } from "@ping-pub/codegen/src/helpers";
|
||||
import type { DepositParams, DepositParamsSDKType, TallyParams, TallyParamsSDKType, VotingParams, VotingParamsSDKType } from "@ping-pub/codegen/src/cosmos/gov/v1beta1/gov";
|
||||
import { createRpcQueryExtension } from '@ping-pub/codegen/src/cosmos/gov/v1beta1/query.rpc.Query'
|
||||
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
|
||||
import { QueryClient } from "@cosmjs/stargate";
|
||||
|
||||
export const useGovStore = defineStore('govStore', {
|
||||
state: () => {
|
||||
return {
|
||||
params: {
|
||||
deposit: {} as DepositParamsSDKType,
|
||||
voting: {} as VotingParamsSDKType,
|
||||
tally: {} as TallyParamsSDKType,
|
||||
deposit: {} as DepositParams,
|
||||
voting: {} as VotingParams,
|
||||
tally: {} as TallyParams,
|
||||
}
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
client() {
|
||||
const chain = useBlockchain()
|
||||
return createGovRestClientForChain(chain.chainName, chain.restClient)
|
||||
blockchain() {
|
||||
return useBlockchain()
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
initial() {
|
||||
this.fetchParams()
|
||||
},
|
||||
fetchProposals( proposalStatus: ProposalStatus, pagination?: PageRequest ) {
|
||||
async fetchProposals( proposalStatus: ProposalStatus, pagination?: PageRequest ) {
|
||||
const param = {
|
||||
proposalStatus,
|
||||
voter: '',
|
||||
depositor: '',
|
||||
pagination,
|
||||
}
|
||||
return this.client.proposals(param)
|
||||
return this.blockchain.rpc.proposals(proposalStatus, '', '')
|
||||
},
|
||||
fetchParams() {
|
||||
this.client.params({paramsType: 'deposit'}).then(x => {
|
||||
if(x.deposit_params) this.params.deposit = x.deposit_params
|
||||
})
|
||||
this.client.params({paramsType: 'voting'}).then(x => {
|
||||
if(x.voting_params) this.params.voting = x.voting_params
|
||||
})
|
||||
this.client.params({paramsType: 'tallying'}).then(x => {
|
||||
if(x.tally_params) this.params.tally = x.tally_params
|
||||
})
|
||||
async fetchParams() {
|
||||
// this.blockchain.rpc.govParam().then(x => {
|
||||
// this.params.deposit = x.deposit
|
||||
// })
|
||||
},
|
||||
fetchTally(proposalId: Long) {
|
||||
return this.client.tallyResult({proposalId})
|
||||
async fetchTally(proposalId: number) {
|
||||
return this.blockchain.rpc.tally(proposalId)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -2,6 +2,10 @@ import { defineStore } from "pinia";
|
||||
import { useBlockchain } from "./useBlockchain";
|
||||
import { createMintClientForChain } from "@/libs/client";
|
||||
|
||||
import { createRpcQueryExtension } from '@ping-pub/codegen/src/cosmos/mint/v1beta1/query.rpc.Query'
|
||||
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
|
||||
import { QueryClient, setupMintExtension } from "@cosmjs/stargate";
|
||||
|
||||
export const useMintStore = defineStore('mintStore', {
|
||||
state: () => {
|
||||
return {
|
||||
@ -9,16 +13,16 @@ export const useMintStore = defineStore('mintStore', {
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
client() {
|
||||
const chain = useBlockchain()
|
||||
return createMintClientForChain(chain.chainName, chain.restClient)
|
||||
blockchain() {
|
||||
return useBlockchain()
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
fetchInflation() {
|
||||
this.client.inflation({}).then(x => {
|
||||
this.inflation = String(x.inflation)
|
||||
console.log(this.inflation)
|
||||
async fetchInflation() {
|
||||
this.blockchain.rpc.inflation().then(x => {
|
||||
this.inflation = String(x)
|
||||
}).catch(err => {
|
||||
this.inflation = ""
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,64 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { useBlockchain } from "./useBlockchain";
|
||||
import { createStakingRestClientForChain } from "@/libs/client";
|
||||
import type { ParamsSDKType, PoolSDKType } from "@ping-pub/codegen/src/cosmos/staking/v1beta1/staking";
|
||||
import type { Params, Pool, Validator} from "@ping-pub/codegen/src/cosmos/staking/v1beta1/staking";
|
||||
|
||||
import { get } from "@/libs/http";
|
||||
|
||||
import type { BondStatusString } from "@/libs/client.rpc";
|
||||
|
||||
export const useStakingStore = defineStore('stakingStore', {
|
||||
state: () => {
|
||||
return {
|
||||
params: {} as ParamsSDKType,
|
||||
pool: {} as PoolSDKType,
|
||||
validators: [] as Validator[],
|
||||
params: {} ,
|
||||
pool: {} as Pool | undefined,
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
client() {
|
||||
const chain = useBlockchain()
|
||||
return createStakingRestClientForChain(chain.chainName, chain.restClient)
|
||||
totalPower(): number {
|
||||
const sum = (s:number, e: Validator) => { return s + parseInt(e.delegatorShares) }
|
||||
return this.validators ? this.validators.reduce(sum, 0): 0
|
||||
},
|
||||
blockchain() {
|
||||
return useBlockchain()
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async init() {
|
||||
this.$reset()
|
||||
this.fetchPool()
|
||||
this.fetchAcitveValdiators()
|
||||
return await this.fetchParams()
|
||||
},
|
||||
async keybase(identity: string) {
|
||||
return get(`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`)
|
||||
},
|
||||
async fetchParams() {
|
||||
const response = await this.client.params({})
|
||||
const response = await this.blockchain.rpc.stakingParams()
|
||||
if(response.params) this.params = response.params
|
||||
return this.params
|
||||
},
|
||||
async fetchPool() {
|
||||
const response = await this.client.pool({})
|
||||
if(response.pool) {
|
||||
const response = await this.blockchain.rpc.stakingPool()
|
||||
this.pool = response.pool
|
||||
},
|
||||
async fetchAcitveValdiators() {
|
||||
return this.fetchValidators('BOND_STATUS_BONDED')
|
||||
},
|
||||
async fetchInacitveValdiators() {
|
||||
return this.fetchValidators('BOND_STATUS_UNBONDED')
|
||||
},
|
||||
async fetchValidator(validatorAddr: string) {
|
||||
return this.blockchain.rpc.validator(validatorAddr)
|
||||
},
|
||||
async fetchValidators(status: BondStatusString) {
|
||||
return this.blockchain.rpc.validators(status, undefined).then(res => {
|
||||
const vals = res.validators.sort((a, b) => (Number(b.delegatorShares) - Number(a.delegatorShares)))
|
||||
if(status==='BOND_STATUS_BONDED') {
|
||||
this.validators = vals
|
||||
}
|
||||
return vals
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
@ -2,6 +2,10 @@
|
||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"es2017",
|
||||
"dom"
|
||||
],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
|
Loading…
Reference in New Issue
Block a user