Skip to content

Commit

Permalink
aprs: fixing bal emissions
Browse files Browse the repository at this point in the history
  • Loading branch information
gmbronco committed Sep 13, 2023
1 parent 770d336 commit e523986
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,14 @@ export class GaugeAprService implements PoolAprService {
const aprItemId = `${pool.id}-${rewardTokenDefinition.symbol}-apr`;
const aprRangeId = `${pool.id}-bal-apr-range`;

// We need gauge's workingSupply and the pool BPT price
const gaugeEmissionsUsd = rewardTokenValuePerYear;

// Only 40% of LP token staked accrue emissions, totalSupply = workingSupply * 2.5
const workingSupply = (parseFloat(preferredStaking.gauge.workingSupply) + 0.4) / 0.4;
const bptPrice = this.tokenService.getPriceForToken(tokenPrices, pool.address);
const workingSupplyUsd = workingSupply * bptPrice;

let balApr = 0;
if (workingSupply > 0) {
balApr = gaugeEmissionsUsd / workingSupplyUsd;
balApr = rewardTokenValuePerYear / workingSupplyUsd;
}

const itemData = {
Expand Down
95 changes: 95 additions & 0 deletions modules/pool/lib/staking/bal-emissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* Weekly Bal emissions are fixed / year according to:
* https://docs.google.com/spreadsheets/d/1FY0gi596YWBOTeu_mrxhWcdF74SwKMNhmu0qJVgs0KI/edit#gid=0
*
* Using regular numbers for simplicity assuming frontend use only.
*
* Calculation source
* https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/liquidity-mining/contracts/BalancerTokenAdmin.sol
*/

export const INITIAL_RATE = 145000;
export const START_EPOCH_TIME = 1648465251;
const RATE_REDUCTION_TIME = 365 * 86400;
const RATE_REDUCTION_COEFFICIENT = 2 ** (1 / 4);

/**
* Weekly BAL emissions
*
* @param currentTimestamp used to get the epoch
* @returns BAL emitted in a week
*/
export const weekly = (
currentTimestamp: number = Math.round(new Date().getTime() / 1000)
): number => {
const miningEpoch = Math.floor(
(currentTimestamp - START_EPOCH_TIME) / RATE_REDUCTION_TIME
);

const rate = INITIAL_RATE * RATE_REDUCTION_COEFFICIENT ** -miningEpoch;

return rate;
};

/**
* Total BAL emitted in epoch (1 year)
*
* @param epoch starting from 0 for the first year of emissions
* @returns BAL emitted in epoch
*/
export const total = (epoch: number): number => {
const weeklyRate = INITIAL_RATE * RATE_REDUCTION_COEFFICIENT ** -epoch;
const dailyRate = weeklyRate / 7;

return dailyRate * 365;
};

/**
* Total BAL emitted between two timestamps
*
* @param start starting timestamp
* @param end ending timestamp
* @returns BAL emitted in period
*/
export const between = (start: number, end: number): number => {
if (start < START_EPOCH_TIME) {
throw 'start timestamp before emission schedule deployment';
}
if (end < start) {
throw 'cannot finish before starting';
}

let totalEmissions = 0;

const startingEpoch = Math.floor(
(start - START_EPOCH_TIME) / RATE_REDUCTION_TIME
);
const endingEpoch = Math.floor(
(end - START_EPOCH_TIME) / RATE_REDUCTION_TIME
);

for (
let currentEpoch = startingEpoch;
currentEpoch <= endingEpoch;
currentEpoch++
) {
totalEmissions += total(currentEpoch);
}

// Subtract what isn't emmited within the time range
const startingEpochEnd =
START_EPOCH_TIME + RATE_REDUCTION_TIME * (startingEpoch + 1);
const endingEpochStart = START_EPOCH_TIME + RATE_REDUCTION_TIME * endingEpoch;

const secondsInStartingEpoch = startingEpochEnd - start;
const secondsInEndingEpoch = end - endingEpochStart;

totalEmissions -=
(total(startingEpoch) * (RATE_REDUCTION_TIME - secondsInStartingEpoch)) /
RATE_REDUCTION_TIME;
totalEmissions -=
(total(endingEpoch) * (RATE_REDUCTION_TIME - secondsInEndingEpoch)) /
RATE_REDUCTION_TIME;

return totalEmissions;
};
11 changes: 4 additions & 7 deletions modules/pool/lib/staking/gauge-staking.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import childChainGaugeV1Abi from './abi/ChildChainGaugeV1.json';
import moment from 'moment';
import { BigNumber, formatFixed } from '@ethersproject/bignumber';
import { Multicaller3 } from '../../../web3/multicaller3';
import * as emissions from './bal-emissions';
import _ from 'lodash';

interface GaugeRate {
Expand Down Expand Up @@ -235,26 +236,22 @@ export class GaugeStakingService implements PoolStakingService {
* @returns
*/
private async getMainnetGaugeRates(gaugeAddresses: string[]): Promise<{ [gaugeAddress: string]: GaugeRate }> {
const now = Math.round(new Date().getTime() / 1000);
const version = 2; // On Mainnet BAL is always distributed directly to gauges
const { gaugeControllerAddress } = networkContext.data;
const abi = [
'function inflation_rate() view returns (uint256)',
'function working_supply() view returns (uint256)',
'function gauge_relative_weight(address) view returns (uint256)'
];
const multicall = new Multicaller3(abi);

// On mainnet inflation rate is the same for all the gauges
multicall.call('inflation_rate', gaugeAddresses[0], 'inflation_rate', [], true);

gaugeAddresses.forEach((address) => {
multicall.call(`${address}.weight`, gaugeControllerAddress!, 'gauge_relative_weight', [address], true);
multicall.call(`${address}.workingSupply`, address, 'working_supply', [], true);
});

const multicallResult = await multicall.execute() as { inflation_rate: BigNumber } & { [address: string]: { weight: BigNumber, workingSupply: BigNumber } };
const { inflation_rate, ...gaugeData } = multicallResult;
const inflationRate = Number(formatUnits(inflation_rate!));
const gaugeData = await multicall.execute() as { [address: string]: { weight: BigNumber, workingSupply: BigNumber } };
const inflationRate = emissions.weekly(now) / 604800; // BAL inflation rate per second

const weightedRates = _.mapValues(gaugeData, ({ weight }) => {
if (weight.eq(0)) return 0;
Expand Down

0 comments on commit e523986

Please sign in to comment.