diff --git a/packages/dashboard/chains/mainnet/cosmos.json b/packages/dashboard/chains/mainnet/cosmos.json index 418945b9..71193408 100644 --- a/packages/dashboard/chains/mainnet/cosmos.json +++ b/packages/dashboard/chains/mainnet/cosmos.json @@ -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", diff --git a/packages/dashboard/package.json b/packages/dashboard/package.json index 1e079d56..e9128a0a 100644 --- a/packages/dashboard/package.json +++ b/packages/dashboard/package.json @@ -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", diff --git a/packages/dashboard/src/components/charts/PriceMarketChart.vue b/packages/dashboard/src/components/charts/PriceMarketChart.vue index a947a19b..ae36430f 100644 --- a/packages/dashboard/src/components/charts/PriceMarketChart.vue +++ b/packages/dashboard/src/components/charts/PriceMarketChart.vue @@ -23,8 +23,8 @@ const series = computed(() => { PriceVolume \ No newline at end of file diff --git a/packages/dashboard/src/components/charts/apexCharConfig.ts b/packages/dashboard/src/components/charts/apexCharConfig.ts index 0703d32c..059fdc43 100644 --- a/packages/dashboard/src/components/charts/apexCharConfig.ts +++ b/packages/dashboard/src/components/charts/apexCharConfig.ts @@ -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 }, }, diff --git a/packages/dashboard/src/layouts/components/ChainProfile.vue b/packages/dashboard/src/layouts/components/ChainProfile.vue index df75e46a..f4253946 100644 --- a/packages/dashboard/src/layouts/components/ChainProfile.vue +++ b/packages/dashboard/src/layouts/components/ChainProfile.vue @@ -3,7 +3,7 @@ import { useBlockchain, useBaseStore } from '@/stores'; const chainStore = useBlockchain() const baseStore = useBaseStore() - +chainStore.initial() - {{ baseStore.latest.block?.header?.chain_id || '' }} - {{ chainStore.availableEndpoint }} + {{ baseStore.latest.block?.header?.chain_id || chainStore.chainName || '' }} + {{ chainStore.connErr|| chainStore.endpoint.address }} diff --git a/packages/dashboard/src/layouts/components/DefaultLayout.vue b/packages/dashboard/src/layouts/components/DefaultLayout.vue index ea5d68d4..b4373bf3 100644 --- a/packages/dashboard/src/layouts/components/DefaultLayout.vue +++ b/packages/dashboard/src/layouts/components/DefaultLayout.vue @@ -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() } }) diff --git a/packages/dashboard/src/libs/address.ts b/packages/dashboard/src/libs/address.ts new file mode 100644 index 00000000..064c40f6 --- /dev/null +++ b/packages/dashboard/src/libs/address.ts @@ -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) + } \ No newline at end of file diff --git a/packages/dashboard/src/libs/client.rpc.ts b/packages/dashboard/src/libs/client.rpc.ts new file mode 100644 index 00000000..f7dd9db3 --- /dev/null +++ b/packages/dashboard/src/libs/client.rpc.ts @@ -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 | ""; + +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() + } + + +} \ No newline at end of file diff --git a/packages/dashboard/src/libs/index.ts b/packages/dashboard/src/libs/index.ts new file mode 100644 index 00000000..af58bf6f --- /dev/null +++ b/packages/dashboard/src/libs/index.ts @@ -0,0 +1,4 @@ +export * from './address' +export * from './client' +export * from './http' +export * from './misc' \ No newline at end of file diff --git a/packages/dashboard/src/libs/misc.ts b/packages/dashboard/src/libs/misc.ts new file mode 100644 index 00000000..7bee44e1 --- /dev/null +++ b/packages/dashboard/src/libs/misc.ts @@ -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 + ) +} \ No newline at end of file diff --git a/packages/dashboard/src/libs/utils.ts b/packages/dashboard/src/libs/utils.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/dashboard/src/main.ts b/packages/dashboard/src/main.ts index be321fc7..8d19d5aa 100644 --- a/packages/dashboard/src/main.ts +++ b/packages/dashboard/src/main.ts @@ -17,7 +17,6 @@ loadFonts(); // Create vue app const app = createApp(App); - // Use plugins app.use(i18n) app.use(vuetify); diff --git a/packages/dashboard/src/modules/[chain]/index.vue b/packages/dashboard/src/modules/[chain]/index.vue index e9ae773d..b1ba12a1 100644 --- a/packages/dashboard/src/modules/[chain]/index.vue +++ b/packages/dashboard/src/modules/[chain]/index.vue @@ -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) {
- + {{ coinInfo.name }} ({{ coinInfo.symbol }}) @@ -120,7 +117,7 @@ function shortName(name: string, id: string) { - + @@ -129,7 +126,7 @@ function shortName(name: string, id: string) { - {{ tag }} + {{ tag }} diff --git a/packages/dashboard/src/modules/[chain]/indexStore.ts b/packages/dashboard/src/modules/[chain]/indexStore.ts index 6b82edd0..9f6e1052 100644 --- a/packages/dashboard/src/modules/[chain]/indexStore.ts +++ b/packages/dashboard/src/modules/[chain]/indexStore.ts @@ -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 } }, @@ -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) diff --git a/packages/dashboard/src/modules/[chain]/staking/[validator].vue b/packages/dashboard/src/modules/[chain]/staking/[validator].vue new file mode 100644 index 00000000..1901bb4b --- /dev/null +++ b/packages/dashboard/src/modules/[chain]/staking/[validator].vue @@ -0,0 +1,55 @@ + + \ No newline at end of file diff --git a/packages/dashboard/src/modules/[chain]/staking/index.vue b/packages/dashboard/src/modules/[chain]/staking/index.vue new file mode 100644 index 00000000..2c45735c --- /dev/null +++ b/packages/dashboard/src/modules/[chain]/staking/index.vue @@ -0,0 +1,226 @@ + + \ No newline at end of file diff --git a/packages/dashboard/src/pages/index.vue b/packages/dashboard/src/pages/index.vue index 665f48c8..18801721 100644 --- a/packages/dashboard/src/pages/index.vue +++ b/packages/dashboard/src/pages/index.vue @@ -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() +