Skip to content

Commit

Permalink
feat: Top accounts (#841)
Browse files Browse the repository at this point in the history
Co-authored-by: Michele F. <michele-franchi@users.noreply.github.com>
  • Loading branch information
janmichek and michele-franchi authored Jul 15, 2024
1 parent 3db3aab commit bd6cbeb
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 0 deletions.
7 changes: 7 additions & 0 deletions cypress/e2e/app/topAccounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
describe('top accounts', () => {
it('should display top accounts', () => {
cy.visit('/accounts')

cy.get('.top-accounts-table').should('be.visible')
})
})
4 changes: 4 additions & 0 deletions src/components/TheNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const menuOptions = ref([{
name: 'Blockchain',
isActive: false,
submenu: [
{
name: 'Accounts',
path: '/accounts',
},
{
name: 'Transactions',
path: '/transactions',
Expand Down
24 changes: 24 additions & 0 deletions src/components/TopAccountsPanel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<app-panel>
<top-accounts-table
:top-accounts="topAccounts"
class="u-hidden-mobile"/>
<top-accounts-table-condensed
:top-accounts="topAccounts"
class="u-hidden-desktop"/>
</app-panel>
</template>

<script setup>
import { useTopAccountsStore } from '@/stores/topAccounts'
import TopAccountsTable from '@/components/TopAccountsTable'
import TopAccountsTableCondensed from '@/components/TopAccountsTableCondensed'
const { topAccounts } = storeToRefs(useTopAccountsStore())
const { fetchTopAccounts } = useTopAccountsStore()
await useAsyncData(async() => {
await fetchTopAccounts()
return true
})
</script>
60 changes: 60 additions & 0 deletions src/components/TopAccountsTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<template>
<table class="top-accounts-table">
<tr>
<th>
Rank
<hint-tooltip>
{{ topAccountsHints.rank }}
</hint-tooltip>
</th>
<th>
Account
<hint-tooltip>
{{ topAccountsHints.account }}
</hint-tooltip>
</th>
<th>
Balance
<hint-tooltip>
{{ topAccountsHints.balance }}
</hint-tooltip>
</th>
<th>
% Of Circulating
<hint-tooltip>
{{ topAccountsHints.percentage }}
</hint-tooltip>
</th>
</tr>
<tr
v-for="account in topAccounts"
:key="account.account">
<td>{{ account.rank }}.</td>

<td>
<app-link
:to="`/accounts/${account.account}`">
{{ account.account }}
</app-link>
</td>
<td>{{ account.balance }}</td>
<td>{{ account.percentage }} %</td>
</tr>
</table>
</template>
<script setup>
import { topAccountsHints } from '@/utils/hints/topAccountsHints'
defineProps({
topAccounts: {
type: Array,
required: true,
},
})
</script>

<style scoped>
.top-accounts-table {
margin-bottom: var(--space-4);
}
</style>
98 changes: 98 additions & 0 deletions src/components/TopAccountsTableCondensed.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<template>
<div>
<table
v-for="account in topAccounts"
:key="account.account"
class="top-accounts-table-condensed__table">
<tbody>
<tr class="top-accounts-table-condensed__row">
<th class="top-accounts-table-condensed__header">
<app-tooltip>
Rank
<template #tooltip>
{{ topAccountsHints.rank }}
</template>
</app-tooltip>
</th>
<td class="top-accounts-table-condensed__data">
{{ account.rank }}.
</td>
</tr>
<tr class="top-accounts-table-condensed__row">
<th class="top-accounts-table-condensed__header">
<app-tooltip>
Account
<template #tooltip>
{{ topAccountsHints.account }}
</template>
</app-tooltip>
</th>
<td class="top-accounts-table-condensed__data">
<app-link
:to="`/accounts/${account.account}`">
{{ account.account }}
</app-link>
</td>
</tr>
<tr class="top-accounts-table-condensed__row">
<th class="top-accounts-table-condensed__header">
<app-tooltip>
Balance
<template #tooltip>
{{ topAccountsHints.balance }}
</template>
</app-tooltip>
</th>
<td class="top-accounts-table-condensed__data">
{{ account.balance }}
</td>
</tr>
<tr class="top-accounts-table-condensed__row">
<th class="top-accounts-table-condensed__header">
<app-tooltip>
% Of Circulating
<template #tooltip>
{{ topAccountsHints.percentage }}
</template>
</app-tooltip>
</th>
<td class="top-accounts-table-condensed__data">
{{ account.percentage }} %
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup>
import { topAccountsHints } from '@/utils/hints/topAccountsHints'
defineProps({
topAccounts: {
type: Array,
required: true,
},
})
</script>

<style scoped>
.top-accounts-table-condensed {
&__table {
padding: 0 var(--space-1) var(--space-7);
margin-bottom: var(--space-5);
}
&__header {
border-bottom: 1px solid var(--color-midnight-25);
}
&__row:last-of-type &__header {
border-bottom: 0;
}
&__data {
text-align: right;
}
}
</style>
21 changes: 21 additions & 0 deletions src/pages/accounts/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<Head>
<Title>Accounts</Title>
</Head>

<page-header>
Top 100 Accounts
<template #tooltip>
{{ topAccountsHints.topAccounts }}
</template>
</page-header>
<top-accounts-panel v-if="!isLoading"/>
<loader-panel v-else/>
</template>

<script setup>
import PageHeader from '@/components/PageHeader'
import { topAccountsHints } from '@/utils/hints/topAccountsHints'
const { isLoading } = useLoading()
</script>
33 changes: 33 additions & 0 deletions src/stores/topAccounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useBlockchainStatsStore } from '@/stores/blockchainStats'

export const useTopAccountsStore = defineStore('topAccounts', () => {
const axios = useAxios()
const rawTopAccounts = ref(null)
const { MIDDLEWARE_URL } = useRuntimeConfig().public
const blockchainStatsStore = useBlockchainStatsStore()
const { fetchTotalStats } = useBlockchainStatsStore()

const topAccounts = computed(() =>
rawTopAccounts.value && blockchainStatsStore.totalTokenSupply
? adaptTopAccounts(rawTopAccounts.value, blockchainStatsStore.totalTokenSupply)
: null,
)

function fetchTopAccounts() {
return Promise.allSettled([
fetchAccounts(),
fetchTotalStats(),
])
}

async function fetchAccounts() {
rawTopAccounts.value = null
const { data } = await axios.get(`${MIDDLEWARE_URL}/v3/wealth`)
rawTopAccounts.value = data
}

return {
topAccounts,
fetchTopAccounts,
}
})
13 changes: 13 additions & 0 deletions src/utils/adapters.js
Original file line number Diff line number Diff line change
Expand Up @@ -621,3 +621,16 @@ export function adaptVerificationResult(verificationStatus) {
status: translateCodeToStatus(verificationStatus.statusCode),
}
}

export function adaptTopAccounts(topAccounts, distribution) {
return topAccounts
.slice(0, 100)
.map((account, index) => {
return {
rank: index + 1,
account: account.account,
balance: formatAePrice(formatAettosToAe(account.balance)),
percentage: (formatAettosToAe(account.balance) * 100 / distribution).toFixed(4),
}
})
}
7 changes: 7 additions & 0 deletions src/utils/hints/topAccountsHints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const topAccountsHints = {
topAccounts: 'The top accounts are the accounts with the highest balance of AE coins.',
rank: 'Rank of the account in the top accounts list.',
balance: 'Amount of AE tokens held by the account.',
account: 'Account address.',
percentage: 'Percentage of the circulating supply of AE coins that the account holds.',
}

0 comments on commit bd6cbeb

Please sign in to comment.