Skip to content

Commit

Permalink
Vsdtoken [sd-vote-boost-twavp-vsdtoken] (#1380)
Browse files Browse the repository at this point in the history
* init

* use sampleStep

* voting power naming

* strat init

* compute vp

* min block + vp formula
  • Loading branch information
pierremarsotlyon1 authored Dec 28, 2023
1 parent b56b735 commit ee38f82
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/strategies/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ import * as gelatoStaking from './gelato-staking';
import * as erc4626AssetsOf from './erc4626-assets-of';
import * as sdVoteBoostTWAVPV2 from './sd-vote-boost-twavp-v2';
import * as sdVoteBoostTWAVPV3 from './sd-vote-boost-twavp-v3';
import * as sdVoteBoostTWAVPVsdToken from './sd-vote-boost-twavp-vsdtoken';
import * as friendTech from './friend-tech';
import * as moonbase from './moonbase';
import * as dssVestUnpaid from './dss-vest-unpaid';
Expand Down Expand Up @@ -776,6 +777,7 @@ const strategies = {
'friend-tech': friendTech,
'sd-vote-boost-twavp-v2': sdVoteBoostTWAVPV2,
'sd-vote-boost-twavp-v3': sdVoteBoostTWAVPV3,
'sd-vote-boost-twavp-vsdtoken': sdVoteBoostTWAVPVsdToken,
moonbase: moonbase,
'dss-vest-unpaid': dssVestUnpaid,
'dss-vest-balance-and-unpaid': dssVestBalanceAndUnpaid,
Expand Down
28 changes: 28 additions & 0 deletions src/strategies/sd-vote-boost-twavp-vsdtoken/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# sd-vote-boost-twavp-vsdtoken

This strategy is used by Stake DAO to vote with sdToken using Time Weighted Averaged Voting Power (TWAVP) system and adapted for veSDT boost delegation with possibility to whiteliste address to by pass TWAVP.

```
VotingPower(user) = veToken.balanceOf(liquidLocker) * (average.sdTokenGauge.working_balances(user) / sdTokenGauge.working_supply)
```

>_sampleSize: in days_
>_sampleStep: the number of block for `average` calculation (max 5)_
Here is an example of parameters:

```json
{
"veToken": "0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2",
"liquidLocker": "0x52f541764E6e90eeBc5c21Ff570De0e2D63766B6",
"sdTokenGauge": "0x7f50786A0b15723D741727882ee99a0BF34e3466",
"sdToken": "0xD1b5651E55D4CeeD36251c61c50C889B36F6abB5",
"pools": ["0xca0253a98d16e9c1e3614cafda19318ee69772d0"],
"symbol": "sdToken",
"decimals": 18,
"sampleSize": 10,
"sampleStep": 5,
"botAddress": "",
"whiteListedAddress": ["0x1c0D72a330F2768dAF718DEf8A19BAb019EEAd09"]
}
```
28 changes: 28 additions & 0 deletions src/strategies/sd-vote-boost-twavp-vsdtoken/examples.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[
{
"name": "Stake DAO vote boost using TWAVP for vsdTkn holders",
"strategy": {
"name": "sd-vote-boost-twavp-vsdtoken",
"params": {
"vsdToken": "0xE079ac07463ff375Ce48E8A9D76211C10696F3B8",
"sdTokenGauge": "0x7f50786A0b15723D741727882ee99a0BF34e3466",
"booster": "0x38d10708Ce535361F178f55E68DF7E85aCc66270",
"locker": "0x52f541764E6e90eeBc5c21Ff570De0e2D63766B6",
"veAddress": "0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2",
"symbol": "sdToken",
"decimals": 18,
"sampleSize": 10,
"sampleStep": 5,
"whiteListedAddress": ["0x1c0D72a330F2768dAF718DEf8A19BAb019EEAd09"]
}
},
"network": "1",
"addresses": [
"0xc59910E5E2dd8225623B663491aa754F7013F067",
"0xDdB50FfDbA4D89354E1088e4EA402de895562173",
"0xE1F7eaD40d33eeF30dCf15eB5efC45409001aAB8",
"0x1c0D72a330F2768dAF718DEf8A19BAb019EEAd09"
],
"snapshot": 18870156
}
]
188 changes: 188 additions & 0 deletions src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { multicall } from '../../utils';
import { BigNumber } from '@ethersproject/bignumber';
import { formatUnits } from '@ethersproject/units';

export const author = 'pierremarsotlyon1';
export const version = '0.0.1';

// Used ABI
const abi = [
'function balanceOf(address account) external view returns (uint256)',
'function working_supply() external view returns (uint256)',
'function totalSupply() external view returns (uint256)',
'function working_balances(address account) external view returns (uint256)'
];

const MIN_BLOCK = 18835548;

export async function strategy(
space,
network,
provider,
addresses,
options,
snapshot
): Promise<Record<string, number>> {
// Maximum of 5 multicall!
if (options.sampleStep > 5) {
throw new Error('maximum of 5 call');
}

// Maximum of 20 whitelisted address
if (options.whiteListedAddress.length > 20) {
throw new Error('maximum of 20 whitelisted address');
}

// --- Create block number list for twavp
// Obtain last block number
// Create block tag
let blockTag = 0;
if (typeof snapshot === 'number') {
blockTag = snapshot;
} else {
blockTag = await provider.getBlockNumber();
}

// Create block list
const blockList = getPreviousBlocks(
blockTag,
options.sampleStep,
options.sampleSize
);

const balanceOfQueries: any[] = [];
for(const address of addresses) {

Check failure on line 54 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Insert `·`
balanceOfQueries.push([

Check failure on line 55 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Replace `⏎······options.vsdToken,⏎······'balanceOf',⏎······[address]⏎····` with `options.vsdToken,·'balanceOf',·[address]`
options.vsdToken,
'balanceOf',
[address]
]);
balanceOfQueries.push([

Check failure on line 60 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Replace `⏎······options.vsdToken,⏎······'totalSupply',⏎······[]⏎····` with `options.vsdToken,·'totalSupply',·[]`
options.vsdToken,
'totalSupply',
[]
]);
}

Check failure on line 66 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Delete `··`
const response: any[] = [];
for (let i = 0; i < options.sampleStep; i++) {
// Use good block number
blockTag = blockList[i];

const loopCalls: any[] = [];

// Add mutlicall response to array
if (i === options.sampleStep - 1) {
// End
loopCalls.push([options.veAddress, 'balanceOf', [options.locker]]);
loopCalls.push([options.sdTokenGauge, 'working_supply']);
loopCalls.push([options.sdTokenGauge, 'working_balances', [options.booster]]);

Check failure on line 79 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Replace `options.sdTokenGauge,·'working_balances',·[options.booster]` with `⏎········options.sdTokenGauge,⏎········'working_balances',⏎········[options.booster]⏎······`
loopCalls.push(...balanceOfQueries);
} else {
loopCalls.push(...balanceOfQueries);
}

response.push(
await multicall(network, provider, abi, loopCalls, { blockTag })
);
}

const lockerVeBalance = response[response.length - 1].shift()[0]; // Last response, latest block
const workingSupply = response[response.length - 1].shift()[0]; // Last response, latest block
const workingBalances = response[response.length - 1].shift()[0]; // Last response, latest block

const totalVP = parseFloat(formatUnits(workingBalances, 18))

Check failure on line 94 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Replace `·parseFloat(formatUnits(workingBalances,·18))` with `⏎····(parseFloat(formatUnits(workingBalances,·18))·/`
/ parseFloat(formatUnits(workingSupply, 18))

Check failure on line 95 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Replace `/·parseFloat(formatUnits(workingSupply,·18))` with `··parseFloat(formatUnits(workingSupply,·18)))·*`
* parseFloat(formatUnits(lockerVeBalance, 18));

Check failure on line 96 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Delete `·*`

return Object.fromEntries(
Array(addresses.length)
.fill('x')
.map((_, i) => {
// Init array of working balances for user
const userWorkingBalances: number[] = [];

for (let j = 0; j < options.sampleStep; j++) {
const balanceOf = BigNumber.from(response[j].shift()[0]);
const totalSupply = BigNumber.from(response[j].shift()[0]);

// Add working balance to array.
if(totalSupply.eq(0)) {

Check failure on line 110 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Insert `·`
userWorkingBalances.push(0);
} else {
userWorkingBalances.push(balanceOf.div(totalSupply).toNumber());
}
}

// Get average working balance.
const averageWorkingBalance = average(
userWorkingBalances,
addresses[i],
options.whiteListedAddress
);

// Calculate voting power.
const votingPower =

Check failure on line 125 in src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts

View workflow job for this annotation

GitHub Actions / lint / Lint

Replace `⏎··········totalVP·!=·0⏎············?·averageWorkingBalance·*·totalVP⏎···········` with `·totalVP·!=·0·?·averageWorkingBalance·*·totalVP`
totalVP != 0
? averageWorkingBalance * totalVP
: 0;

// Return address and voting power
return [addresses[i], Number(votingPower)];
})
);
}


function average(
numbers: number[],
address: string,
whiteListedAddress: string[]
): number {
// If no numbers, return 0 to avoid division by 0.
if (numbers.length === 0) return 0;

// If address is whitelisted, return most recent working balance. i.e. no twavp applied.
if (whiteListedAddress.includes(address)) return numbers[numbers.length - 1];

// Init sum
let sum = 0;
// Loop through all elements and add them to sum
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i];
}

// Return sum divided by array length to get mean
return sum / numbers.length;
}


function getPreviousBlocks(
currentBlockNumber: number,
numberOfBlocks: number,
daysInterval: number
): number[] {
// Estimate number of blocks per day
const blocksPerDay = 86400 / 12;
// Calculate total blocks interval
const totalBlocksInterval = blocksPerDay * daysInterval;
// Calculate block interval
const blockInterval = totalBlocksInterval / (numberOfBlocks - 1);

// Init array of block numbers
const blockNumbers: number[] = [];

for (let i = 0; i < numberOfBlocks; i++) {
// Calculate block number
let blockNumber =
currentBlockNumber - totalBlocksInterval + blockInterval * i;
if (blockNumber < MIN_BLOCK) {
blockNumber = MIN_BLOCK;
}
// Add block number to array
blockNumbers.push(Math.round(blockNumber));
}

// Return array of block numbers
return blockNumbers;
}

0 comments on commit ee38f82

Please sign in to comment.