Merge pull request #363 from alisaweb3/v3-single

Parameters page
This commit is contained in:
ping
2023-04-27 17:54:07 +08:00
committed by GitHub
538 changed files with 365 additions and 23012 deletions
+22
View File
@@ -0,0 +1,22 @@
<script lang="ts" setup>
import type { PropType } from 'vue';
const props = defineProps({
cardItem: {
type: Object as PropType<{title: string;items: Array<any>;}>,
},
})
</script>
<template>
<div class="bg-card px-4 pt-3 pb-4 rounded-sm mt-6"
v-if="props.cardItem?.items && props.cardItem?.items?.length > 0">
<div class="text-base mb-3 text-main">{{ props.cardItem?.title }}</div>
<div class="grid grid-cols-5 gap-4">
<div v-for="(item,index) of props.cardItem?.items" :key="index" class="rounded-sm bg-active px-4 py-2">
<div class="text-xs mb-2 text-secondary">{{ item?.subtitle }}</div>
<div class="text-base text-main">{{ item?.value }}</div>
<!-- {{ item }} -->
</div>
</div>
</div>
</template>
+26
View File
@@ -0,0 +1,26 @@
<script lang="ts" setup>
import type { PropType } from 'vue';
const props = defineProps({
tableItem: {
type: Object as PropType<{title: string;items: Object;}>,
},
})
function formatTitle (name: string){
return String(name).replaceAll('_', ' ')
}
</script>
<template>
<div class="bg-card px-4 pt-3 pb-4 rounded-sm mt-6">
<div class="text-base mb-3 text-main">{{ props.tableItem?.title }}</div>
<div class="">
<div class="d-flex flex-nowrap" v-for="(item,index ) of props.tableItem?.items" :key="index">
<div class="mr-6">{{ formatTitle(item?.subtitle) }}</div>
<div class="flex-1" >{{ item?.value }}</div>
<div>{{typeof(item?.value )}}</div>
<!-- {{ item }} -->
</div>
</div>
</div>
</template>
+48
View File
@@ -0,0 +1,48 @@
export function getLocalObject(name: string) {
const text = localStorage.getItem(name)
if (text) {
return JSON.parse(text)
}
return null
}
export function getLocalChains() {
return 'osmosis'
}
export const percent = (num: number) => {
return parseFloat((num * 100).toFixed(2))
}
const COUNT_ABBRS = ['', 'K', 'M', 'B', 't', 'q', 's', 'S', 'o', 'n', 'd', 'U', 'D', 'T', 'Qt', 'Qd', 'Sd', 'St']
export function formatNumber(count:number, withAbbr = false, decimals = 2) {
const i = count === 0 ? count : Math.floor(Math.log(count) / Math.log(1000))
let result: any = parseFloat((count / (1000 ** i)).toFixed(decimals))
if (withAbbr && COUNT_ABBRS[i]) {
result += `${COUNT_ABBRS[i]}`
}
return result
}
export function formatTokenAmount(assets: any ,tokenAmount: any, decimals = 2, tokenDenom = 'uatom', format = true) {
const denom = tokenDenom?.denom_trace ? tokenDenom?.denom_trace?.base_denom : tokenDenom
let amount = 0
const asset = assets.find((a:any) => (a.base === denom))
let exp = asset? asset.exponent: String(denom).startsWith('gravity') ? 18 : 6
const config = Object.values(getLocalChains())
amount = Number(Number(tokenAmount)) / (10 ** exp)
if (amount > 10) {
if (format) { return numberWithCommas(parseFloat(amount.toFixed(decimals))) }
return parseFloat(amount.toFixed(decimals))
}
return parseFloat(amount.toFixed(exp))
}
export function numberWithCommas(x: any) {
const parts = x.toString().split('.')
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',')
return parts.join('.')
}
-12
View File
@@ -1,12 +0,0 @@
<template>
<div class="text-main text-h5 mb-4">Parameters</div>
<div class="bg-card px-4 pt-3 pb-4 rounded-sm">
<div class="text-base mb-3 text-main">Minting Parameters</div>
<div class="grid grid-cols-5 gap-4">
<div v-for="item in 10" :key="item" class="rounded-sm bg-active px-4 py-2">
<div class="text-xs mb-2 text-secondary">Blocks Per Year</div>
<div class="text-base text-main">4,360,000</div>
</div>
</div>
</div>
</template>
+3 -3
View File
@@ -19,15 +19,15 @@ onMounted(()=>{
</VTabs>
<VWindow v-model="tab" class="mt-5">
<VWindowItem value="2">
<ProposalListItem :proposals="store.proposals['2']"/>
<ProposalListItem :proposals="store?.proposals['2']"/>
</VWindowItem>
<VWindowItem value="3">
<ProposalListItem :proposals="store.proposals['3']"/>
<ProposalListItem :proposals="store?.proposals['3']"/>
</VWindowItem>
<VWindowItem value="4">
<ProposalListItem :proposals="store.proposals['4']"/>
<ProposalListItem :proposals="store?.proposals['4']"/>
</VWindowItem>
</VWindow>
</div>
+1 -3
View File
@@ -25,9 +25,7 @@ const format = useFormatter();
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
blockchain.$subscribe((m, s) => {
console.log('index:', m);
if (!Array.isArray(m.events) && ['chainName', 'endpoint'].includes(m.events.key)) {
console.log(m.events.key);
store.loadDashboard();
}
});
@@ -154,7 +152,7 @@ function shortName(name: string, id: string) {
<VCardTitle>Active Proposals</VCardTitle>
</VCardItem>
<VCardItem>
<ProposalListItem :proposals="store.proposals" />
<ProposalListItem :proposals="store?.proposals" />
</VCardItem>
<VCardText v-if="store.proposals.length === 0">No active proposals</VCardText>
</VCard>
-1
View File
@@ -176,7 +176,6 @@ export const useIndexModule = defineStore('module-index', {
},
actions: {
async loadDashboard() {
console.log('initial dashboard')
this.$reset()
this.initCoingecko()
useMintStore().fetchInflation()
+50
View File
@@ -0,0 +1,50 @@
<script lang="ts" setup>
import { useParamStore } from '@/stores';
import { ref, onMounted } from 'vue'
import CardParameter from '@/components/CardParameter.vue'
import TableParameter from '@/components/TableParameter.vue'
import { sort } from 'semver';
const store = useParamStore()
const chain = ref(store.chain)
onMounted(() => {
// fetch the data
store.initial()
})
</script>
<template>
<div>
<!-- Chain ID -->
<div class="bg-card px-4 pt-3 pb-4 rounded-sm">
<div class="text-base mb-3 text-main">{{ chain.title }}</div>
<div class="grid grid-cols-5 gap-4">
<div v-for="(item,index) of chain.items" :key="index" class="rounded-sm bg-active px-4 py-2">
<div class="text-xs mb-2 text-secondary">{{ item.subtitle }}</div>
<div class="text-base text-main">{{ item.value }}</div>
</div>
</div>
</div>
<!-- minting Parameters -->
<CardParameter :cardItem="store.mint"/>
<!-- Staking Parameters -->
<CardParameter :cardItem="store.staking"/>
<!-- Governance Parameters -->
<CardParameter :cardItem="store.gov"/>
<!-- Distribution Parameters -->
<CardParameter :cardItem="store.distribution"/>
<!-- Slashing Parameters -->
<CardParameter :cardItem="store.slashing"/>
<!-- Application Version -->
<TableParameter :tableItem="store.appVersion"/>
<!-- Node Information -->
<TableParameter :tableItem="store.nodeVersion"/>
</div>
</template>
<route>
{
meta: {
i18n: 'parameters'
}
}
</route>
+2 -1
View File
@@ -3,7 +3,8 @@
"dashboard": "面板首页",
"blocks": "区块和交易",
"staking": "质押生息",
"governance": "社区治理"
"governance": "社区治理",
"parameters": "参数"
},
"index": {
"slogan": "Ping Dashboard 是一个区块链浏览器,也是一个网页钱包,还有更多 ... 🛠",
+2 -1
View File
@@ -3,7 +3,8 @@
"dashboard": "Dashboard",
"blocks": "Blocks&Transaction",
"staking": "Staking",
"governance": "Governance"
"governance": "Governance",
"parameters": "Parameters"
},
"index": {
"slogan": "Ping Dashboard is not just an explorer but also a wallet and more ... 🛠",
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { useThemeConfig } from '@core/composable/useThemeConfig';
import type { ThemeSwitcherTheme } from '@layouts/types';
import { onMounted } from 'vue';
import { onMounted, watch } from 'vue';
const props = defineProps<{
themes: ThemeSwitcherTheme[];
@@ -18,11 +18,15 @@ const {
);
const changeTheme = () => {
theme.value = getNextThemeName();
theme.value = getNextThemeName();
};
const changeMode = (val: 'dark' | 'light') => {
if (val === 'dark') {
const changeMode = (val: 'dark' | 'light' | 'system') => {
let value = val;
if (theme.value === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
value = 'dark';
}
if (value === 'dark') {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
} else {
@@ -31,7 +35,7 @@ const changeMode = (val: 'dark' | 'light') => {
}
};
// Update icon if theme is changed from other sources
watch(theme, (val: 'dark' | 'light') => {
watch(theme, (val: 'dark' | 'light' | 'system') => {
currentThemeName.value = val;
changeMode(val);
});
@@ -230,8 +230,8 @@ watch(isVerticalNavMini(windowWidth, isVerticalNavHovered), val => {
class="nav-group-children"
>
<Component
:is="'children' in child ? 'VerticalNavGroup' : VerticalNavLink"
v-for="child in item.children"
:is="'children' in child ? 'VerticalNavGroup' : VerticalNavLink"
:key="child.title"
:item="child"
/>
+2 -1
View File
@@ -7,4 +7,5 @@ export * from './useFormatter'
export * from './useGovStore'
export * from './useMintStore'
export * from './useStakingStore'
export * from './useDistributionStore'
export * from './useDistributionStore'
export * from './useParamsStore'
-2
View File
@@ -36,7 +36,6 @@ 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 || '',
@@ -115,7 +114,6 @@ export const useBlockchain = defineStore("blockchain", {
},
setCurrent(name: string) {
this.chainName = name
console.log('set current', name)
},
}
+4 -1
View File
@@ -70,6 +70,8 @@ export interface ChainConfig {
cosmosSdk?: string,
tendermint?: string,
},
exponent: string,
excludes?: string,
}
export interface LocalConfig {
@@ -109,7 +111,8 @@ export function fromLocal(lc: LocalConfig ): ChainConfig {
display: x.symbol,
symbol: x.symbol,
logo_URIs: { svg: x.logo },
coingecko_id: x.coingecko_id,
coingecko_id: x.coingecko_id,
exponent: x.exponent,
denom_units: [{denom: x.base, exponent: 0}, {denom: x.symbol.toLowerCase(), exponent: Number(x.exponent)}]
}))
conf.bech32Prefix = lc.addr_prefix
+195
View File
@@ -0,0 +1,195 @@
import { defineStore } from "pinia";
import { useBlockchain } from "./useBlockchain";
import { percent,formatNumber,formatTokenAmount } from '@/libs/utils'
export interface stakingItem {
unbonding_time: string
max_validators: number
max_entries:number
historical_entries:number
bond_denom: string
min_commission_rate: string
min_self_delegation:string
}
export const useParamStore = defineStore("paramstore", {
state: () => ({
latestTime: '',
chain: {
title: '',
class: 'border-primary',
items: [
{ subtitle: 'height', icon: 'BoxIcon', color: 'light-success', value: '-' },
{ subtitle: 'bonded_and_supply', icon: 'DollarSignIcon', color: 'light-danger', value: '-' },
{ subtitle: 'bonded_ratio', icon: 'PercentIcon', color: 'light-warning', value: '-' },
{ subtitle: 'inflation', icon: 'TrendingUpIcon', color: 'light-primary', value: '-' },
],
},
mint: {
title: 'Mint Parameters',
items: [] as Array<any>,
},
staking: {
title: 'Staking Parameters',
items: [] as Array<any>,
},
distribution: {
title: 'Distribution Parameters',
items: [] as Array<any>,
},
slashing: {
title: 'Slashing Parameters',
items: [] as Array<any>,
},
gov: {
title: 'Governance Parameters',
items: [] as Array<any>,
},
appVersion: {
title: 'Application Version',
items: {},
},
nodeVersion: {
title: 'Node Information',
items: {},
},
}),
getters: {
blockchain() {
return useBlockchain()
},
},
actions: {
initial() {
this.handleBaseBlockLatest()
// this.handleMintParam()
this.handleStakingParams()
this.handleSlashingParams()
this.handleDistributionParams()
this.handleGovernanceParams()
this.handleAbciInfo()
},
async handleBaseBlockLatest() {
try {
const res = await this.getBaseTendermintBlockLatest()
const height = this.chain.items.findIndex(x => x.subtitle === 'height')
this.chain.title = `Chain ID: ${res.block.header.chain_id}`
this.chain.items[height].value = res.block.header.height
// if (timeIn(res.block.header.time, 3, 'm')) {
// this.syncing = true
// } else {
// this.syncing = false
// }
// this.latestTime = toDay(res.block.header.time, 'long')
this.latestTime = res.block.header.time
} catch (error) {
console.warn(error)
}
},
async handleStakingParams() {
const res = await this.getStakingParams()
const bond_denom = res?.params.bond_denom
this.staking.items = Object.entries(res.params).map(([key, value]) => ({ subtitle:key,
value: value })).filter((item: any) => {
if (!['min_commission_rate','min_self_delegation'].includes(item.subtitle)) return item
})
Promise.all([this.getStakingPool(), this.getBankTotal(bond_denom)])
.then(resArr => {
const pool = resArr[0]?.pool
const amount =resArr[1]?.amount?.amount
const assets = this.blockchain.current?.assets
const bondedAndSupply = this.chain.items.findIndex(x => x.subtitle === 'bonded_and_supply')
this.chain.items[bondedAndSupply].value = `${formatNumber(formatTokenAmount(assets,pool.bonded_tokens, 2, bond_denom, false), true, 0)}/${formatNumber(formatTokenAmount(assets,amount, 2, bond_denom, false), true, 0)}`
const bondedRatio = this.chain.items.findIndex(x => x.subtitle === 'bonded_ratio')
this.chain.items[bondedRatio].value = `${percent(Number(pool.bonded_tokens) /Number(amount)) }%`
})
},
async handleMintParam() {
const excludes = this.blockchain.current?.excludes
if(excludes && excludes.indexOf('mint') > -1){
return
}
// this.getMintingInflation().then(res => {
// const chainIndex = this.chain.items.findIndex(x => x.subtitle === 'inflation')
// this.chain.items[chainIndex].value = `${percent(res)}%`
// })
const res = await this.getMintParam()
console.log(res, 'mint')
},
async handleSlashingParams(){
const res = await this.getSlashingParams()
this.slashing.items = Object.entries(res.params).map(([key, value]) => ({ subtitle:key,
value: value }))
},
async handleDistributionParams(){
const res = await this.getDistributionParams()
this.distribution.items = Object.entries(res.params).map(([key, value]) => ({ subtitle: key,
value: value }))
},
async handleGovernanceParams() {
const excludes = this.blockchain.current?.excludes
if(excludes && excludes.indexOf('governance') > -1){
return
}
Promise.all([this.getGovParamsVoting(),this.getGovParamsDeposit(),this.getGovParamsTally()]).then((resArr) => {
const govParams = {...resArr[0]?.voting_params,...resArr[1]?.deposit_params,...resArr[2]?.tally_params}
this.gov.items = Object.entries(govParams).map(([key, value]) => ({ subtitle:key,
value: value }))
})
},
async handleAbciInfo(){
const res = await this.fetchAbciInfo()
this.appVersion.items = Object.entries(res.application_version).map(([key, value]) => ({ subtitle:key,
value: value }))
this.nodeVersion.items = Object.entries(res.default_node_info).map(([key, value]) => ({ subtitle:key,
value: value }))
console.log('handleAbciInfo', res)
},
async getBaseTendermintBlockLatest() {
return await this.blockchain.rpc.getBaseBlockLatest()
},
async getMintParam() {
return await this.blockchain.rpc.getMintParam()
},
async getStakingParams() {
return await this.blockchain.rpc.getStakingParams()
},
async getStakingPool(){
return await this.blockchain.rpc.getStakingPool()
},
async getBankTotal(denom: string){
return await this.blockchain.rpc.getBankSupplyByDenom(denom)
// if (compareVersions(this.config.sdk_version, '0.46.2') > 0) {
// return this.get(`/cosmos/bank/v1beta1/supply/by_denom?denom=${denom}`).then(data => commonProcess(data).amount)
// }
// if (compareVersions(this.config.sdk_version, '0.40') < 0) {
// return this.get(`/supply/total/${denom}`).then(data => ({ amount: commonProcess(data), denom }))
// }
// return this.get(`/cosmos/bank/v1beta1/supply/${denom}`).then(data => commonProcess(data).amount)
},
async getSlashingParams() {
return await this.blockchain.rpc.getSlashingParams()
},
async getDistributionParams() {
return await this.blockchain.rpc.getDistributionParams()
},
async getGovParamsVoting() {
return await this.blockchain.rpc.getGovParamsVoting()
},
async getGovParamsDeposit() {
return await this.blockchain.rpc.getGovParamsDeposit()
},
async getGovParamsTally() {
return await this.blockchain.rpc.getGovParamsTally()
},
async fetchAbciInfo() {
return this.blockchain.rpc.getBaseNodeInfo()
}
}
})