use simple repo

This commit is contained in:
liangping
2023-04-03 17:08:02 +08:00
parent 52de377644
commit 671050f5f2
1157 changed files with 33878 additions and 159167 deletions
+10
View File
@@ -0,0 +1,10 @@
export * from './useBankStore'
export * from './useBlockchain'
export * from './useCoinGecko'
export * from './useDashboard'
export * from './useBaseStore'
export * from './useFormatter'
export * from './useGovStore'
export * from './useMintStore'
export * from './useStakingStore'
export * from './useDistributionStore'
+13
View File
@@ -0,0 +1,13 @@
import { defineStore } from "pinia";
export const useStoreName = defineStore('bankstore', {
state: () => {
return {
}
},
getters: {
},
actions: {
}
})
+45
View File
@@ -0,0 +1,45 @@
import { defineStore } from "pinia";
import { useBlockchain } from "./useBlockchain";
import { useStakingStore } from "./useStakingStore";
import type { Coin } from "@/types";
export const useBankStore = defineStore('bankstore', {
state: () => {
return {
supply: {} as Coin[],
balances: {} as Record<string, Coin[]>,
totalSupply: {supply: []} ,
}
},
getters: {
blockchain() {
return useBlockchain()
},
staking() {
return useStakingStore()
}
},
actions: {
initial() {
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
})
}
},
// 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 fetchSupply(denom: string) {
return this.blockchain.rpc.supplyOf( denom )
}
}
})
+66
View File
@@ -0,0 +1,66 @@
import { defineStore } from "pinia";
import { useBlockchain } from "@/stores";
import dayjs from "dayjs";
import type { BlockResponse } from "@cosmjs/tendermint-rpc";
export const useBaseStore = defineStore('baseStore', {
state: () => {
return {
earlest: {} as BlockResponse,
latest: {} as BlockResponse,
recents: [] as BlockResponse[]
}
},
getters: {
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: {
async initial() {
this.fetchLatest()
},
async clearRecentBlocks() {
this.recents = []
},
async fetchLatest() {
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 fetchValidatorByHeight(height?: number, offset = 0) {
return this.blockchain.rpc.validatorsAtHeight(height)
},
async fetchLatestValidators(offset = 0) {
return this.blockchain.rpc.validatorsAtHeight()
},
async fetchBlock(height?: number) {
return this.blockchain.rpc.block(height)
},
async fetchAbciInfo() {
return this.blockchain.rpc.abciInfo()
}
// async fetchNodeInfo() {
// return this.blockchain.rpc.no()
// }
}
})
+128
View File
@@ -0,0 +1,128 @@
import { defineStore } from "pinia";
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";
import { useStakingStore } from "./useStakingStore";
import { useBankStore } from "./useBankStore";
import { useBaseStore } from "./useBaseStore";
import { useGovStore } from "./useGovStore";
import { ref } from "vue";
import { useMintStore } from "./useMintStore";
import { useBlockModule } from "@/modules/[chain]/block/block";
export const useBlockchain = defineStore("blockchain", {
state: () => {
return {
status: {} as Record<string, string>,
rest: '',
chainName: "",
endpoint: {} as {
type?: EndpointType,
address: string
provider: string
},
connErr: ""
}
},
getters: {
current() : ChainConfig | undefined {
return this.dashboard.chains[this.chainName]
},
logo(): string {
return this.current?.logo || ''
},
dashboard() {
return useDashboard()
},
computedChainMenu() {
let currNavItem: VerticalNavItems = []
const router = useRouter()
const routes = router?.getRoutes()||[]
console.log(routes)
if(this.current && routes) {
currNavItem = [{
title: this.current?.prettyName || this.chainName || '',
icon: {image: this.current.logo, size: '22'},
i18n: false,
children: routes
.filter(x=>x.name && x.name.toString().startsWith('chain'))
.map(x => ({
title: `module.${x.name?.toString()}`,
to: {path: x.path.replace(':chain',this.chainName)},
icon: { icon: 'mdi-chevron-right', size: '22'},
i18n: true
}))
.sort((a,b)=>a.to.path.length - b.to.path.length)
}]
}
// compute favorite menu
const favNavItems: VerticalNavItems = []
this.dashboard.favorite.forEach(name => {
const ch = this.dashboard.chains[name]
if(ch) {
favNavItems.push({
title: ch.prettyName || ch.chainName || name,
to: { path: `/${ch.chainName || name}`},
icon: {image: ch.logo, size: '22'}
} )
}
})
// combine all together
return [...currNavItem,
{ heading: 'Ecosystem' },
{
title: 'Favorite',
children: favNavItems,
badgeContent: favNavItems.length,
badgeClass: 'bg-primary',
i18n: true,
icon: { icon: 'mdi-star', size: '22'}
},
{
title: 'All Blockchains',
to: { path : '/'},
badgeContent: this.dashboard.length,
badgeClass: 'bg-primary',
i18n: true,
icon: { icon: 'mdi-grid', size: '22'}
}
]
},
},
actions: {
async initial() {
await this.randomSetupEndpoint()
await useStakingStore().init()
useBankStore().initial()
useBaseStore().initial()
useGovStore().initial()
useMintStore().initial()
useBlockModule().initial()
},
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)
},
}
})
+51
View File
@@ -0,0 +1,51 @@
import { defineStore } from "pinia";
import { get } from '../libs/http'
import type { LoadingStatus } from "./useDashboard";
export interface PriceMeta {
usd?: string,
usd_24h_change?: string,
cny?: string,
cny_24h_change? : string,
eur?: string,
eur_24h_change?: string,
}
const LocalStoreKey = 'currency'
export const useCoingecko = defineStore('coingecko', {
state: () => {
const currency = localStorage.getItem(LocalStoreKey)
return {
currency, // secondary currency
loadStatus: {} as Record<string, LoadingStatus | undefined>,
prices: {} as Record<string, PriceMeta>,
marketChart: {}
}
},
getters: {
},
actions: {
getMarketChart(days = 30, coinId = 'cosmos') {
return get(`https://api.coingecko.com/api/v3/coins/${coinId}/market_chart?vs_currency=usd&days=${days}`)
},
fetchCoinPrice(ids: string[]) {
const url = `https://api.coingecko.com/api/v3/simple/price?include_24hr_change=true&vs_currencies=${['usd', this.currency].join(',')}&ids=${ids.join(',')}`
get(url).then(data => {
this.prices = {...this.prices, ...data}
})
},
getCoinInfo(coinId: string) {
return get(`https://api.coingecko.com/api/v3/coins/${coinId}`)
},
setSecondaryCurrency(currency: string) {
if(currency !== 'usd') {
localStorage.setItem(LocalStoreKey, currency)
this.currency = currency
}
}
}
})
+261
View File
@@ -0,0 +1,261 @@
import { defineStore } from "pinia";
import { get } from '../libs/http'
import type { Chain, Asset } from '@ping-pub/chain-registry-client/dist/types'
import { useBlockchain } from "./useBlockchain";
export enum EndpointType {
rpc,
rest,
grpc,
// webgrpc
}
export interface Endpoint {
type?: EndpointType,
address: string,
provider: string
}
// Chain config structure of cosmos.directory
export interface DirectoryChain {
assets: Asset[],
bech32_prefix: string,
best_apis: {
rest: Endpoint[]
rpc: Endpoint[]
},
chain_id: string,
chain_name: string,
pretty_name: string,
coingecko_id: string,
cosmwasm_enabled: boolean,
decimals: number,
denom: string,
display: string,
explorers: {
name?: string | undefined;
kind?: string | undefined;
url?: string | undefined;
tx_page?: string | undefined;
account_page?: string | undefined;
}[] | undefined,
height: number,
image: string,
name: string,
network_type: string,
symbol: string,
versions?: {
application_version: string,
cosmos_sdk_version: string,
tendermint_version: string,
}
}
export interface ChainConfig {
chainName: string,
prettyName: string,
bech32Prefix: string,
chainId: string,
assets: Asset[],
themeColor?: string,
endpoints: {
rest?: Endpoint[]
rpc?: Endpoint[]
grpc?: Endpoint[]
},
logo: string,
versions: {
application?: string,
cosmosSdk?: string,
tendermint?: string,
},
}
export interface LocalConfig {
addr_prefix: string,
alias: string,
api: string[] | Endpoint[],
assets: {base: string, coingecko_id: string, exponent: string, logo: string, symbol: string}[]
chain_name: string,
coin_type: string
logo: string,
min_tx_fee: string,
rpc: string[] | Endpoint[],
sdk_version: string,
}
function apiConverter(api: any[]){
if(!api) return []
const array = typeof api === 'string'? [api] : api
return array.map(x => {
if(typeof x === 'string') {
const parts = String(x).split('.')
return {
address: x,
provider: parts.length >=2 ? parts[parts.length-2] : x
}
}else{
return x as Endpoint
}
})
}
export function fromLocal(lc: LocalConfig ): ChainConfig {
const conf = {} as ChainConfig
conf.assets = lc.assets.map(x => ({
name: x.base,
base: x.base,
display: x.symbol,
symbol: x.symbol,
logo_URIs: { svg: x.logo },
coingecko_id: x.coingecko_id,
denom_units: [{denom: x.base, exponent: 0}, {denom: x.symbol.toLowerCase(), exponent: Number(x.exponent)}]
}))
conf.bech32Prefix = lc.addr_prefix
conf.chainName = lc.chain_name
conf.prettyName = lc.chain_name
conf.endpoints = {
rest: apiConverter(lc.api),
rpc: apiConverter(lc.rpc),
}
conf.logo = lc.logo
return conf
}
export function fromDirectory(source: DirectoryChain): ChainConfig {
const conf = {} as ChainConfig
conf.assets = source.assets,
conf.bech32Prefix = source.bech32_prefix,
conf.chainId = source.chain_id,
conf.chainName = source.chain_name,
conf.prettyName = source.pretty_name,
conf.versions = {
application: source.versions?.application_version || '',
cosmosSdk: source.versions?.cosmos_sdk_version || '',
tendermint: source.versions?.tendermint_version || '',
},
conf.logo = pathConvert(source.image)
conf.endpoints = source.best_apis
return conf
}
function pathConvert(path: string | undefined) {
if(path) {
path = path.replace('https://raw.githubusercontent.com/cosmos/chain-registry/master', 'https://registry.ping.pub')
}
return path || ''
}
export function getLogo(conf: {
svg?: string,
png?: string,
jpeg?: string,
} | undefined) {
if(conf) {
return pathConvert(conf.svg || conf.png || conf.jpeg)
}
return undefined
}
function createChainFromDirectory(source: DirectoryChain) : Chain {
const conf: Chain = {} as Chain;
conf.apis = source.best_apis
conf.bech32_prefix = source.bech32_prefix
conf.chain_id = source.chain_id
conf.chain_name = source.chain_name
conf.explorers = source.explorers
conf.pretty_name = source.pretty_name
if(source.versions) {
conf.codebase = {
recommended_version: source.versions.application_version,
cosmos_sdk_version: source.versions.cosmos_sdk_version,
tendermint_version: source.versions.tendermint_version,
}
}
if(source.image) {
conf.logo_URIs = {
svg: source.image
}
}
return conf
}
export enum LoadingStatus {
Empty,
Loading,
Loaded,
}
export enum NetworkType {
Mainnet,
Testnet,
}
export enum ConfigSource {
MainnetCosmosDirectory = "https://chains.cosmos.directory",
TestnetCosmosDirectory = "https://chains.testcosmos.directory",
Local = 'local',
}
export const useDashboard = defineStore('dashboard', {
state: () => {
const fav = JSON.parse(localStorage.getItem('favorite') || '["cosmoshub", "osmosis"]')
return {
status: LoadingStatus.Empty,
source: ConfigSource.MainnetCosmosDirectory,
networkType: NetworkType.Mainnet,
favorite: fav as string[],
chains: {} as Record<string, ChainConfig>,
}
},
getters: {
length() : number {
return Object.keys(this.chains).length
}
},
actions: {
initial() {
this.loadingFromLocal()
// this.loadingFromRegistry()
},
async loadingFromRegistry() {
if(this.status === LoadingStatus.Empty) {
this.status = LoadingStatus.Loading
get(this.source).then((res)=> {
res.chains.forEach(( x: DirectoryChain ) => {
this.chains[x.chain_name] = fromDirectory(x)
});
this.status = LoadingStatus.Loaded
})
}
},
async loadingFromLocal() {
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<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()
}
}
})
+19
View File
@@ -0,0 +1,19 @@
import { defineStore } from "pinia";
import { useBlockchain } from "./useBlockchain";
export const useDistributionStore = defineStore('distributionStore', {
state: () => {
return {
}
},
getters: {
blockchain() {
return useBlockchain()
}
},
actions: {
async fetchCommunityPool() {
return this.blockchain.rpc.communityPool()
}
}
})
+160
View File
@@ -0,0 +1,160 @@
import { defineStore } from "pinia";
import { useBlockchain } from "./useBlockchain";
import Long from "long";
import numeral from "numeral";
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration'
import relativeTime from 'dayjs/plugin/relativeTime'
import updateLocale from 'dayjs/plugin/updateLocale'
import utc from 'dayjs/plugin/utc'
import localeData from 'dayjs/plugin/localeData'
import { useStakingStore } from "./useStakingStore";
import { fromBech32, toBase64, toHex } from "@cosmjs/encoding";
import { consensusPubkeyToHexAddress, operatorAddressToAccount } from "@/libs";
dayjs.extend(localeData)
dayjs.extend(duration)
dayjs.extend(relativeTime)
dayjs.extend(updateLocale)
dayjs.extend(utc)
dayjs.updateLocale('en', {
relativeTime: {
future: 'in %s',
past: '%s ago',
s: '%ds',
m: '1m',
mm: '%dm',
h: 'an hour',
hh: '%d hours',
d: 'a day',
dd: '%d days',
M: 'a month',
MM: '%d months',
y: 'a year',
yy: '%d years',
},
})
export const useFormatter = defineStore('formatter', {
state: () => {
return {
}
},
getters: {
blockchain() {
return useBlockchain()
},
staking() {
return useStakingStore()
}
},
actions: {
formatTokenAmount(token: {denom: string, amount: string;}) {
return this.formatToken(token, false)
},
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 = 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) {
let unit = {exponent: 6, denom: ''}
// find the max exponent for display
conf.denom_units.forEach(x => {
if(x.exponent >= unit.exponent) {
unit = x
}
})
if(unit && unit.exponent > 0) {
amount = amount / Math.pow(10, unit.exponent || 6)
denom = unit.denom.toUpperCase()
}
}
return `${numeral(amount).format(fmt)} ${withDenom ? denom: ''}`
}
return '-'
},
formatTokens(tokens?: { denom: string, amount: string;}[], withDenom = true, fmt='0.0a') : string {
if(!tokens) return ''
return tokens.map(x => this.formatToken(x, withDenom, fmt)).join(', ')
},
calculateBondedRatio(pool: {bonded_tokens: string, not_bonded_tokens: string}|undefined) {
if(pool && pool.bonded_tokens) {
const b = Number(pool.bonded_tokens)
const nb = Number(pool.not_bonded_tokens)
const p = b/(b+nb)
console.log(b, nb, p, pool)
return numeral(p).format('0.[00]%')
}
return '-'
},
validator(address: Uint8Array) {
const txt = toHex(address).toUpperCase()
const validator = this.staking.validators.find(x => consensusPubkeyToHexAddress(x.consensusPubkey) === txt)
return validator?.description?.moniker
},
calculatePercent(input?: string, total?: string|number ) {
if(!input || !total) return '0'
const percent = Number(input)/Number(total)
return numeral(percent).format("0.[00]%")
},
formatDecimalToPercent(decimal: string) {
return numeral(decimal).format('0.[00]%')
},
formatCommissionRate(v?: string) {
console.log(v)
if(!v) return '-'
const rate = Number(v) / Number("1000000000000000000")
return this.percent(rate)
},
percent(decimal?: string|number) {
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 ''
if (format === 'long') {
return dayjs(time).format('YYYY-MM-DD HH:mm')
}
if (format === 'date') {
return dayjs(time).format('YYYY-MM-DD')
}
if (format === 'time') {
return dayjs(time).format('HH:mm:ss')
}
if (format === 'from') {
return dayjs(time).fromNow()
}
if (format === 'to') {
return dayjs(time).toNow()
}
return dayjs(time).format('YYYY-MM-DD HH:mm:ss')
},
messages(msgs: {typeUrl: string}[]) {
if(msgs) {
const sum: Record<string, number> = msgs.map(msg => {
return msg.typeUrl.substring(msg.typeUrl.lastIndexOf('.') + 1).replace('Msg', '')
}).reduce((s, c) => {
const sh: Record<string, number> = s
if (sh[c]) {
sh[c] += 1
} else {
sh[c] = 1
}
return sh
}, {})
const output: string[] = []
Object.keys(sum).forEach(k => {
output.push(sum[k] > 1 ? `${k}×${sum[k]}` : k)
})
return output.join(', ')
}
},
}
})
+46
View File
@@ -0,0 +1,46 @@
import { defineStore } from "pinia";
import { useBlockchain } from "./useBlockchain";
import { createGovRestClientForChain } from "@/libs/client";
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
import { QueryClient } from "@cosmjs/stargate";
export const useGovStore = defineStore('govStore', {
state: () => {
return {
params: {
deposit: {} as DepositParams,
voting: {} as VotingParams,
tally: {} as TallyParams,
}
}
},
getters: {
blockchain() {
return useBlockchain()
}
},
actions: {
initial() {
this.fetchParams()
},
async fetchProposals( proposalStatus: ProposalStatus, pagination?: PageRequest ) {
const param = {
proposalStatus,
voter: '',
depositor: '',
pagination,
}
const proposals = await this.blockchain.rpc.proposals(proposalStatus, '', '')
console.log(proposals)
return proposals
},
async fetchParams() {
// this.blockchain.rpc.govParam().then(x => {
// this.params.deposit = x.deposit
// })
},
async fetchTally(proposalId: number) {
return this.blockchain.rpc.tally(proposalId)
}
}
})
+27
View File
@@ -0,0 +1,27 @@
import { defineStore } from "pinia";
import { useBlockchain } from "./useBlockchain";
export const useMintStore = defineStore('mintStore', {
state: () => {
return {
inflation: "0",
}
},
getters: {
blockchain() {
return useBlockchain()
}
},
actions: {
initial() {
this.fetchInflation()
},
async fetchInflation() {
this.blockchain.rpc.inflation().then(x => {
this.inflation = x.inflation
}).catch(() => {
this.inflation = "0"
})
}
}
})
+64
View File
@@ -0,0 +1,64 @@
import { defineStore } from "pinia";
import { useBlockchain } from "./useBlockchain";
import { get } from "@/libs/http";
export const useStakingStore = defineStore('stakingStore', {
state: () => {
return {
validators: [] as Validator[],
params: {} as QueryParamsResponse,
pool: {} as Pool | undefined,
}
},
getters: {
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.blockchain.rpc.stakingParams()
if(response.params) this.params = response.params
return this.params
},
async fetchPool() {
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 fetchValidatorDelegation(validatorAddr: string, delegatorAddr: string) {
return (await this.blockchain.rpc.validatorDelegation(validatorAddr, delegatorAddr)).delegationResponse
},
async fetchValidators(status: string) {
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
})
}
}
})