-
Notifications
You must be signed in to change notification settings - Fork 815
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Vsdtoken [sd-vote-boost-twavp-vsdtoken] (#1380)
* init * use sampleStep * voting power naming * strat init * compute vp * min block + vp formula
- Loading branch information
1 parent
b56b735
commit ee38f82
Showing
4 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
balanceOfQueries.push([ | ||
options.vsdToken, | ||
'balanceOf', | ||
[address] | ||
]); | ||
balanceOfQueries.push([ | ||
options.vsdToken, | ||
'totalSupply', | ||
[] | ||
]); | ||
} | ||
|
||
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
|
||
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)) | ||
/ parseFloat(formatUnits(workingSupply, 18)) | ||
* parseFloat(formatUnits(lockerVeBalance, 18)); | ||
|
||
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)) { | ||
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 = | ||
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; | ||
} |