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';
const walletStore = useWalletStore();
walletStore.$subscribe((m, s) => {
console.log(m, s);
});
</script>
<template>
@ -10,7 +13,10 @@ const walletStore = useWalletStore();
walletStore.currentAddress
}}</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
>
</div>

View File

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

View File

@ -73,7 +73,13 @@ export interface RequestRegistry extends AbstractRegistry {
}>;
distribution_validator_slashes: Request<PaginatedSlashes>;
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_params: Request<{

View File

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

View File

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

View File

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

View File

@ -14,6 +14,7 @@ import ProposalListItem from '@/components/ProposalListItem.vue';
const blockchain = useBlockchain();
const store = useIndexModule();
const walletStore = useWalletStore()
const format = useFormatter()
const coinInfo = computed(() => {
return store.coinInfo;
@ -21,9 +22,9 @@ const coinInfo = computed(() => {
onMounted(() => {
store.loadDashboard();
walletStore.loadMyAsset()
});
const format = useFormatter();
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
blockchain.$subscribe((m, s) => {
@ -32,6 +33,7 @@ blockchain.$subscribe((m, s) => {
['chainName', 'endpoint'].includes(m.events.key)
) {
store.loadDashboard();
walletStore.loadMyAsset()
}
});
function shortName(name: string, id: string) {
@ -65,7 +67,20 @@ const comLinks = [
];
// 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>
<template>
@ -226,10 +241,18 @@ const comLinks = [
<span v-if="walletStore.currentAddress" class="float-right font-light text-sm">More</span>
</div>
<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">2</div>
<div class="bg-base-100">1</div>
<div class="bg-base-100">2</div>
<div class="bg-base-100">{{ format.formatToken(walletStore.balanceOfStakingToken) }}
<br><span :class="color">{{ format.showChanges(change) }}<small>%</small></span>{{ format.tokenValue(walletStore.balanceOfStakingToken) }}</div>
<div class="bg-base-100">{{ format.formatToken(walletStore.stakingAmount) }}
<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>
@ -244,7 +267,8 @@ const comLinks = [
<route>
{
meta: {
i18n: 'dashboard'
i18n: 'dashboard',
order: 1,
}
}
</route>

View File

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

View File

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

View File

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

View File

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

View File

@ -56,10 +56,10 @@ export const useBaseStore = defineStore('baseStore', {
},
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) {
return this.blockchain.rpc.getBaseValidatorsetLatest();
return this.blockchain.rpc.getBaseValidatorsetLatest(offset);
},
async fetchBlock(height?: number) {
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) },
icon: { icon: 'mdi-chevron-right', size: '22' },
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[],
favoriteMap: favMap as Record<string, boolean>,
chains: {} as Record<string, ChainConfig>,
prices: {},
coingecko: {} as Record<string, {coinId: string, exponent: number, symbol: string}>,
};
},
getters: {
@ -246,6 +248,28 @@ export const useDashboard = defineStore('dashboard', {
this.loadingFromLocal();
// 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() {
if (this.status === LoadingStatus.Empty) {
this.status = LoadingStatus.Loading;
@ -274,12 +298,14 @@ export const useDashboard = defineStore('dashboard', {
for (let i = 0; i < this.favorite.length; i++) {
if (!blockchain.chainName && this.chains[this.favorite[i]]) {
blockchain.setCurrent(this.favorite[i]);
break
}
}
if (!blockchain.chainName) {
const [first] = Object.keys(this.chains);
blockchain.setCurrent(first);
}
this.loadingPrices()
}
},
setConfigSource(newSource: ConfigSource) {

View File

@ -11,7 +11,8 @@ import { useStakingStore } from './useStakingStore';
import { fromBase64, fromBech32, fromHex, toHex } from '@cosmjs/encoding';
import { consensusPubkeyToHexAddress } from '@/libs';
import { useBankStore } from './useBankStore';
import type { DenomTrace } from '@/types';
import type { Coin, DenomTrace } from '@/types';
import { useDashboard } from './useDashboard';
dayjs.extend(localeData);
dayjs.extend(duration);
@ -52,6 +53,9 @@ export const useFormatter = defineStore('formatter', {
useBank() {
return useBankStore();
},
dashboard() {
return useDashboard()
}
},
actions: {
async fetchDenomTrace(denom: string) {
@ -64,6 +68,33 @@ export const useFormatter = defineStore('formatter', {
}
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 }) {
return this.formatToken(token, false);
},
@ -136,6 +167,7 @@ export const useFormatter = defineStore('formatter', {
);
return validator?.description?.moniker;
},
// find validator by operator address
validatorFromBech32(address: string) {
if (!address) return address;
const validator = this.staking.validators.find(

View File

@ -1,10 +1,22 @@
import { defineStore } from 'pinia';
import { useBlockchain } from './useBlockchain';
import { fromBech32, toBech32 } from '@cosmjs/encoding';
import type {
Delegation,
Coin,
UnbondingResponses,
DelegatorRewards,
} from '@/types';
import { useStakingStore } from './useStakingStore';
export const useWalletStore = defineStore('walletStore', {
state: () => {
return {};
return {
balances: [] as Coin[],
delegations: [] as Delegation[],
unbonding: [] as UnbondingResponses[],
rewards: {} as DelegatorRewards,
};
},
getters: {
blockchain() {
@ -17,6 +29,42 @@ export const useWalletStore = defineStore('walletStore', {
const connected = JSON.parse(localStorage.getItem(key) || '{}');
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() {
if (!this.connectedWallet?.cosmosAddress) return '';
const { prefix, data } = fromBech32(this.connectedWallet.cosmosAddress);
@ -25,6 +73,27 @@ export const useWalletStore = defineStore('walletStore', {
},
},
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() {
return this.blockchain.rpc.getBankBalances(this.currentAddress);
},