Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI 678 lm table #621

Merged
merged 13 commits into from
Aug 5, 2021
7 changes: 1 addition & 6 deletions src/components/_global/BalTable/BalTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,7 @@
'p-6 bg-white dark:bg-gray-850 border-t dark:border-gray-900'
]"
>
<slot
v-if="column.totalsCell"
v-bind="dataItem"
:name="column.totalsCell"
>
</slot>
<slot v-if="column.totalsCell" :name="column.totalsCell"> </slot>
</td>
</tr>
</tbody>
Expand Down
69 changes: 19 additions & 50 deletions src/components/tables/LMTable/LMTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
</div>
</template>
<template v-slot:poolNameCell="pool">
{{ console.log('steble', pool) }}
<div class="px-6 py-4">
<TokenPills
:tokens="orderedPoolTokens(pool)"
Expand Down Expand Up @@ -79,27 +78,25 @@

<script lang="ts">
import { ColumnDefinition } from '@/components/_global/BalTable/BalTable.vue';
import { WeeklyDistributions } from '@/pages/LiquidityMining.vue';
import { TokenTotal, WeeklyDistributions } from '@/pages/LiquidityMining.vue';
import TokenPills from '../PoolsTable/TokenPills/TokenPills.vue';
import {
DecoratedPoolWithShares,
PoolToken
} from '@/services/balancer/subgraph/types';
import { getAddress } from '@ethersproject/address';
import { computed, defineComponent, PropType, toRefs } from 'vue';
import { computed, defineComponent, PropType, Ref, toRefs } from 'vue';
import { useI18n } from 'vue-i18n';
import useTokens from '@/composables/useTokens';
import useNumbers from '@/composables/useNumbers';
import { sum } from 'lodash';
import { useStore } from 'vuex';
import useDarkMode from '@/composables/useDarkMode';

function getWeekName(week: string) {
const parts = week.split('_');
return `Week ${parts[1]}`;
}

type TokenTotal = { token: string; total: number };

export default defineComponent({
components: {
TokenPills
Expand All @@ -110,21 +107,23 @@ export default defineComponent({
required: true
},
poolMetadata: {
type: Object,
required: true
type: Object
},
isLoading: {
type: Boolean
},
totals: {
type: Object as PropType<Ref<Record<string, TokenTotal[]>>>,
required: true
}
},
setup(props) {
const { t } = useI18n();
const { weeks, poolMetadata } = toRefs(props);
const { tokens } = useTokens();
const { tokens, priceFor } = useTokens();
const { fNum } = useNumbers();
const store = useStore();
const { darkMode } = useDarkMode();

const prices = computed(() => store.state.market.prices);
const data = computed(() => {
if (!poolMetadata.value) return [];
return poolMetadata.value[0].pools.map(pool => ({
Expand Down Expand Up @@ -173,34 +172,6 @@ export default defineComponent({
];
});

const totals = computed(() => {
// map tracking a list of token totals for each week
const weeklyTotals: Record<string, TokenTotal[]> = {};
for (const week of weeks.value) {
// map tracking totals for each token
const tokenTotals: Record<string, TokenTotal> = {};
// this will be an array of pools with their token distributions,
// we just want the values, not the pool id
const distributions = Object.values(week.distributions);
for (const distribution of distributions) {
for (const allocation of distribution) {
if (!tokenTotals[allocation.tokenAddress]) {
tokenTotals[allocation.tokenAddress] = {
token: allocation.tokenAddress,
total: allocation.amount
};
continue;
} else {
tokenTotals[allocation.tokenAddress].total =
tokenTotals[allocation.tokenAddress].total + allocation.amount;
}
}
}
weeklyTotals[week.week] = Object.values(tokenTotals);
}
return weeklyTotals;
});

function orderedPoolTokens(pool: DecoratedPoolWithShares): PoolToken[] {
if (pool.poolType === 'Stable') return pool.tokens;

Expand All @@ -215,27 +186,25 @@ export default defineComponent({
}

function calculatePricesFor(totals: TokenTotal[]) {
let totalUsd = 0;
let totalFiat = 0;
for (const total of totals) {
const usdValue =
prices.value[total.token.toLowerCase()].price * total.total;
totalUsd = totalUsd + usdValue;
const usdValue = priceFor(getAddress(total.token)) * total.total;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to be picky but could we call this fiatValue

Suggested change
const usdValue = priceFor(getAddress(total.token)) * total.total;
const fiatValue = priceFor(getAddress(total.token)) * total.total;

totalFiat = totalFiat + usdValue;
}
return totalUsd;
return totalFiat;
}

return {
columns,
data,
orderedTokenAddressesFor,
orderedPoolTokens,
fNum,
getAddress,
totals,
tokens,
prices,
calculatePricesFor,
console
columns,
data,
tokens,
priceFor,
darkMode
};
}
});
Expand Down
3 changes: 3 additions & 0 deletions src/composables/queries/usePoolsQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export default function usePoolsQuery(
tokensList_contains: tokenList.value
}
};
if (filterOptions?.poolIds?.value.length) {
queryArgs.where.id_in = filterOptions.poolIds.value;
}

const pools = await balancerSubgraphService.pools.get(queryArgs);

Expand Down
169 changes: 120 additions & 49 deletions src/pages/LiquidityMining.vue
Original file line number Diff line number Diff line change
@@ -1,45 +1,56 @@
<template>
<div class="lg:container lg:mx-auto pt-10 md:pt-12">
<div class="px-4">
<h3>{{ title }}</h3>
<span class="text-black-600">{{ description }}</span>
<LMTable
:is-loading="isLoadingPools || isLoadingPoolsIdle"
:poolMetadata="pools"
:weeks="distributions"
/>
<div class="mt-20">
<h4 class="font-bold">About liquidity mining</h4>
<p class="mt-2">
Liquidity mining is a form of ‘yield farming’ used to align incentives
between a protocol and its community. Typically, DeFi protocols
distribute tokens to users who perform certain activities which help
the network grow.<br /><br />
The Balancer protocol via the community Ballers, has allocated BAL
tokens to go to liquidity providers in certain eligible pools (as
listed in the tables above). Tokens are distributed proportional to
the amount of liquidity each address contributed, relative to the
total liquidity in eligible Balancer pools. BAL tokens represent an
ownership stake in the platform and voting rights in community
governance. In addition, other protocols like Polygon are further
incentivizing liquidity by also distributing MATIC tokens to Balancer
liquidity providers in certain pools on Polygon.
</p>
<div class="mt-6">
<h5>Liquidity mining details</h5>
<ul class="mt-2 pl-8 list-disc">
<li class="mt-2">
You’re eligible to receive token distributions if you add
liquidity to any of the eligible pools.
</li>
<li class="mt-2">
Liquidity mining weeks start and end at 00:00 UTC on Mondays.
</li>
<li class="mt-2">
BAL allocations and pool eligibility are determined weekly by the
community ‘Ballers’.
</li>
</ul>
<div>
<div class="lm-banner h-48 flex items-center justify-center flex-col">
<span class="text-white font-semibold"
>Week {{ currentWeek }} Liquidity mining incentives</span
>
<h1 class="font-body mt-2 text-white font-semi bold">
~{{ fNum(currentWeekTotalFiat, 'usd') }}
</h1>
</div>
<div class="lg:container lg:mx-auto pt-10 md:pt-12">
<div class="px-4">
<h3>{{ title }}</h3>
<span class="text-black-600">{{ description }}</span>
<LMTable
:is-loading="isLoadingPools || isLoadingPoolsIdle"
:poolMetadata="pools"
:weeks="weeks"
:totals="totals"
/>
<div class="mt-20">
<h4 class="font-bold">About liquidity mining</h4>
<p class="mt-2">
Liquidity mining is a form of ‘yield farming’ used to align
incentives between a protocol and its community. Typically, DeFi
protocols distribute tokens to users who perform certain activities
which help the network grow.<br /><br />
The Balancer protocol via the community Ballers, has allocated BAL
tokens to go to liquidity providers in certain eligible pools (as
listed in the tables above). Tokens are distributed proportional to
the amount of liquidity each address contributed, relative to the
total liquidity in eligible Balancer pools. BAL tokens represent an
ownership stake in the platform and voting rights in community
governance. In addition, other protocols like Polygon are further
incentivizing liquidity by also distributing MATIC tokens to
Balancer liquidity providers in certain pools on Polygon.
</p>
<div class="mt-6">
<h5>Liquidity mining details</h5>
<ul class="mt-2 pl-8 list-disc">
<li class="mt-2">
You’re eligible to receive token distributions if you add
liquidity to any of the eligible pools.
</li>
<li class="mt-2">
Liquidity mining weeks start and end at 00:00 UTC on Mondays.
</li>
<li class="mt-2">
BAL allocations and pool eligibility are determined weekly by
the community ‘Ballers’.
</li>
</ul>
</div>
</div>
</div>
</div>
Expand All @@ -51,19 +62,24 @@ import { computed, defineComponent } from 'vue';
import LMTable from '@/components/tables/LMTable/LMTable.vue';
import LiquidityMiningDistributions from '@/lib/utils/liquidityMining/MultiTokenLiquidityMining.json';
import usePoolsQuery from '@/composables/queries/usePoolsQuery';
import { flatten, takeRight, uniq } from 'lodash';
import { flatten, last, takeRight, uniq } from 'lodash';
import { Network } from '@/constants/network';
import useNumbers from '@/composables/useNumbers';
import useTokens from '@/composables/useTokens';
import { getAddress } from '@ethersproject/address';

type TokenDistribution = {
tokenAddress: string;
amount: number;
}[];
};

type PoolDistribution = {
chainId: number;
pools: Record<string, TokenDistribution[]>;
};

export type TokenTotal = { token: string; total: number };

type LiquidityMiningDistribution = Record<string, PoolDistribution[]>;

const NETWORK = process.env.VUE_APP_NETWORK || '1';
Expand All @@ -78,19 +94,62 @@ export default defineComponent({
LMTable
},
setup() {
const { fNum } = useNumbers();
const { priceFor } = useTokens();

// seperate variable to type the JSON
const weeks = (LiquidityMiningDistributions as unknown) as LiquidityMiningDistribution;
const weeksJSON = (LiquidityMiningDistributions as unknown) as LiquidityMiningDistribution;

const totals = computed(() => {
// map tracking a list of token totals for each week
const weeklyTotals: Record<string, TokenTotal[]> = {};
for (const week of weeks) {
// map tracking totals for each token
const tokenTotals: Record<string, TokenTotal> = {};
// this will be an array of pools with their token distributions,
// we just want the values, not the pool id
const distributions = Object.values(week.distributions);
for (const distribution of distributions) {
for (const allocation of distribution) {
if (!tokenTotals[allocation.tokenAddress]) {
tokenTotals[allocation.tokenAddress] = {
token: allocation.tokenAddress,
total: allocation.amount
};
continue;
} else {
tokenTotals[allocation.tokenAddress].total =
tokenTotals[allocation.tokenAddress].total + allocation.amount;
}
}
}
weeklyTotals[week.week] = Object.values(tokenTotals);
}
return weeklyTotals;
});

const currentWeekTotalFiat = computed(() => {
let totalFiat = 0;
const currentWeek = last(Object.values(totals.value));
if (currentWeek) {
for (const total of currentWeek) {
const fiatValue = priceFor(getAddress(total.token)) * total.total;
totalFiat = totalFiat + fiatValue;
}
}
return totalFiat;
});

// only concerned with past 3 weeks
const distributions = takeRight(Object.keys(weeks), 3).map(week => ({
const weeks = takeRight(Object.keys(weeksJSON), 3).map(week => ({
week: week,
distributions: weeks[week]
distributions: weeksJSON[week]
.filter(d => d.chainId === Number(NETWORK))
.map(d => d.pools)[0]
}));

const poolIds = computed(() =>
uniq(flatten(distributions.map(d => Object.keys(d.distributions))))
uniq(flatten(weeks.map(d => Object.keys(d.distributions))))
);

// there shouldn't be too many pools for the LM distribution for each chain
Expand Down Expand Up @@ -125,14 +184,26 @@ export default defineComponent({
return '';
});

const currentWeek = computed(() => last(last(weeks)?.week.split('_')));

return {
distributions,
weeks,
pools,
title,
totals,
description,
isLoadingPools,
isLoadingPoolsIdle
isLoadingPoolsIdle,
currentWeek,
currentWeekTotalFiat,
fNum
};
}
});
</script>

<style>
.lm-banner {
background-image: url('/images/backgrounds/bg-header.svg');
}
</style>