Skip to content

Commit

Permalink
A voting strategy for Lizard Labs' Lizcoin ERC20 Token (LIZ) (#1662)
Browse files Browse the repository at this point in the history
  • Loading branch information
vgorin authored Dec 19, 2024
1 parent 25bad80 commit 47c9072
Show file tree
Hide file tree
Showing 5 changed files with 353 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/strategies/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ import * as sacraSubgraph from './sacra-subgraph';
import * as fountainhead from './fountainhead';
import * as naymsStaking from './nayms-staking';
import * as morphoDelegation from './morpho-delegation';
import * as lizcoinStrategy2024 from './lizcoin-strategy-2024';

const strategies = {
'delegatexyz-erc721-balance-of': delegatexyzErc721BalanceOf,
Expand Down Expand Up @@ -944,7 +945,8 @@ const strategies = {
'sacra-subgraph': sacraSubgraph,
fountainhead,
'nayms-staking': naymsStaking,
'morpho-delegation': morphoDelegation
'morpho-delegation': morphoDelegation,
'lizcoin-strategy-2024': lizcoinStrategy2024
};

Object.keys(strategies).forEach(function (strategyName) {
Expand Down
36 changes: 36 additions & 0 deletions src/strategies/lizcoin-strategy-2024/README.md
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"
}
```
44 changes: 44 additions & 0 deletions src/strategies/lizcoin-strategy-2024/examples.json
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
}
]
161 changes: 161 additions & 0 deletions src/strategies/lizcoin-strategy-2024/index.ts
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;
}
109 changes: 109 additions & 0 deletions src/strategies/lizcoin-strategy-2024/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/Strategy",
"definitions": {
"Strategy": {
"title": "Lizcoin Voting Strategy 2024",
"type": "object",
"properties": {
"lizcoinAddress": {
"type": "string",
"title": "Lizcoin ERC20 Token (LIZ) Address",
"default": "0xAF4144cd943ed5362Fed2BaE6573184659CBe6FF",
"examples": ["e.g. 0xAF4144cd943ed5362Fed2BaE6573184659CBe6FF"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
},
"cLIZAddress": {
"type": "string",
"title": "cLIZ Convertable Coin Address",
"default": "0x0F9dc0c0A46733c8b9a6C2E4850913Ed31d31205",
"examples": ["e.g. 0x0F9dc0c0A46733c8b9a6C2E4850913Ed31d31205"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
},
"vLIZAddress": {
"type": "string",
"title": "vLIZ Convertable Coin Address",
"default": "0xe20C4edb8440CaDD4001c144B4F38576d1AA3820",
"examples": ["e.g. 0xe20C4edb8440CaDD4001c144B4F38576d1AA3820"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
},
"veLIZAddress": {
"type": "string",
"title": "veLIZ Convertable Coin Address",
"default": "0xC817C0B518e8Fc98034ad867d679d4f8A284BFBE",
"examples": ["e.g. 0xC817C0B518e8Fc98034ad867d679d4f8A284BFBE"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
},
"cLIZConversionRate": {
"type": "number",
"title": "cLIZ to LIZ Conversion Rate",
"default": 0.01,
"examples": [0.01],
"minimum": 0.000000001,
"maximum": 1000000000
},
"vLIZConversionRate": {
"type": "number",
"title": "vLIZ to LIZ Conversion Rate",
"default": 0.01,
"examples": [0.01],
"minimum": 0.000000001,
"maximum": 1000000000
},
"veLIZConversionRate": {
"type": "number",
"title": "veLIZ to LIZ Conversion Rate",
"default": 0.01,
"examples": [0.01],
"minimum": 0.000000001,
"maximum": 1000000000
},
"uniswapV2PoolAddress": {
"type": "string",
"title": "Voting Eligible Uniswap V2 LP Address",
"examples": ["e.g. 0xD47B93360EAADBA2678c30F64209a42b9800cEE4"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
},
"stakingContractAddress": {
"type": "string",
"title": "Voting Eligible Staking Contract Address",
"examples": ["e.g. 0xEf2E841AA9F49cc0E54697A4Afa6361eA24d682F"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
},
"vestingContractAddress": {
"type": "string",
"title": "Voting Eligible Vesting Contract Address",
"examples": ["e.g. 0x895ecdCAC6431272946Ec615eD368d2f42fC2b44"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
}
},
"required": [
"lizcoinAddress",
"cLIZAddress",
"vLIZAddress",
"veLIZAddress",
"cLIZConversionRate",
"vLIZConversionRate",
"veLIZConversionRate",
"uniswapV2PoolAddress",
"stakingContractAddress",
"vestingContractAddress"
],
"additionalProperties": false
}
}
}

0 comments on commit 47c9072

Please sign in to comment.