Skip to content

Commit

Permalink
rerun week 1, add readme
Browse files Browse the repository at this point in the history
  • Loading branch information
mikemcdonald committed Jun 9, 2020
1 parent 7a693dd commit 7e19e76
Show file tree
Hide file tree
Showing 716 changed files with 4,160,056 additions and 1,438,462 deletions.
11 changes: 11 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"printWidth": 80,
"tabWidth": 4,
"singleQuote": true,
"bracketSpacing": true,
"trailingComma": "es5",
"semi": true,
"newline-before-return": true,
"no-duplicate-variable": [true, "check-parameters"],
"no-var-keyword": true
}
675 changes: 675 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,26 @@ Set of scripts to calculate weekly BAL liquidity mining distributions

## Requirements

An archive node is needed to run the scripts because historical balance snapshots are needed. A "starting-point" archive node can also be used that will only archive at x block onwards. Note this still probably requires 750G+ of disk space.

## Usage

```
node index.js --week 1 --startBlock 10176690 --endBlock 10221761
```

This will run run all historical calculations by block. Using an infura endpoint this may take upwards of 18 hours. For a local archive node, the sync time is roughly 10 minutes. Progress bars with estimates are shown during the sync. Reports will be saved in the folder for the given week specified

```
node sum.js --week 1 --startBlock 10176690 --endBlock 10221761
```

After all reports are generated, `sum.js` will create a final tally of user address to BAL received. This is stored in the report week folder at `_totals.json`

## Weekly distributions

145,000 BAL will be distributed directly to addresses on a weekly basis. Due to block gas limits, the tx's to batch transfer BAL will need to be split up across blocks. In order to prevent favoring certain accounts, the block hash of the `endBlock` will be the starting point and addresses will be ordered alphabetically for distributions.

## BAL Redirections

In case smart contracts which cannot receive BAL tokens are specified, owners of those smart contracts can choose to redirect BAL tokens to a new address. In order to submit a redirection request, submit a pull request to update `redirect.json` using `"fromAddress" : "toAddress"` along with some sort of ownership proof. Please reach out to the Balancer team if you need assistance.
200 changes: 126 additions & 74 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ const utils = require('./utils');
const poolAbi = require('./abi/BPool.json');
const tokenAbi = require('./abi/BToken.json');

const web3 = new Web3(new Web3.providers.WebsocketProvider(`ws://localhost:8546`));
const web3 = new Web3(
new Web3.providers.WebsocketProvider(`ws://localhost:8546`)
);

BigNumber.config({
EXPONENTIAL_AT: [-100, 100],
Expand All @@ -20,18 +22,26 @@ function bnum(val) {
return new BigNumber(val.toString());
}

function getFeeFactor(feePercentage) {
return Math.exp(-Math.pow(feePercentage / 2, 2));
}

if (!argv.startBlock || !argv.endBlock || !argv.week) {
console.log('Usage: node index.js --week 1 --startBlock 10131642 --endBlock 10156690')
process.exit()
console.log(
'Usage: node index.js --week 1 --startBlock 10131642 --endBlock 10156690'
);
process.exit();
}

const END_BLOCK = argv.endBlock; // Closest block to reference time at end of week
const START_BLOCK = argv.startBlock; // Closest block to reference time at beginning of week
const WEEK = argv.week;
const WEEK = argv.week; // Week for mining distributions. Ex: 1

const BAL_PER_WEEK = bnum(145000);
const BLOCKS_PER_SNAPSHOT = 64;
const BAL_PER_SNAPSHOT = BAL_PER_WEEK.div(bnum(Math.ceil((END_BLOCK - START_BLOCK)/64))); // Ceiling because it includes end block
const BAL_PER_SNAPSHOT = BAL_PER_WEEK.div(
bnum(Math.ceil((END_BLOCK - START_BLOCK) / 64))
); // Ceiling because it includes end block

async function getRewardsAtBlock(i, pools, prices, poolProgress) {
let totalBalancerLiquidity = bnum(0);
Expand All @@ -41,10 +51,8 @@ async function getRewardsAtBlock(i, pools, prices, poolProgress) {
let userPools = {};
let userLiquidity = {};


poolProgress.update(0, {task: `Block ${i} Progress`})
poolProgress.update(0, { task: `Block ${i} Progress` });
for (const pool of pools) {

let poolAddress = pool.id;

// Check if at least two tokens have a price
Expand All @@ -58,14 +66,14 @@ async function getRewardsAtBlock(i, pools, prices, poolProgress) {

for (const t of pool.tokensList) {
if (prices[t] !== undefined && prices[t].length > 0) {
nTokensHavePrice ++;
if(nTokensHavePrice>1){
nTokensHavePrice++;
if (nTokensHavePrice > 1) {
atLeastTwoTokensHavePrice = true;
break;
}
}
}
}

if (!atLeastTwoTokensHavePrice) {
poolProgress.increment(1);
continue;
Expand All @@ -79,7 +87,7 @@ async function getRewardsAtBlock(i, pools, prices, poolProgress) {
continue;
}

let shareHolders = pool.shares.flatMap(a => a.userAddress.id);
let shareHolders = pool.shares.flatMap((a) => a.userAddress.id);

let poolMarketCap = bnum(0);
let poolMarketCapFactor = bnum(0);
Expand All @@ -90,58 +98,101 @@ async function getRewardsAtBlock(i, pools, prices, poolProgress) {
continue;
}
let bToken = new web3.eth.Contract(tokenAbi, t);
let tokenBalanceWei = await bPool.methods.getBalance(t).call(undefined, i);
let tokenBalanceWei = await bPool.methods
.getBalance(t)
.call(undefined, i);
let tokenDecimals = await bToken.methods.decimals().call();

let closestPrice = prices[t].reduce((a, b) => {
return (Math.abs(b[0] - (block.timestamp * 1000)) < Math.abs(a[0] - (block.timestamp * 1000)) ? b : a);
return Math.abs(b[0] - block.timestamp * 1000) <
Math.abs(a[0] - block.timestamp * 1000)
? b
: a;
})[1];

let tokenBalance = utils.scale(tokenBalanceWei, -tokenDecimals);
let tokenMarketCap = tokenBalance.times(bnum(closestPrice)).dp(18);
poolMarketCap = poolMarketCap.plus(tokenMarketCap)
};
poolMarketCap = poolMarketCap.plus(tokenMarketCap);
}

let poolFee = await bPool.methods.getSwapFee().call(undefined, i);
poolFee = utils.scale(poolFee, -16); // -16 = -18 * 100 since it's in percentage terms

poolMarketCapFactor = bnum(getFeeFactor(poolFee)).times(poolMarketCap).dp(18);
totalBalancerLiquidity = totalBalancerLiquidity.plus(poolMarketCapFactor);
poolMarketCapFactor = bnum(getFeeFactor(poolFee))
.times(poolMarketCap)
.dp(18);
totalBalancerLiquidity = totalBalancerLiquidity.plus(
poolMarketCapFactor
);

let bptSupplyWei = await bPool.methods.totalSupply().call(undefined, i);
let bptSupply = utils.scale(bptSupplyWei, -18);


if (bptSupply.eq(bnum(0))) { // Private pool
if (bptSupply.eq(bnum(0))) {
// Private pool
if (userPools[pool.controller]) {
userPools[pool.controller].push({ pool: poolAddress, valueUSD: poolMarketCap.toString(), factorUSD: poolMarketCapFactor.toString() })
userPools[pool.controller].push({
pool: poolAddress,
valueUSD: poolMarketCap.toString(),
factorUSD: poolMarketCapFactor.toString(),
});
} else {
userPools[pool.controller] = [{ pool: poolAddress, valueUSD: poolMarketCap.toString(), factorUSD: poolMarketCapFactor.toString() }]
userPools[pool.controller] = [
{
pool: poolAddress,
valueUSD: poolMarketCap.toString(),
factorUSD: poolMarketCapFactor.toString(),
},
];
}

// Add this pool liquidity to total user liquidity
if (userLiquidity[pool.controller]) {
userLiquidity[pool.controller] = bnum(userLiquidity[pool.controller]).plus(poolMarketCapFactor).toString();
userLiquidity[pool.controller] = bnum(
userLiquidity[pool.controller]
)
.plus(poolMarketCapFactor)
.toString();
} else {
userLiquidity[pool.controller] = poolMarketCapFactor.toString();
}
} else { // Shared pool
} else {
// Shared pool
for (const holder of shareHolders) {
let userBalanceWei = await bPool.methods.balanceOf(holder).call(undefined, i);
let userBalanceWei = await bPool.methods
.balanceOf(holder)
.call(undefined, i);
let userBalance = utils.scale(userBalanceWei, -18);
let userPoolValue = (userBalance.div(bptSupply)).times(poolMarketCap).dp(18);
let userPoolValueFactor = (userBalance.div(bptSupply)).times(poolMarketCapFactor).dp(18);

let userPoolValue = userBalance
.div(bptSupply)
.times(poolMarketCap)
.dp(18);
let userPoolValueFactor = userBalance
.div(bptSupply)
.times(poolMarketCapFactor)
.dp(18);

if (userPools[holder]) {
userPools[holder].push({ pool: poolAddress, valueUSD: userPoolValue.toString(), factorUSD: userPoolValueFactor.toString() })
userPools[holder].push({
pool: poolAddress,
valueUSD: userPoolValue.toString(),
factorUSD: userPoolValueFactor.toString(),
});
} else {
userPools[holder] = [{ pool: poolAddress, valueUSD: userPoolValue.toString(), factorUSD: userPoolValueFactor.toString() }]
userPools[holder] = [
{
pool: poolAddress,
valueUSD: userPoolValue.toString(),
factorUSD: userPoolValueFactor.toString(),
},
];
}

// Add this pool liquidity to total user liquidity
if (userLiquidity[holder]) {
userLiquidity[holder] = bnum(userLiquidity[holder]).plus(userPoolValueFactor).toString();
userLiquidity[holder] = bnum(userLiquidity[holder])
.plus(userPoolValueFactor)
.toString();
} else {
userLiquidity[holder] = userPoolValueFactor.toString();
}
Expand All @@ -154,60 +205,61 @@ async function getRewardsAtBlock(i, pools, prices, poolProgress) {
// Final iteration across all users to calculate their BAL tokens for this block
let userBalReceived = {};
let balDistributedDoubleCheck = bnum(0);
for (const user in userLiquidity){
userBalReceived[user] = bnum(userLiquidity[user]).times(BAL_PER_SNAPSHOT).div(totalBalancerLiquidity);
for (const user in userLiquidity) {
userBalReceived[user] = bnum(userLiquidity[user])
.times(BAL_PER_SNAPSHOT)
.div(totalBalancerLiquidity);
}

return [userPools, userBalReceived];
}

(async function() {

const multibar = new cliProgress.MultiBar({
clearOnComplete: false,
format: '[{bar}] {percentage}% | ETA: {eta}s | {value}/{total} | {task}'

}, cliProgress.Presets.shades_classic);

(async function () {
const multibar = new cliProgress.MultiBar(
{
clearOnComplete: false,
format:
'[{bar}] {percentage}% | ETA: {eta}s | {value}/{total} | {task}',
},
cliProgress.Presets.shades_classic
);

!fs.existsSync(`./reports/${WEEK}/`) && fs.mkdirSync(`./reports/${WEEK}/`);

let startBlockTimestamp = (await web3.eth.getBlock(START_BLOCK)).timestamp;
let endBlockTimestamp = (await web3.eth.getBlock(END_BLOCK)).timestamp;

let pools = await utils.fetchPublicSwapPools();
const allTokens = pools.flatMap(a => a.tokensList);

const priceProgress = multibar.create(allTokens.length, 0, {task: "Fetching Prices"});
const prices = await utils.fetchTokenPrices(allTokens, startBlockTimestamp, endBlockTimestamp, priceProgress);

const poolProgress = multibar.create(pools.length, 0, {task: "Block Progress"});
const blockProgress = multibar.create(END_BLOCK - START_BLOCK, 0, {task: "Overall Progress"});

const allTokens = pools.flatMap((a) => a.tokensList);

const priceProgress = multibar.create(allTokens.length, 0, {
task: 'Fetching Prices',
});
const prices = await utils.fetchTokenPrices(
allTokens,
startBlockTimestamp,
endBlockTimestamp,
priceProgress
);

const poolProgress = multibar.create(pools.length, 0, {
task: 'Block Progress',
});
const blockProgress = multibar.create(END_BLOCK - START_BLOCK, 0, {
task: 'Overall Progress',
});

for (i = END_BLOCK; i > START_BLOCK; i -= BLOCKS_PER_SNAPSHOT) {

let blockRewards = await getRewardsAtBlock(i, pools, prices, poolProgress);
let blockRewards = await getRewardsAtBlock(
i,
pools,
prices,
poolProgress
);
let path = `/${WEEK}/${i}`;
utils.writeData(blockRewards, path)
utils.writeData(blockRewards, path);
blockProgress.increment(BLOCKS_PER_SNAPSHOT);
}

blockProgress.stop()


})()

function getFeeFactor(feePercentage){
return Math.exp(-Math.pow(feePercentage/2,2));
}










blockProgress.stop();
})();
40 changes: 24 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
{
"name": "bal-liquidity-mining",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"sum": "node sum.js",
"timestamps": "node timestamps.js"
},
"dependencies": {
"bignumber.js": "^9.0.0",
"cli-progress": "^3.8.2",
"dotenv": "^8.2.0",
"isomorphic-fetch": "^2.2.1",
"web3": "^1.2.7",
"yargs": "^15.3.1"
}
"name": "bal-liquidity-mining",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js",
"sum": "node sum.js",
"timestamps": "node timestamps.js"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
},
"dependencies": {
"bignumber.js": "^9.0.0",
"cli-progress": "^3.8.2",
"dotenv": "^8.2.0",
"husky": "^4.2.5",
"isomorphic-fetch": "^2.2.1",
"prettier": "^2.0.5",
"pretty-quick": "^2.0.1",
"web3": "^1.2.7",
"yargs": "^15.3.1"
}
}
3 changes: 3 additions & 0 deletions redirect.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"0x00000000000000000000000000000000deadbeef": "0x0000000000000000000000000000000000000000"
}
Loading

0 comments on commit 7e19e76

Please sign in to comment.