-
Notifications
You must be signed in to change notification settings - Fork 804
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into sf-vesting-strategy
- Loading branch information
Showing
16 changed files
with
823 additions
and
9 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
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,36 @@ | ||
# Lizcoin Voting Strategy 2024 | ||
lizcoin-strategy-2024 | ||
|
||
A voting strategy for Lizard Labs' Lizcoin ERC20 Token (LIZ). | ||
|
||
The strategy is based on the quadratic voting formula. The strategy counts only staked and vested tokens – details | ||
follow bellow. | ||
|
||
## Eligible $LIZ forms: | ||
* Staked $LIZ | ||
* Staked LP tokens (converted back to their value in $LIZ) | ||
* vLIZ pre-tokens (investors) in a wallet | ||
* veLIZ pre-tokens (team) in a wallet | ||
* ANY position in the pre-token vesting contract, whether it came from vLIZ, veLIZ, or was set manually for e.g. KOLs | ||
|
||
## Ineligible $LIZ forms: | ||
* Regular $LIZ or LP tokens in a wallet | ||
* cLIZ tokens in a wallet | ||
* Staking or loot box rewards that are unclaimed | ||
|
||
Here is an example of parameters: | ||
|
||
```json | ||
{ | ||
"lizcoinAddress": "0xAF4144cd943ed5362Fed2BaE6573184659CBe6FF", | ||
"cLIZAddress": "0x0F9dc0c0A46733c8b9a6C2E4850913Ed31d31205", | ||
"vLIZAddress": "0xe20C4edb8440CaDD4001c144B4F38576d1AA3820", | ||
"veLIZAddress": "0xC817C0B518e8Fc98034ad867d679d4f8A284BFBE", | ||
"cLIZConversionRate": 0.01, | ||
"vLIZConversionRate": 0.01, | ||
"veLIZConversionRate": 0.01, | ||
"uniswapV2PoolAddress": "0xD47B93360EAADBA2678c30F64209a42b9800cEE4", | ||
"stakingContractAddress": "0xEf2E841AA9F49cc0E54697A4Afa6361eA24d682F", | ||
"vestingContractAddress": "0x895ecdCAC6431272946Ec615eD368d2f42fC2b44" | ||
} | ||
``` |
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,44 @@ | ||
[ | ||
{ | ||
"name": "Example query for Lizcoin Voting Strategy 2024", | ||
"strategy": { | ||
"name": "lizcoin-strategy-2024", | ||
"params": { | ||
"lizcoinAddress": "0xAF4144cd943ed5362Fed2BaE6573184659CBe6FF", | ||
"cLIZAddress": "0x0F9dc0c0A46733c8b9a6C2E4850913Ed31d31205", | ||
"vLIZAddress": "0xe20C4edb8440CaDD4001c144B4F38576d1AA3820", | ||
"veLIZAddress": "0xC817C0B518e8Fc98034ad867d679d4f8A284BFBE", | ||
"cLIZConversionRate": 0.01, | ||
"vLIZConversionRate": 0.01, | ||
"veLIZConversionRate": 0.01, | ||
"uniswapV2PoolAddress": "0xD47B93360EAADBA2678c30F64209a42b9800cEE4", | ||
"stakingContractAddress": "0xEf2E841AA9F49cc0E54697A4Afa6361eA24d682F", | ||
"vestingContractAddress": "0x895ecdCAC6431272946Ec615eD368d2f42fC2b44" | ||
} | ||
}, | ||
"network": "1", | ||
"addresses": [ | ||
"0xC5015Af75881b4aBed54043B46561ca829EC2468", | ||
"0x11619E71E2B53AF33e1CFEAaab51d4E570aFC299", | ||
"0x922dfB7092BB3A14A1335c843C1760f88A406E99", | ||
"0xAe8513960374736fAe8CBA47e1A2Ae1Cd842e439", | ||
"0xc9b95B403dC5C9E2A8cB6A703A78016A537DBAE3", | ||
"0xAB12253171A0d73df64B115cD43Fe0A32Feb9dAA", | ||
"0x7C175168d76849Aba1EAD344AD30AC94f120a077", | ||
"0xC1305f9Df23799596a12E6C7cCD8D2663B875D61", | ||
"0x601071a8fB7890bAda13E24c6Bf7838e90778A78", | ||
"0xc6184b7e9C896f5035F7A670BBeA5A445fDA6E1C", | ||
"0xf102978E0C95207AD6Ee2d39d18f1e7497d89EAD", | ||
"0xf91D65180d0e5eD3C5a56B9211b6B4b26E24C2d5", | ||
"0xf02b4c61Bb87817690034667186EF9836F4F0898", | ||
"0xdD020966b963FFC7623498dF5B8Ed14b11AF7B2A", | ||
"0x6C6a234D1806F7d0baC872eD49933A6852424467", | ||
"0x3cC31eFcEEFAdC2A86716A3BeE91D1917418E29a", | ||
"0x8f60501dE5b9b01F9EAf1214dbE1924aA97F7fd0", | ||
"0x9B8e8dD9151260c21CB6D7cc59067cd8DF306D58", | ||
"0x17ea92D6FfbAA1c7F6B117c1E9D0c88ABdc8b84C", | ||
"0x38C0039247A31F3939baE65e953612125cB88268" | ||
], | ||
"snapshot": 21428809 | ||
} | ||
] |
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,161 @@ | ||
import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; | ||
import { StaticJsonRpcProvider } from '@ethersproject/providers'; | ||
import { formatUnits } from '@ethersproject/units'; | ||
import { Multicaller } from '../../utils'; | ||
|
||
export const author = 'ethlizards'; | ||
export const version = '0.1.0'; | ||
|
||
const abi = [ | ||
'function decimals() external view returns (uint8)', | ||
'function totalSupply() external view returns (uint256)', | ||
'function balanceOf(address account) external view returns (uint256)', | ||
'function sumDepositAmounts(address depositToken, address rewardToken, address accountAddress) external view returns (uint96)', | ||
// TODO: introduce sumVestedAmounts(address holder) function | ||
'function getVestingSchedulesArray(address holder) external view returns (tuple(uint32 startDate, uint32 duration, uint96 amountVested, uint96 amountClaimed)[])' | ||
]; | ||
|
||
interface Options { | ||
lizcoinAddress: string; | ||
cLIZAddress: string; | ||
vLIZAddress: string; | ||
veLIZAddress: string; | ||
cLIZConversionRate: number; | ||
vLIZConversionRate: number; | ||
veLIZConversionRate: number; | ||
uniswapV2PoolAddress: string; | ||
stakingContractAddress: string; | ||
vestingContractAddress: string; | ||
} | ||
|
||
interface VestingSchedule { | ||
startDate: number; | ||
duration: number; | ||
amountVested: BigNumber; | ||
amountClaimed: BigNumber; | ||
} | ||
|
||
export async function strategy( | ||
space: string, | ||
network: string, | ||
provider: StaticJsonRpcProvider, | ||
addresses: string[], | ||
options: Options, | ||
snapshot: number | 'latest' | ||
): Promise<Record<string, number>> { | ||
const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; | ||
|
||
const multi = new Multicaller(network, provider, abi, { blockTag }); | ||
|
||
// Fetch decimals for LIZ, vLIZ, veLIZ, and LP | ||
multi.call('lizcoinDecimals', options.lizcoinAddress, 'decimals'); | ||
multi.call('vLIZDecimals', options.vLIZAddress, 'decimals'); | ||
multi.call('veLIZDecimals', options.veLIZAddress, 'decimals'); | ||
multi.call('lpDecimals', options.uniswapV2PoolAddress, 'decimals'); | ||
// Fetch LP/LIZ conversion rate | ||
multi.call('lpSupply', options.uniswapV2PoolAddress, 'totalSupply'); | ||
multi.call('lpLizcoinBalance', options.lizcoinAddress, 'balanceOf', [ | ||
options.uniswapV2PoolAddress | ||
]); | ||
|
||
// Fetch balances for LIZ, vLIZ, and veLIZ | ||
addresses.forEach((address: string) => { | ||
// Staked $LIZ | ||
multi.call( | ||
`stakedLIZ_${address}`, | ||
options.stakingContractAddress, | ||
'sumDepositAmounts', | ||
[options.lizcoinAddress, options.lizcoinAddress, address] | ||
); | ||
// Staked LP tokens | ||
multi.call( | ||
`stakedLP_${address}`, | ||
options.stakingContractAddress, | ||
'sumDepositAmounts', | ||
[options.uniswapV2PoolAddress, options.lizcoinAddress, address] | ||
); | ||
// vLIZ pre-tokens (investors) in a wallet | ||
multi.call(`vLIZ_${address}`, options.vLIZAddress, 'balanceOf', [address]); | ||
// veLIZ pre-tokens (team) in a wallet | ||
multi.call(`veLIZ_${address}`, options.veLIZAddress, 'balanceOf', [ | ||
address | ||
]); | ||
// ANY position in the pre-token vesting contract, whether it came from vLIZ, veLIZ, or was set manually for e.g. KOLs | ||
multi.call( | ||
`vestingSchedules_${address}`, | ||
options.vestingContractAddress, | ||
'getVestingSchedulesArray', | ||
[address] | ||
); | ||
}); | ||
|
||
const result: Record<string, BigNumberish> = await multi.execute(); | ||
|
||
// decimals | ||
const lizcoinDecimals = result['lizcoinDecimals']; | ||
const vLIZDecimals = result['vLIZDecimals']; | ||
const veLIZDecimals = result['veLIZDecimals']; | ||
const lpDecimals = result['lpDecimals']; | ||
// LP/LIZ conversion rate | ||
const lpSupply = parseFloat(formatUnits(result['lpSupply'], lpDecimals)); | ||
const lpLizcoinBalance = parseFloat( | ||
formatUnits(result['lpLizcoinBalance'], lpDecimals) | ||
); | ||
const lpLizcoinConvRate = lpSupply / lpLizcoinBalance / 2; | ||
|
||
// derive the voting power as an address:number mapping | ||
const votingPower = Object.fromEntries( | ||
addresses.map(function (address) { | ||
// Staked $LIZ | ||
const stakedLizcoinBalance = parseFloat( | ||
formatUnits(result[`stakedLIZ_${address}`], lizcoinDecimals) | ||
); | ||
|
||
// Staked LP tokens (converted back to their value in $LIZ) | ||
const stakedLpBalance = | ||
parseFloat( | ||
formatUnits(result[`stakedLP_${address}`], lizcoinDecimals) | ||
) / lpLizcoinConvRate; | ||
|
||
// vLIZ pre-tokens (investors) in a wallet (converted to their value in $LIZ) | ||
const vLIZBalance = | ||
parseFloat(formatUnits(result[`vLIZ_${address}`], vLIZDecimals)) / | ||
options.vLIZConversionRate; | ||
|
||
// veLIZ pre-tokens (team) in a wallet (converted to their value in $LIZ) | ||
const veLIZBalance = | ||
parseFloat(formatUnits(result[`veLIZ_${address}`], veLIZDecimals)) / | ||
options.veLIZConversionRate; | ||
|
||
// ANY position in the pre-token vesting contract, whether it came from vLIZ, veLIZ, or was set manually for e.g. KOLs | ||
const schedules: VestingSchedule[] = result[ | ||
`vestingSchedules_${address}` | ||
] as unknown as VestingSchedule[]; | ||
const vestedAmountsSum = schedules.reduce( | ||
(accumulator, currentValue) => | ||
BigNumber.from(accumulator).add( | ||
BigNumber.from(currentValue.amountVested).sub( | ||
BigNumber.from(currentValue.amountClaimed) | ||
) | ||
), | ||
BigNumber.from(0) | ||
); | ||
const vestedLizcoinBalance = parseFloat( | ||
formatUnits(vestedAmountsSum, lizcoinDecimals) | ||
); | ||
|
||
// sum all the balances (already converted to LIZ equivalents) | ||
const totalBalance = | ||
stakedLizcoinBalance + | ||
stakedLpBalance + | ||
vLIZBalance + | ||
veLIZBalance + | ||
vestedLizcoinBalance; | ||
|
||
// apply the quadratic voting formula and return | ||
return [address, Math.sqrt(totalBalance)]; | ||
}) | ||
); | ||
// console.log(votingPower); | ||
return votingPower; | ||
} |
Oops, something went wrong.