cosmos-explorer/src/stores/useDashboard.ts
2023-06-12 14:59:24 +08:00

351 lines
9.0 KiB
TypeScript

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;
coinType: string;
assets: Asset[];
themeColor?: string;
features?: string[]
endpoints: {
rest?: Endpoint[];
rpc?: Endpoint[];
grpc?: Endpoint[];
};
logo: string;
versions: {
application?: string;
cosmosSdk?: string;
tendermint?: string;
};
exponent: string;
excludes?: string;
providerChain: {
api: Endpoint[]
};
// keplr config
keplrFeatures?: string[],
keplrPriceStep?: {
low: number,
average: number,
high: number,
},
}
export interface LocalConfig {
addr_prefix: string;
alias: string;
api: string[] | Endpoint[];
provider_chain: {
api: string[] | Endpoint[]
}
assets: {
base: string;
coingecko_id: string;
exponent: string;
logo: string;
symbol: string;
}[];
chain_name: string;
coin_type: string;
logo: string;
theme_color?: string;
min_tx_fee: string;
rpc: string[] | Endpoint[];
sdk_version: string;
registry_name?: string;
features?: string[];
keplr_price_step?: {
low: number,
average: number,
high: number,
},
keplr_features: 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,
exponent: x.exponent,
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.coinType = lc.coin_type;
conf.prettyName = lc.registry_name || lc.chain_name;
conf.endpoints = {
rest: apiConverter(lc.api),
rpc: apiConverter(lc.rpc),
};
if(lc.provider_chain) {
conf.providerChain = {
api: apiConverter(lc.provider_chain.api)
}
}
conf.features = lc.features
conf.logo = lc.logo;
conf.keplrFeatures = lc.keplr_features;
conf.keplrPriceStep = lc.keplr_price_step;
conf.themeColor = lc.theme_color;
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 favMap = JSON.parse(
localStorage.getItem('favoriteMap') ||
'{"cosmos":true, "osmosis":true}'
);
return {
status: LoadingStatus.Empty,
source: ConfigSource.MainnetCosmosDirectory,
networkType: NetworkType.Mainnet,
favoriteMap: favMap as Record<string, boolean>,
chains: {} as Record<string, ChainConfig>,
prices: {} as Record<string, any>,
coingecko: {} as Record<string, {coinId: string, exponent: number, symbol: string}>,
};
},
getters: {
length(): number {
return Object.keys(this.chains).length;
},
},
actions: {
initial() {
this.loadingFromLocal();
// this.loadingFromRegistry()
},
loadingPrices() {
const coinIds = [] as string[]
const keys = Object.keys(this.chains) // load all blockchain
// Object.keys(this.favoriteMap) //only load favorite once it has too many chains
keys.forEach(k => {
if(this.chains[k]) this.chains[k].assets.forEach(a => {
if(a.coingecko_id !== undefined && a.coingecko_id.length > 0) {
coinIds.push(a.coingecko_id)
a.denom_units.forEach(u => {
this.coingecko[u.denom] = {
coinId: a.coingecko_id || '',
exponent: u.exponent,
symbol: a.symbol
}
})
}
})
})
const currencies = ['usd, cny'] // usd,cny,eur,jpy,krw,sgd,hkd
get(`https://api.coingecko.com/api/v3/simple/price?include_24hr_change=true&vs_currencies=${currencies.join(',')}&ids=${coinIds.join(",")}`).then(x => {
this.prices = x
})
},
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() {
if(window.location.hostname.search("testnet") > -1) {
this.networkType = NetworkType.Testnet
}
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();
const keys = Object.keys(this.favoriteMap)
for (let i = 0; i < keys.length; i++) {
if (!blockchain.chainName && this.chains[keys[i]] && this.favoriteMap[keys[i]]) {
blockchain.setCurrent(keys[i]);
break
}
}
if (!blockchain.chainName) {
const [first] = Object.keys(this.chains);
blockchain.setCurrent(first);
}
this.loadingPrices()
}
},
setConfigSource(newSource: ConfigSource) {
this.source = newSource;
this.initial();
},
},
});