forked from cerc-io/cosmos-explorer
add basic portfolio
This commit is contained in:
parent
d956fdd9c7
commit
e38001e5fe
@ -20,3 +20,9 @@ const expenseRationChartConfig = computed(() =>
|
|||||||
:series="series"
|
:series="series"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'DonetChart',
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
@ -2,15 +2,17 @@
|
|||||||
import { CosmosRestClient } from '@/libs/client';
|
import { CosmosRestClient } from '@/libs/client';
|
||||||
import type { Coin, Delegation } from '@/types';
|
import type { Coin, Delegation } from '@/types';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { scanLocalKeys } from './utils';
|
import { scanLocalKeys, type AccountEntry } from './utils';
|
||||||
import { fromBech32, toBase64 } from '@cosmjs/encoding';
|
import { fromBech32, toBase64 } from '@cosmjs/encoding';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useFormatter } from '@/stores';
|
import { useFormatter } from '@/stores';
|
||||||
|
import DonutChart from '@/components/charts/DonutChart.vue';
|
||||||
|
|
||||||
const format = useFormatter()
|
const format = useFormatter()
|
||||||
const conf = ref(JSON.parse(localStorage.getItem("imported-addresses") || "{}") as Record<string, AccountEntry[]>)
|
const conf = ref(JSON.parse(localStorage.getItem("imported-addresses") || "{}") as Record<string, AccountEntry[]>)
|
||||||
const balances = ref({} as Record<string, Coin[]>)
|
const balances = ref({} as Record<string, Coin[]>)
|
||||||
const delegations = ref({} as Record<string, Delegation[]>)
|
const delegations = ref({} as Record<string, Delegation[]>)
|
||||||
|
const tokenMeta = ref({} as Record<string, AccountEntry>)
|
||||||
|
|
||||||
scanLocalKeys().forEach(wallet => {
|
scanLocalKeys().forEach(wallet => {
|
||||||
const { data } = fromBech32(wallet.cosmosAddress)
|
const { data } = fromBech32(wallet.cosmosAddress)
|
||||||
@ -22,10 +24,17 @@ scanLocalKeys().forEach(wallet => {
|
|||||||
if (x.endpoint) {
|
if (x.endpoint) {
|
||||||
const client = CosmosRestClient.newDefault(x.endpoint)
|
const client = CosmosRestClient.newDefault(x.endpoint)
|
||||||
client.getBankBalances(x.address).then(res => {
|
client.getBankBalances(x.address).then(res => {
|
||||||
balances.value[x.address] = res.balances.filter(x => x.denom.length < 10)
|
const bal = res.balances.filter(x => x.denom.length < 10)
|
||||||
|
balances.value[x.address] = bal
|
||||||
|
bal.forEach(b => {
|
||||||
|
tokenMeta.value[b.denom] = x
|
||||||
|
})
|
||||||
})
|
})
|
||||||
client.getStakingDelegations(x.address).then(res => {
|
client.getStakingDelegations(x.address).then(res => {
|
||||||
delegations.value[x.address] = res.delegation_responses
|
delegations.value[x.address] = res.delegation_responses
|
||||||
|
res.delegation_responses.forEach(del => {
|
||||||
|
tokenMeta.value[del.balance.denom] = x
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -35,19 +44,25 @@ const tokenValues = computed(() => {
|
|||||||
const values = {} as Record<string, number>
|
const values = {} as Record<string, number>
|
||||||
Object.values(balances.value).forEach(b => {
|
Object.values(balances.value).forEach(b => {
|
||||||
b.forEach(coin => {
|
b.forEach(coin => {
|
||||||
if(values[coin.denom]) {
|
const v = format.tokenValueNumber(coin)
|
||||||
values[coin.denom] += format.tokenValueNumber(coin)
|
if(v) {
|
||||||
} else {
|
if (values[coin.denom]) {
|
||||||
values[coin.denom] = format.tokenValueNumber(coin)
|
values[coin.denom] += v
|
||||||
|
} else {
|
||||||
|
values[coin.denom] = v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Object.values(delegations.value).forEach(b => {
|
Object.values(delegations.value).forEach(b => {
|
||||||
b.forEach(d => {
|
b.forEach(d => {
|
||||||
if(values[d.balance.denom]) {
|
const v = format.tokenValueNumber(d.balance)
|
||||||
values[d.balance.denom] += format.tokenValueNumber(d.balance)
|
if(v) {
|
||||||
} else {
|
if (values[d.balance.denom]) {
|
||||||
values[d.balance.denom] = format.tokenValueNumber(d.balance)
|
values[d.balance.denom] += v
|
||||||
|
} else {
|
||||||
|
values[d.balance.denom] = v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -58,6 +73,20 @@ const totalValue = computed(() => {
|
|||||||
return Object.values(tokenValues.value).reduce((a, s) => (a + s), 0)
|
return Object.values(tokenValues.value).reduce((a, s) => (a + s), 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const tokenList = computed(() => {
|
||||||
|
const list = [] as { denom: string, value: number, logo: string, chainName: string, percentage: number }[]
|
||||||
|
Object.keys(tokenValues.value).map(x => {
|
||||||
|
list.push({
|
||||||
|
denom: x,
|
||||||
|
value: tokenValues.value[x],
|
||||||
|
chainName: tokenMeta.value[x]?.chainName,
|
||||||
|
logo: tokenMeta.value[x]?.logo,
|
||||||
|
percentage: tokenValues.value[x] / totalValue.value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return list.filter(x => x.value > 0).sort((a, b) => b.value - a.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
@ -83,5 +112,33 @@ const totalValue = computed(() => {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="bg-base-100">
|
||||||
|
<DonutChart :series="Object.values(tokenValues)" :labels="Object.keys(tokenValues)"/>
|
||||||
|
<table class="table w-100">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Token</th>
|
||||||
|
<th class="text-right">Value</th>
|
||||||
|
<th class="text-right">Percent</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="x in tokenList">
|
||||||
|
<td class="capitalize">
|
||||||
|
<div class="flex">
|
||||||
|
<div class="avatar">
|
||||||
|
<div class="mask mask-squircle w-6 h-6 mr-2">
|
||||||
|
<img :src="x.logo" :alt="x.chainName" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ x.chainName }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">${{ format.formatNumber(x.value) }}</td>
|
||||||
|
<td class="text-right">{{ format.percent(x.percentage) }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -216,6 +216,9 @@ export const useFormatter = defineStore('formatter', {
|
|||||||
percent(decimal?: string | number) {
|
percent(decimal?: string | number) {
|
||||||
return decimal ? numeral(decimal).format('0.[00]%') : '-';
|
return decimal ? numeral(decimal).format('0.[00]%') : '-';
|
||||||
},
|
},
|
||||||
|
formatNumber(input: number, fmt = '0.[00]') {
|
||||||
|
return numeral(input).format(fmt)
|
||||||
|
},
|
||||||
numberAndSign(input: number, fmt = '+0,0') {
|
numberAndSign(input: number, fmt = '+0,0') {
|
||||||
return numeral(input).format(fmt);
|
return numeral(input).format(fmt);
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user