This commit is contained in:
Alisa | Side.one 2023-05-11 01:45:05 +08:00
commit 39f5cf5337
16 changed files with 247 additions and 48 deletions

View File

@ -2,6 +2,9 @@
import { useWalletStore } from '@/stores'; import { useWalletStore } from '@/stores';
const walletStore = useWalletStore(); const walletStore = useWalletStore();
walletStore.$subscribe((m, s) => {
console.log(m, s);
});
</script> </script>
<template> <template>
@ -10,7 +13,10 @@ const walletStore = useWalletStore();
walletStore.currentAddress walletStore.currentAddress
}}</span> }}</span>
<label v-else for="PingConnectWallet" class="btn btn-sm ml-4 ping-connect-btn" <label
v-else
for="PingConnectWallet"
class="btn btn-sm ml-4 ping-connect-btn"
>Connect Wallet</label >Connect Wallet</label
> >
</div> </div>

View File

@ -193,13 +193,17 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
async getBaseNodeInfo() { async getBaseNodeInfo() {
return this.request(this.registry.base_tendermint_node_info, {}); return this.request(this.registry.base_tendermint_node_info, {});
} }
async getBaseValidatorsetAt(height: string | number) { async getBaseValidatorsetAt(height: string | number, offset: number) {
console.log("offset", offset)
const query = `?pagination.limit=100&pagination.offset=${offset}`
return this.request(this.registry.base_tendermint_validatorsets_height, { return this.request(this.registry.base_tendermint_validatorsets_height, {
height, height,
}); }, query);
} }
async getBaseValidatorsetLatest() { async getBaseValidatorsetLatest(offset: number) {
return this.request(this.registry.base_tendermint_validatorsets_latest, {}); console.log(offset)
const query = `?pagination.limit=100&pagination.offset=${offset}`
return this.request(this.registry.base_tendermint_validatorsets_latest, {}, query);
} }
// tx // tx
async getTxsBySender(sender: string) { async getTxsBySender(sender: string) {

View File

@ -73,7 +73,13 @@ export interface RequestRegistry extends AbstractRegistry {
}>; }>;
distribution_validator_slashes: Request<PaginatedSlashes>; distribution_validator_slashes: Request<PaginatedSlashes>;
distribution_community_pool: Request<{ pool: Coin[] }>; distribution_community_pool: Request<{ pool: Coin[] }>;
distribution_delegator_rewards: Request<any>; distribution_delegator_rewards: Request<{
rewards: {
validator_address: string,
reward: Coin[]
}[],
total: Coin[]
}>;
mint_inflation: Request<{ inflation: string }>; mint_inflation: Request<{ inflation: string }>;
mint_params: Request<{ mint_params: Request<{

View File

@ -97,7 +97,8 @@ const format = useFormatter();
<route> <route>
{ {
meta: { meta: {
i18n: 'blocks' i18n: 'blocks',
order: 5
} }
} }
</route> </route>

View File

@ -47,7 +47,8 @@ const changeTab = (val: '2' | '3' | '4') => {
<route> <route>
{ {
meta: { meta: {
i18n: 'governance' i18n: 'governance',
order: 2
} }
} }
</route> </route>

View File

@ -70,7 +70,8 @@ function color(v: string) {
<route> <route>
{ {
meta: { meta: {
i18n: 'ibc' i18n: 'ibc',
order: 8
} }
} }
</route> </route>

View File

@ -14,6 +14,7 @@ import ProposalListItem from '@/components/ProposalListItem.vue';
const blockchain = useBlockchain(); const blockchain = useBlockchain();
const store = useIndexModule(); const store = useIndexModule();
const walletStore = useWalletStore() const walletStore = useWalletStore()
const format = useFormatter()
const coinInfo = computed(() => { const coinInfo = computed(() => {
return store.coinInfo; return store.coinInfo;
@ -21,9 +22,9 @@ const coinInfo = computed(() => {
onMounted(() => { onMounted(() => {
store.loadDashboard(); store.loadDashboard();
walletStore.loadMyAsset()
}); });
const format = useFormatter();
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]); const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
blockchain.$subscribe((m, s) => { blockchain.$subscribe((m, s) => {
@ -32,6 +33,7 @@ blockchain.$subscribe((m, s) => {
['chainName', 'endpoint'].includes(m.events.key) ['chainName', 'endpoint'].includes(m.events.key)
) { ) {
store.loadDashboard(); store.loadDashboard();
walletStore.loadMyAsset()
} }
}); });
function shortName(name: string, id: string) { function shortName(name: string, id: string) {
@ -65,7 +67,20 @@ const comLinks = [
]; ];
// wallet box // wallet box
const change = computed(() => {
const token = walletStore.balanceOfStakingToken
return token? format.priceChanges(token.denom) : 0
})
const color= computed(() => {
switch(true) {
case change.value > 0:
return "text-green-600"
case change.value === 0:
return "text-grey-500"
case change.value < 0:
return "text-red-600"
}
})
</script> </script>
<template> <template>
@ -226,10 +241,18 @@ const comLinks = [
<span v-if="walletStore.currentAddress" class="float-right font-light text-sm">More</span> <span v-if="walletStore.currentAddress" class="float-right font-light text-sm">More</span>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-4 auto-cols-auto gap-4 px-4 pb-8 py-4"> <div class="grid grid-cols-1 md:grid-cols-4 auto-cols-auto gap-4 px-4 pb-8 py-4">
<div class="bg-base-100">1</div> <div class="bg-base-100">{{ format.formatToken(walletStore.balanceOfStakingToken) }}
<div class="bg-base-100">2</div> <br><span :class="color">{{ format.showChanges(change) }}<small>%</small></span>{{ format.tokenValue(walletStore.balanceOfStakingToken) }}</div>
<div class="bg-base-100">1</div> <div class="bg-base-100">{{ format.formatToken(walletStore.stakingAmount) }}
<div class="bg-base-100">2</div> <br> {{ format.tokenValue(walletStore.stakingAmount) }}</div>
<div class="bg-base-100">{{ format.formatToken(walletStore.rewardAmount) }}</div>
<div class="bg-base-100">{{ format.formatToken(walletStore.unbondingAmount) }}</div>
</div>
<div>
<div v-for="v in walletStore.delegations">
{{ v }}
</div>
</div> </div>
<div> <div>
@ -244,7 +267,8 @@ const comLinks = [
<route> <route>
{ {
meta: { meta: {
i18n: 'dashboard' i18n: 'dashboard',
order: 1,
} }
} }
</route> </route>

View File

@ -55,7 +55,8 @@ onMounted(() => {
<route> <route>
{ {
meta: { meta: {
i18n: 'parameters' i18n: 'parameters',
order: 10
} }
} }
</route> </route>

View File

@ -16,43 +16,68 @@ const tab = ref('active');
const unbondList = ref([] as Validator[]); const unbondList = ref([] as Validator[]);
const base = useBaseStore(); const base = useBaseStore();
onMounted(() => { onMounted(() => {
fetchChange(0);
staking.fetchInacitveValdiators().then((x) => { staking.fetchInacitveValdiators().then((x) => {
unbondList.value = x; unbondList.value = x;
}); });
}); });
function fetchChange(offset: number) { async function fetchChange() {
const base = useBaseStore(); console.log('fetch changes')
const diff = 86400000 / base.blocktime; let page = 0
// base.fetchAbciInfo().then(h => {
// // console.log('block:', h) let height = Number(base.latest?.block?.header?.height || 0)
// base.fetchValidatorByHeight(h.lastBlockHeight, offset).then(x => { if (height > 14400) {
// x.validators.forEach(v => { height -= 14400
// if(v.pubkey) latest.value[pubkeyToAddress(v.pubkey.algorithm, v.pubkey.data)] = Number(v.votingPower) } else {
// }) height = 1
// }) }
// const height = Number(h.lastBlockHeight) - diff // voting power in 24h ago
// base.fetchValidatorByHeight(height > 0 ? height : 1, offset).then(old => { while(page < staking.validators.length && height > 0) {
// old.validators.forEach(v => { await base.fetchValidatorByHeight(height, page).then(x => {
// if(v.pubkey) yesterday.value[pubkeyToAddress(v.pubkey.algorithm, v.pubkey.data)] = Number(v.votingPower) x.validators.forEach(v => {
// }) yesterday.value[v.pub_key.key] = Number(v.voting_power)
// // console.log(Object.keys(yesterday.value).map(x => x.toUpperCase())) })
// }) })
// }) page += 100
}
page = 0
// voting power for now
while(page < staking.validators.length) {
await base.fetchLatestValidators(page).then(x => {
x.validators.forEach(v => {
latest.value[v.pub_key.key] = Number(v.voting_power)
})
})
page += 100
}
} }
fetchChange();
const changes = computed(() => {
const changes = {} as Record<string, number>
Object.keys(latest.value).forEach(k => {
const l = latest.value[k] || 0
const y = yesterday.value[k] || 0
changes[k] = l - y
})
return changes
})
const change24 = (key: Key) => { const change24 = (key: Key) => {
const txt = key.key; const txt = key.key;
const n: number = latest.value[txt]; // const n: number = latest.value[txt];
const o: number = yesterday.value[txt]; // const o: number = yesterday.value[txt];
return n > 0 && o > 0 ? n - o : 0; // // console.log( txt, n, o)
// return n > 0 && o > 0 ? n - o : 0;
return changes.value[txt]
}; };
const change24Text = (key?: Key) => { const change24Text = (key?: Key) => {
if (!key) return ''; if (!key) return '';
const v = change24(key); const v = change24(key);
return v !== 0 ? format.numberAndSign(v) : ''; return v !== 0 ? format.showChanges(v) : '';
}; };
const change24Color = (key?: Key) => { const change24Color = (key?: Key) => {
@ -308,7 +333,8 @@ const rank = function (position: number) {
<route> <route>
{ {
meta: { meta: {
i18n: 'staking' i18n: 'staking',
order: 3
} }
} }
</route> </route>

View File

@ -162,7 +162,8 @@ watchEffect(() => {
<route> <route>
{ {
meta: { meta: {
i18n: 'uptime' i18n: 'uptime',
order: 8
} }
} }
</route> </route>

View File

@ -12,6 +12,7 @@ const dashboard = useDashboard();
dashboard.$subscribe((mutation, state) => { dashboard.$subscribe((mutation, state) => {
localStorage.setItem('favorite', JSON.stringify(state.favorite)); localStorage.setItem('favorite', JSON.stringify(state.favorite));
dashboard.loadingPrices()
}); });
const keywords = ref(''); const keywords = ref('');
const chains = computed(() => { const chains = computed(() => {
@ -23,7 +24,6 @@ const chains = computed(() => {
return Object.values(dashboard.chains); return Object.values(dashboard.chains);
} }
}); });
const chain = useBlockchain();
</script> </script>
<template> <template>
<div class=""> <div class="">

View File

@ -56,10 +56,10 @@ export const useBaseStore = defineStore('baseStore', {
}, },
async fetchValidatorByHeight(height?: number, offset = 0) { async fetchValidatorByHeight(height?: number, offset = 0) {
return this.blockchain.rpc.getBaseValidatorsetAt(String(height)); return this.blockchain.rpc.getBaseValidatorsetAt(String(height), offset);
}, },
async fetchLatestValidators(offset = 0) { async fetchLatestValidators(offset = 0) {
return this.blockchain.rpc.getBaseValidatorsetLatest(); return this.blockchain.rpc.getBaseValidatorsetLatest(offset);
}, },
async fetchBlock(height?: number) { async fetchBlock(height?: number) {
return this.blockchain.rpc.getBaseBlockAt(String(height)); return this.blockchain.rpc.getBaseBlockAt(String(height));

View File

@ -68,8 +68,9 @@ export const useBlockchain = defineStore('blockchain', {
to: { path: x.path.replace(':chain', this.chainName) }, to: { path: x.path.replace(':chain', this.chainName) },
icon: { icon: 'mdi-chevron-right', size: '22' }, icon: { icon: 'mdi-chevron-right', size: '22' },
i18n: true, i18n: true,
order: Number(x.meta.order || 100)
})) }))
.sort((a, b) => a.to.path.length - b.to.path.length), .sort((a, b) => a.order - b.order),
}, },
]; ];
} }

View File

@ -234,6 +234,8 @@ export const useDashboard = defineStore('dashboard', {
favorite: fav as string[], favorite: fav as string[],
favoriteMap: favMap as Record<string, boolean>, favoriteMap: favMap as Record<string, boolean>,
chains: {} as Record<string, ChainConfig>, chains: {} as Record<string, ChainConfig>,
prices: {},
coingecko: {} as Record<string, {coinId: string, exponent: number, symbol: string}>,
}; };
}, },
getters: { getters: {
@ -246,6 +248,28 @@ export const useDashboard = defineStore('dashboard', {
this.loadingFromLocal(); this.loadingFromLocal();
// this.loadingFromRegistry() // this.loadingFromRegistry()
}, },
loadingPrices() {
const coinIds = [] as string[]
Object.keys(this.favoriteMap).forEach(k => {
if(this.chains[k]) this.chains[k].assets.forEach(a => {
if(a.coingecko_id !== undefined) {
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() { async loadingFromRegistry() {
if (this.status === LoadingStatus.Empty) { if (this.status === LoadingStatus.Empty) {
this.status = LoadingStatus.Loading; this.status = LoadingStatus.Loading;
@ -274,12 +298,14 @@ export const useDashboard = defineStore('dashboard', {
for (let i = 0; i < this.favorite.length; i++) { for (let i = 0; i < this.favorite.length; i++) {
if (!blockchain.chainName && this.chains[this.favorite[i]]) { if (!blockchain.chainName && this.chains[this.favorite[i]]) {
blockchain.setCurrent(this.favorite[i]); blockchain.setCurrent(this.favorite[i]);
break
} }
} }
if (!blockchain.chainName) { if (!blockchain.chainName) {
const [first] = Object.keys(this.chains); const [first] = Object.keys(this.chains);
blockchain.setCurrent(first); blockchain.setCurrent(first);
} }
this.loadingPrices()
} }
}, },
setConfigSource(newSource: ConfigSource) { setConfigSource(newSource: ConfigSource) {

View File

@ -11,7 +11,8 @@ import { useStakingStore } from './useStakingStore';
import { fromBase64, fromBech32, fromHex, toHex } from '@cosmjs/encoding'; import { fromBase64, fromBech32, fromHex, toHex } from '@cosmjs/encoding';
import { consensusPubkeyToHexAddress } from '@/libs'; import { consensusPubkeyToHexAddress } from '@/libs';
import { useBankStore } from './useBankStore'; import { useBankStore } from './useBankStore';
import type { DenomTrace } from '@/types'; import type { Coin, DenomTrace } from '@/types';
import { useDashboard } from './useDashboard';
dayjs.extend(localeData); dayjs.extend(localeData);
dayjs.extend(duration); dayjs.extend(duration);
@ -52,6 +53,9 @@ export const useFormatter = defineStore('formatter', {
useBank() { useBank() {
return useBankStore(); return useBankStore();
}, },
dashboard() {
return useDashboard()
}
}, },
actions: { actions: {
async fetchDenomTrace(denom: string) { async fetchDenomTrace(denom: string) {
@ -64,6 +68,33 @@ export const useFormatter = defineStore('formatter', {
} }
return trace; return trace;
}, },
priceInfo(denom: string) {
const id = this.dashboard.coingecko[denom]?.coinId
const prices = this.dashboard.prices[id]
return prices
},
price(denom: string, currency = "usd") {
const info = this.priceInfo(denom);
console.log("info", info, denom)
return info? info[currency]||0 : 0
},
priceChanges(denom: string, currency="usd"): number {
const info = this.priceInfo(denom);
return info? info[`${currency}_24h_change`]||0 : 0
},
showChanges(v: number) {
return numeral(v).format("+0,0.[00]")
},
tokenValue(token: Coin) {
// find the symbol,
const symbol = this.dashboard.coingecko[token.denom]?.symbol || ""
// convert denomation to to symbol
const exponent = this.dashboard.coingecko[symbol.toLowerCase()]?.exponent || 0
// cacualte amount of symbol
const amount = Number(token.amount) / (10 ** exponent)
const value = amount * this.price(token.denom)
return numeral(value).format('0,0.[00]')
},
formatTokenAmount(token: { denom: string; amount: string }) { formatTokenAmount(token: { denom: string; amount: string }) {
return this.formatToken(token, false); return this.formatToken(token, false);
}, },
@ -136,6 +167,7 @@ export const useFormatter = defineStore('formatter', {
); );
return validator?.description?.moniker; return validator?.description?.moniker;
}, },
// find validator by operator address
validatorFromBech32(address: string) { validatorFromBech32(address: string) {
if (!address) return address; if (!address) return address;
const validator = this.staking.validators.find( const validator = this.staking.validators.find(

View File

@ -1,10 +1,22 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { useBlockchain } from './useBlockchain'; import { useBlockchain } from './useBlockchain';
import { fromBech32, toBech32 } from '@cosmjs/encoding'; import { fromBech32, toBech32 } from '@cosmjs/encoding';
import type {
Delegation,
Coin,
UnbondingResponses,
DelegatorRewards,
} from '@/types';
import { useStakingStore } from './useStakingStore';
export const useWalletStore = defineStore('walletStore', { export const useWalletStore = defineStore('walletStore', {
state: () => { state: () => {
return {}; return {
balances: [] as Coin[],
delegations: [] as Delegation[],
unbonding: [] as UnbondingResponses[],
rewards: {} as DelegatorRewards,
};
}, },
getters: { getters: {
blockchain() { blockchain() {
@ -17,6 +29,42 @@ export const useWalletStore = defineStore('walletStore', {
const connected = JSON.parse(localStorage.getItem(key) || '{}'); const connected = JSON.parse(localStorage.getItem(key) || '{}');
return connected; return connected;
}, },
balanceOfStakingToken(): Coin {
const stakingStore = useStakingStore();
return (
this.balances.find(
(x) => x.denom === stakingStore.params.bond_denom
) || { amount: '0', denom: stakingStore.params.bond_denom }
);
},
stakingAmount() {
let amt = 0;
let denom = '';
this.delegations.forEach((i) => {
amt += Number(i.balance.amount);
denom = i.balance.denom;
});
return { amount: String(amt), denom };
},
rewardAmount() {
const stakingStore = useStakingStore();
const reward = this.rewards.total?.find(
(x) => x.denom === stakingStore.params.bond_denom
);
return reward || { amount: '0', denom: stakingStore.params.bond_denom };
},
unbondingAmount() {
let amt = 0;
let denom = '';
this.unbonding.forEach((i) => {
i.entries.forEach((e) => {
amt += Number(e.balance);
});
});
const stakingStore = useStakingStore();
return { amount: String(amt), denom: stakingStore.params.bond_denom };
},
currentAddress() { currentAddress() {
if (!this.connectedWallet?.cosmosAddress) return ''; if (!this.connectedWallet?.cosmosAddress) return '';
const { prefix, data } = fromBech32(this.connectedWallet.cosmosAddress); const { prefix, data } = fromBech32(this.connectedWallet.cosmosAddress);
@ -25,6 +73,27 @@ export const useWalletStore = defineStore('walletStore', {
}, },
}, },
actions: { actions: {
async loadMyAsset() {
if (!this.currentAddress) return;
this.blockchain.rpc.getBankBalances(this.currentAddress).then((x) => {
this.balances = x.balances;
});
this.blockchain.rpc
.getStakingDelegations(this.currentAddress)
.then((x) => {
this.delegations = x.delegation_responses;
});
this.blockchain.rpc
.getStakingDelegatorUnbonding(this.currentAddress)
.then((x) => {
this.unbonding = x.unbonding_responses;
});
this.blockchain.rpc
.getDistributionDelegatorRewards(this.currentAddress)
.then((x) => {
this.rewards = x;
});
},
myBalance() { myBalance() {
return this.blockchain.rpc.getBankBalances(this.currentAddress); return this.blockchain.rpc.getBankBalances(this.currentAddress);
}, },