diff --git a/package.json b/package.json old mode 100755 new mode 100644 index aa2320fc5..9a96a9dc7 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@ethersproject/strings": "^5.6.1", "@ethersproject/units": "^5.6.1", "@ethersproject/wallet": "^5.6.2", - "@snapshot-labs/snapshot.js": "^0.7.3", + "@snapshot-labs/snapshot.js": "^0.10.1", "@spruceid/didkit-wasm-node": "^0.2.1", "@uniswap/sdk-core": "^3.0.1", "@uniswap/v3-sdk": "^3.9.0", diff --git a/src/strategies/aavegotchi-agip-17/examples.json b/src/strategies/aavegotchi-agip-17/examples.json index 360eac9d2..a1f741ddb 100644 --- a/src/strategies/aavegotchi-agip-17/examples.json +++ b/src/strategies/aavegotchi-agip-17/examples.json @@ -8,7 +8,11 @@ } }, "network": "137", - "addresses": ["0x027Ffd3c119567e85998f4E6B9c3d83D5702660c"], - "snapshot": 22089223 + "addresses": [ + "0x027Ffd3c119567e85998f4E6B9c3d83D5702660c", + "0xC3c2e1Cf099Bc6e1fA94ce358562BCbD5cc59FE5", + "0x585E06CA576D0565a035301819FD2cfD7104c1E8" + ], + "snapshot": 50601487 } ] diff --git a/src/strategies/aavegotchi-agip-37-gltr-staked-lp/README.md b/src/strategies/aavegotchi-agip-37-gltr-staked-lp/README.md index 81369b718..3a448a5ca 100644 --- a/src/strategies/aavegotchi-agip-37-gltr-staked-lp/README.md +++ b/src/strategies/aavegotchi-agip-37-gltr-staked-lp/README.md @@ -13,10 +13,11 @@ This snapshot strategy enables voting power for the following assets staked in G - GHST-WMATIC LP Please note this excludes voting power from: + - Staked wapGHST and unstaked wapGHST held in a wallet (see aavegotchi-agip-37-wap-ghst) - amGHST (see erc20-balance-of) - Unstaked GHST-FUD, GHST-FOMO, GHST-ALPHA, GHST-KEK, GHST-GLTR LP tokens (see erc20-tokens-per-uni) ## References -Aavegotchi AGIP 37: https://snapshot.org/#/aavegotchi.eth/proposal/0x9923aab6825158ec2503d88e3ee2f9c5fbb12000581d06343ac9829aa59b66a6 \ No newline at end of file +Aavegotchi AGIP 37: https://snapshot.org/#/aavegotchi.eth/proposal/0x9923aab6825158ec2503d88e3ee2f9c5fbb12000581d06343ac9829aa59b66a6 diff --git a/src/strategies/aavegotchi-agip-37-wap-ghst/README.md b/src/strategies/aavegotchi-agip-37-wap-ghst/README.md index 45c8a29a7..e974b3d66 100644 --- a/src/strategies/aavegotchi-agip-37-wap-ghst/README.md +++ b/src/strategies/aavegotchi-agip-37-wap-ghst/README.md @@ -6,4 +6,4 @@ This snapshot strategy enables voting power for staked and unstaked wapGHST. ## References -Aavegotchi AGIP 37: https://snapshot.org/#/aavegotchi.eth/proposal/0x9923aab6825158ec2503d88e3ee2f9c5fbb12000581d06343ac9829aa59b66a6 \ No newline at end of file +Aavegotchi AGIP 37: https://snapshot.org/#/aavegotchi.eth/proposal/0x9923aab6825158ec2503d88e3ee2f9c5fbb12000581d06343ac9829aa59b66a6 diff --git a/src/strategies/aavegotchi-agip/examples.json b/src/strategies/aavegotchi-agip/examples.json index 72a671a70..631a5cf86 100644 --- a/src/strategies/aavegotchi-agip/examples.json +++ b/src/strategies/aavegotchi-agip/examples.json @@ -10,10 +10,10 @@ }, "network": "137", "addresses": [ - "0x51195e21BDaE8722B29919db56d95Ef51FaecA6C", + "0x9730299b10A30bDbAF81815c99b4657c685314AB", "0xDd564df884Fd4e217c9ee6F65B4BA6e5641eAC63", "0xBfe09443556773958bae1699b786d8E9680B5571" ], - "snapshot": 39986904 + "snapshot": 51370819 } ] diff --git a/src/strategies/aavegotchi-agip/index.ts b/src/strategies/aavegotchi-agip/index.ts index d3c278216..83dc40d53 100644 --- a/src/strategies/aavegotchi-agip/index.ts +++ b/src/strategies/aavegotchi-agip/index.ts @@ -349,7 +349,25 @@ const prices: Prices = { '366': 10000, '367': 10000, '368': 10000, - '369': 10000 + '369': 10000, + '370': 5, + '371': 5, + '372': 5, + '373': 10, + '374': 100, + '375': 5, + '376': 100, + '377': 10, + '378': 100, + '379': 100, + '380': 300, + '381': 300, + '382': 300, + '383': 300, + '384': 2000, + '385': 10000, + '386': 10000, + '387': 10000 }; const tokenAbi = [ diff --git a/src/strategies/aavegotchi/examples.json b/src/strategies/aavegotchi/examples.json index 4e9e847ab..adc31be7f 100644 --- a/src/strategies/aavegotchi/examples.json +++ b/src/strategies/aavegotchi/examples.json @@ -15,7 +15,11 @@ } }, "network": "137", - "addresses": ["0x027Ffd3c119567e85998f4E6B9c3d83D5702660c"], - "snapshot": 12089223 + "addresses": [ + "0x027Ffd3c119567e85998f4E6B9c3d83D5702660c", + "0xC3c2e1Cf099Bc6e1fA94ce358562BCbD5cc59FE5", + "0x585E06CA576D0565a035301819FD2cfD7104c1E8" + ], + "snapshot": 50601487 } ] diff --git a/src/strategies/apeswap/README.md b/src/strategies/apeswap/README.md deleted file mode 100644 index 90c90eefb..000000000 --- a/src/strategies/apeswap/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Apeswap - -This is the most common strategy, it returns the balances of the voters for a balances GNANA token -in Apeswap project(pools, token). - -Here is an example of parameters: -```json -[ - { - "name": "Example query Apeswap", - "strategy": { - "name": "apeswap", - "params": { - "address": "0xddb3bd8645775f59496c821e4f55a7ea6a6dc299", - "symbol": "GNANA", - "decimals": 18 - } - }, - "network": "56", - "addresses": [ - "0x56E17565Ce37c0Dbb2BDDec0eC607b874785c376", - "0x0326824dB556Ab5525608851713e269Ce583B629", - "0xc47dec7ffde829043b91e904fdaf1e048bdd482c", - "0x356f74457a8002c680a0c8fa628083d619267c88", - "0x607f62572ea0c00da5048eb39d89d32110151681", - "0x75768Ea1A1C3c84121063f7A281ee3081dB1D8Ef" - ], - "snapshot": 18979451 - } -] - - -``` diff --git a/src/strategies/apeswap/examples.json b/src/strategies/apeswap/examples.json deleted file mode 100644 index 07d7dfa3f..000000000 --- a/src/strategies/apeswap/examples.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "name": "Example query Apeswap", - "strategy": { - "name": "apeswap", - "params": { - "address": "0xddb3bd8645775f59496c821e4f55a7ea6a6dc299", - "symbol": "GNANA", - "decimals": 18 - } - }, - "network": "56", - "addresses": [ - "0xe5EdeA54596B385C3c8dFd5010C3Cf892d547Acb", - "0x56E17565Ce37c0Dbb2BDDec0eC607b874785c376", - "0x0326824dB556Ab5525608851713e269Ce583B629", - "0xc47dec7ffde829043b91e904fdaf1e048bdd482c", - "0x356f74457a8002c680a0c8fa628083d619267c88", - "0x607f62572ea0c00da5048eb39d89d32110151681", - "0x75768Ea1A1C3c84121063f7A281ee3081dB1D8Ef" - ], - "snapshot": 18979451 - } -] diff --git a/src/strategies/apeswap/index.ts b/src/strategies/apeswap/index.ts deleted file mode 100644 index 4fab8dae6..000000000 --- a/src/strategies/apeswap/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = 'ApeSwapFinance'; -export const version = '0.0.1'; - -const GNANA_POOL = '0x8F97B2E6559084CFaBA140e2AB4Da9aAF23FE7F8'; -const abi = [ - 'function balanceOf(address _owner) view returns (uint256 balance)', - 'function userInfo(address) view returns (uint256 amount, uint256 rewardDebt)' -]; - -const bn = (num: any): BigNumber => { - return BigNumber.from(num.toString()); -}; - -const addUserBalance = (userBalances, user: string, balance) => { - if (userBalances[user]) { - return (userBalances[user] = userBalances[user].add(balance)); - } else { - return (userBalances[user] = balance); - } -}; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const multicall = new Multicaller(network, provider, abi, { blockTag }); - addresses.forEach((address: any) => { - multicall.call(`token.${address}`, options.address, 'balanceOf', [address]); - multicall.call(`pool.${address}`, GNANA_POOL, 'userInfo', [address]); - }); - const result = await multicall.execute(); - - const userBalances: any = []; - for (let i = 0; i < addresses.length - 1; i++) { - userBalances[addresses[i]] = bn(0); - } - - addresses.forEach((address: any) => { - addUserBalance(userBalances, address, result.token[address] ?? 0); - addUserBalance(userBalances, address, result.pool[address][0] ?? 0); - }); - - return Object.fromEntries( - Object.entries(userBalances).map(([address, balance]) => [ - address, - parseFloat(formatUnits(balance, options.decimals)) - ]) - ); -} diff --git a/src/strategies/arrow-vesting/examples.json b/src/strategies/arrow-vesting/examples.json index c862e4d7b..f3cfaa5e9 100644 --- a/src/strategies/arrow-vesting/examples.json +++ b/src/strategies/arrow-vesting/examples.json @@ -16,8 +16,9 @@ "0xB66f08DBd7A59B32e98033b9A1da08B5793DAb79", "0x5b8eD2A2CfFCD474B2E688fdeA21CB5c4350E575", "0x03b5Dc2CE78a7bEe9F66DD619b291595a2E166BB", - "0x06A61f56de8c6a2735D1Dea68340D201ddEd7348" + "0x06A61f56de8c6a2735D1Dea68340D201ddEd7348", + "0x252C855Cc3aB5f48229393Bc4DA129542a08C808" ], - "snapshot": 18879362 + "snapshot": 112192500 } ] diff --git a/src/strategies/arrow-vesting/index.ts b/src/strategies/arrow-vesting/index.ts index a8569d213..31bad26b8 100644 --- a/src/strategies/arrow-vesting/index.ts +++ b/src/strategies/arrow-vesting/index.ts @@ -14,7 +14,10 @@ const vestingContractAbi = [ 'function recipient() public view returns (address)', 'function total_locked() public view returns (uint256)', 'function start_time() public view returns (uint256)', - 'function end_time() public view returns (uint256)' + 'function unclaimed() public view returns (uint256)' + // don't need to check initialized? + // don't need to check admin? + // don't need to check future_admin? ]; export async function strategy( @@ -82,9 +85,9 @@ export async function strategy( [] ); vestingContractMulti.call( - `${vestingContractAddress}.end_time`, + `${vestingContractAddress}.unclaimed`, vestingContractAddress, - 'end_time', + 'unclaimed', [] ); }); @@ -106,15 +109,12 @@ export async function strategy( const start = params['start_time']; if (recipient in addressBalances && time > start) { - const locked = parseFloat( - formatUnits(params['total_locked'], options.decimals) + const unclaimedTokens = parseFloat( + formatUnits(params['unclaimed'], options.decimals) ); - const end = params['end_time']; - addressBalances[recipient] += Math.min( - (locked * (time - start)) / (end - start), - locked - ); + // Vested arrow that can be claimed is all that is counted in this strategy + addressBalances[recipient] += unclaimedTokens; } }); diff --git a/src/strategies/aura-balance-of-vlaura-vebal/README.md b/src/strategies/aura-balance-of-vlaura-vebal/README.md deleted file mode 100644 index aa1838dd7..000000000 --- a/src/strategies/aura-balance-of-vlaura-vebal/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# aura-balance-of-vlaura-vebal - -This strategy returns proportional voting power for vlAURA holders based on system owned veBAL. - -The voting power is based on the raw balance, rather than delegated voting power. - -For example: -- there are 10000 vlAURA total supply -- a user has 2000 vlAURA (raw balance) -- Aura's voterProxy owns 100k veBAL - -In this example, the user has 20k veBAL balance as they own 20% of the vlAURA voting power. - -_Note: When depositing to the auraLocker, a user does not receive vlAURA until the next epoch has begun (Thursday at 00:00 UTC)_ - -## Params - -- `auraLocker` - (**Required**, `string`) Address of AuraLocker (vlAURA) contract -- `auraVoterProxy` - (**Required**, `string`) Address of Aura VoterProxy contract -- `votingEscrow` - (**Required**, `string`) Address of Balancer VotingEscrow contract - -Here is an example of parameters: - -```json -{ - "auraLocker": "0x3Fa73f1E5d8A792C80F426fc8F84FBF7Ce9bBCAC", - "auraVoterProxy": "0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2", - "votingEscrow": "0xC128a9954e6c874eA3d62ce62B468bA073093F25" -} -``` diff --git a/src/strategies/aura-balance-of-vlaura-vebal/examples.json b/src/strategies/aura-balance-of-vlaura-vebal/examples.json deleted file mode 100644 index 648742d98..000000000 --- a/src/strategies/aura-balance-of-vlaura-vebal/examples.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "aura-balance-of-vlaura-vebal", - "params": { - "auraLocker": "0x3Fa73f1E5d8A792C80F426fc8F84FBF7Ce9bBCAC", - "auraVoterProxy": "0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2", - "votingEscrow": "0xC128a9954e6c874eA3d62ce62B468bA073093F25" - } - }, - "network": "1", - "addresses": [ - "0x512fce9B07Ce64590849115EE6B32fd40eC0f5F3", - "0x808af82545A721C06D1FcCEbea915a6F5128BeF9", - "0x0CAd1d5ea8b4EeE26959cC00B4A3677f7A11e40F" - ], - "snapshot": 15276577 - } -] diff --git a/src/strategies/aura-balance-of-vlaura-vebal/index.ts b/src/strategies/aura-balance-of-vlaura-vebal/index.ts deleted file mode 100644 index d294c271a..000000000 --- a/src/strategies/aura-balance-of-vlaura-vebal/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = '0xButterfield'; -export const version = '0.1.0'; - -const abi = [ - 'function balanceOf(address account) public view returns (uint256)', - 'function totalSupply() public view returns (uint256)' -]; - -interface Params { - auraLocker: string; - auraVoterProxy: string; - votingEscrow: string; -} - -interface Response { - vlAuraTotalSupply: BigNumber; - vlAuraBalance: Record; - veBalOwnedByAura: BigNumber; -} - -export async function strategy( - space, - network, - provider, - addresses, - options: Params, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - multi.call('vlAuraTotalSupply', options.auraLocker, 'totalSupply', []); - addresses.forEach((address) => - multi.call(`vlAuraBalance.${address}`, options.auraLocker, 'balanceOf', [ - address - ]) - ); - multi.call('veBalOwnedByAura', options.votingEscrow, 'balanceOf', [ - options.auraVoterProxy - ]); - const res: Response = await multi.execute(); - - return Object.fromEntries( - Object.entries(res.vlAuraBalance).map(([address, balance]) => [ - address, - parseFloat( - formatUnits( - res.veBalOwnedByAura.mul(balance).div(res.vlAuraTotalSupply), - 18 - ) - ) - ]) - ); -} diff --git a/src/strategies/aura-balance-of-vlaura-vebal/schema.json b/src/strategies/aura-balance-of-vlaura-vebal/schema.json deleted file mode 100644 index 85b8c6d26..000000000 --- a/src/strategies/aura-balance-of-vlaura-vebal/schema.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "Strategy", - "type": "object", - "properties": { - "auraLocker": { - "type": "string", - "title": "auraLocker", - "examples": ["e.g. 0x3Fa73f1E5d8A792C80F426fc8F84FBF7Ce9bBCAC"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "auraVoterProxy": { - "type": "string", - "title": "auraVoterProxy", - "examples": ["e.g. 0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "votingEscrow": { - "type": "string", - "title": "votingEscrow", - "examples": ["e.g. 0xC128a9954e6c874eA3d62ce62B468bA073093F25"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - } - }, - "required": ["auraLocker", "auraVoterProxy", "votingEscrow"], - "additionalProperties": false - } - } -} diff --git a/src/strategies/aura-vlaura-vebal-with-overrides/README.md b/src/strategies/aura-vlaura-vebal-with-overrides/README.md deleted file mode 100644 index 8f832bb4d..000000000 --- a/src/strategies/aura-vlaura-vebal-with-overrides/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# aura-vlaura-vebal-with-overrides - -This strategy returns proportional voting power for vlAURA holders based on system owned veBAL. - -For example: -- there are 10000 vlAURA total supply -- a user has 2000 vlAURA (voting power, delegated to them) -- Aura's voterProxy owns 100k veBAL - -In this example, the user has 20k veBAL voting power as they own 20% of the vlAURA voting power. - -Voters can optionally override their delegated voting power. - -_Note: When depositing to the auraLocker, a user does not receive vlAURA until the next epoch has begun (Thursday at 00:00 UTC)_ - -## Params - -- `auraLocker` - (**Required**, `string`) Address of AuraLocker (vlAURA) contract -- `auraVoterProxy` - (**Required**, `string`) Address of Aura VoterProxy contract -- `votingEscrow` - (**Required**, `string`) Address of Balancer VotingEscrow contract - -Here is an example of parameters: - -```json -{ - "auraLocker": "0x3Fa73f1E5d8A792C80F426fc8F84FBF7Ce9bBCAC", - "auraVoterProxy": "0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2", - "votingEscrow": "0xC128a9954e6c874eA3d62ce62B468bA073093F25" -} -``` diff --git a/src/strategies/aura-vlaura-vebal-with-overrides/examples.json b/src/strategies/aura-vlaura-vebal-with-overrides/examples.json deleted file mode 100644 index 1442c1163..000000000 --- a/src/strategies/aura-vlaura-vebal-with-overrides/examples.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "name": "Example query Aura with overrides", - "strategy": { - "name": "aura-vlaura-vebal-with-overrides", - "params": { - "auraLocker": "0x3Fa73f1E5d8A792C80F426fc8F84FBF7Ce9bBCAC", - "auraVoterProxy": "0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2", - "votingEscrow": "0xC128a9954e6c874eA3d62ce62B468bA073093F25" - } - }, - "network": "1", - "addresses": [ - "0xB1f881f47baB744E7283851bC090bAA626df931d", - "0x808af82545A721C06D1FcCEbea915a6F5128BeF9", - "0x0CAd1d5ea8b4EeE26959cC00B4A3677f7A11e40F", - "0x3cde8fa1c73afe0828f672d197e082463c2ac8e2", - "0xca86d57519dbfe34a25eef0923b259ab07986b71" - ], - "snapshot": 15184638 - } -] diff --git a/src/strategies/aura-vlaura-vebal-with-overrides/index.ts b/src/strategies/aura-vlaura-vebal-with-overrides/index.ts deleted file mode 100644 index 69372c5b5..000000000 --- a/src/strategies/aura-vlaura-vebal-with-overrides/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; -import { strategy as erc20VotesWithOverrideStrategy } from '../erc20-votes-with-override'; - -export const author = '0xMaharishi'; -export const version = '0.1.0'; - -const abi = [ - 'function delegates(address account) external view returns (address)', - 'function getVotes(address account) external view returns (uint256)', - 'function totalSupply() public view returns (uint256)', - 'function balanceOf(address account) public view returns (uint256)' -]; - -interface Options { - auraLocker: string; - auraVoterProxy: string; - votingEscrow: string; - includeSnapshotDelegations?: boolean; - delegationSpace?: string; -} - -interface Response { - vlAuraTotalSupply: BigNumber; - veBalOwnedByAura: BigNumber; -} - -/* - Based on the `erc20-votes-with-override` strategy, with global vote scaling - to represent the share of Aura's veBAL. -*/ -export async function strategy( - space, - network, - provider, - addresses, - options: Options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - multi.call('vlAuraTotalSupply', options.auraLocker, 'totalSupply', []); - multi.call('veBalOwnedByAura', options.votingEscrow, 'balanceOf', [ - options.auraVoterProxy - ]); - const res: Response = await multi.execute(); - - const scores: Record = await erc20VotesWithOverrideStrategy( - space, - network, - provider, - addresses, - { - address: options.auraLocker, - delegatesName: 'delegates', - balanceOfName: 'balanceOf', - getVotesName: 'getVotes', - decimals: 18, - includeSnapshotDelegations: options.includeSnapshotDelegations, - delegationSpace: options.delegationSpace - }, - snapshot - ); - - const veBalOwnedByAura = parseFloat(formatUnits(res.veBalOwnedByAura)); - const vlAuraTotalSupply = parseFloat(formatUnits(res.vlAuraTotalSupply)); - - return Object.fromEntries( - Object.entries(scores).map(([address, score]) => [ - address, - (veBalOwnedByAura * score) / vlAuraTotalSupply - ]) - ); -} diff --git a/src/strategies/aura-vlaura-vebal-with-overrides/schema.json b/src/strategies/aura-vlaura-vebal-with-overrides/schema.json deleted file mode 100644 index 85b8c6d26..000000000 --- a/src/strategies/aura-vlaura-vebal-with-overrides/schema.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "Strategy", - "type": "object", - "properties": { - "auraLocker": { - "type": "string", - "title": "auraLocker", - "examples": ["e.g. 0x3Fa73f1E5d8A792C80F426fc8F84FBF7Ce9bBCAC"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "auraVoterProxy": { - "type": "string", - "title": "auraVoterProxy", - "examples": ["e.g. 0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "votingEscrow": { - "type": "string", - "title": "votingEscrow", - "examples": ["e.g. 0xC128a9954e6c874eA3d62ce62B468bA073093F25"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - } - }, - "required": ["auraLocker", "auraVoterProxy", "votingEscrow"], - "additionalProperties": false - } - } -} diff --git a/src/strategies/aura-vlaura-vebal/README.md b/src/strategies/aura-vlaura-vebal/README.md deleted file mode 100644 index 2ccf3397c..000000000 --- a/src/strategies/aura-vlaura-vebal/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# aura-vlaura-vebal - -This strategy returns proportional voting power for vlAURA holders based on system owned veBAL. - -For example: -- there are 10000 vlAURA total supply -- a user has 2000 vlAURA (voting power, delegated to them) -- Aura's voterProxy owns 100k veBAL - -In this example, the user has 20k veBAL voting power as they own 20% of the vlAURA voting power. - -_Note: When depositing to the auraLocker, a user does not receive vlAURA until the next epoch has begun (Thursday at 00:00 UTC)_ - -## Params - -- `auraLocker` - (**Required**, `string`) Address of AuraLocker (vlAURA) contract -- `auraVoterProxy` - (**Required**, `string`) Address of Aura VoterProxy contract -- `votingEscrow` - (**Required**, `string`) Address of Balancer VotingEscrow contract - -Here is an example of parameters: - -```json -{ - "auraLocker": "0x3Fa73f1E5d8A792C80F426fc8F84FBF7Ce9bBCAC", - "auraVoterProxy": "0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2", - "votingEscrow": "0xC128a9954e6c874eA3d62ce62B468bA073093F25" -} -``` diff --git a/src/strategies/aura-vlaura-vebal/examples.json b/src/strategies/aura-vlaura-vebal/examples.json deleted file mode 100644 index 6d4f34d9c..000000000 --- a/src/strategies/aura-vlaura-vebal/examples.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "aura-vlaura-vebal", - "params": { - "auraLocker": "0x3Fa73f1E5d8A792C80F426fc8F84FBF7Ce9bBCAC", - "auraVoterProxy": "0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2", - "votingEscrow": "0xC128a9954e6c874eA3d62ce62B468bA073093F25" - } - }, - "network": "1", - "addresses": [ - "0xB1f881f47baB744E7283851bC090bAA626df931d", - "0x808af82545A721C06D1FcCEbea915a6F5128BeF9", - "0x0CAd1d5ea8b4EeE26959cC00B4A3677f7A11e40F" - ], - "snapshot": 14974420 - } -] diff --git a/src/strategies/aura-vlaura-vebal/index.ts b/src/strategies/aura-vlaura-vebal/index.ts deleted file mode 100644 index 8b4e6886c..000000000 --- a/src/strategies/aura-vlaura-vebal/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = '0xMaharishi'; -export const version = '0.1.0'; - -const abi = [ - 'function getVotes(address account) external view returns (uint256)', - 'function totalSupply() public view returns (uint256)', - 'function balanceOf(address account) public view returns (uint256)' -]; - -interface Params { - auraLocker: string; - auraVoterProxy: string; - votingEscrow: string; -} - -interface Response { - vlAuraTotalSupply: BigNumber; - vlAuraVotes: Record; - veBalOwnedByAura: BigNumber; -} - -export async function strategy( - space, - network, - provider, - addresses, - options: Params, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - multi.call('vlAuraTotalSupply', options.auraLocker, 'totalSupply', []); - addresses.forEach((address) => - multi.call(`vlAuraVotes.${address}`, options.auraLocker, 'getVotes', [ - address - ]) - ); - multi.call('veBalOwnedByAura', options.votingEscrow, 'balanceOf', [ - options.auraVoterProxy - ]); - const res: Response = await multi.execute(); - - return Object.fromEntries( - Object.entries(res.vlAuraVotes).map(([address, votes]) => [ - address, - parseFloat( - formatUnits( - res.veBalOwnedByAura.mul(votes).div(res.vlAuraTotalSupply), - 18 - ) - ) - ]) - ); -} diff --git a/src/strategies/aura-vlaura-vebal/schema.json b/src/strategies/aura-vlaura-vebal/schema.json deleted file mode 100644 index 85b8c6d26..000000000 --- a/src/strategies/aura-vlaura-vebal/schema.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "Strategy", - "type": "object", - "properties": { - "auraLocker": { - "type": "string", - "title": "auraLocker", - "examples": ["e.g. 0x3Fa73f1E5d8A792C80F426fc8F84FBF7Ce9bBCAC"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "auraVoterProxy": { - "type": "string", - "title": "auraVoterProxy", - "examples": ["e.g. 0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "votingEscrow": { - "type": "string", - "title": "votingEscrow", - "examples": ["e.g. 0xC128a9954e6c874eA3d62ce62B468bA073093F25"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - } - }, - "required": ["auraLocker", "auraVoterProxy", "votingEscrow"], - "additionalProperties": false - } - } -} diff --git a/src/strategies/avn-balance-of-staked/examples.json b/src/strategies/avn-balance-of-staked/examples.json deleted file mode 100644 index eaf1f1141..000000000 --- a/src/strategies/avn-balance-of-staked/examples.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "avn-balance-of-staked", - "params": { - "vrAddress": "0x8D9D42AF6D048B296cDE8B28693b276dF5e8bfB1", - "vr2Address": "0xE30dE0b5d61B297Da4CA00cFF583CaEdEDaa0C42", - "tokenAddress": "0x0d88ed6e74bbfd96b831231638b66c05571e824f", - "symbol": "AVT", - "decimals": 18, - "start": 11750517 - } - }, - "network": "1", - "addresses": ["0xeEfbADa5539f9ae2c87C1151Cdb2057398383342"], - "snapshot": 12273608 - } -] diff --git a/src/strategies/avn-balance-of-staked/index.ts b/src/strategies/avn-balance-of-staked/index.ts deleted file mode 100644 index 519e6de85..000000000 --- a/src/strategies/avn-balance-of-staked/index.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; - -export const author = 'andrew-frank'; -export const version = '0.1.1'; - -const AVT_ABI = [ - { - constant: true, - inputs: [ - { - internalType: 'address', - name: 'account', - type: 'address' - } - ], - name: 'balanceOf', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256' - } - ], - payable: false, - stateMutability: 'view', - type: 'function' - } -]; - -const VR_ABI = [ - { - inputs: [ - { internalType: 'uint8', name: 'node', type: 'uint8' }, - { internalType: 'address', name: 'staker', type: 'address' } - ], - name: 'getStakerBalance', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function' - } -]; - -const NUM_NODES = 10; -// [0, 1, ... , 9] for convinience -const NODES_INDICES = Array.from(Array(NUM_NODES).keys()); -const STAKES_MULTIPLIER = 1; - -class EthCall { - constructor( - public readonly contract: string, - public readonly method: string, - public readonly args: Array - ) {} - get ethCall(): any[] { - return [this.contract, this.method, this.args]; - } -} - -/** creates flat array of eth calls for each user's stake in each VR contract in each node */ -function serializeVrMultiCalls( - vr1Address: string, - vr2Address: string, - userAddresses: string[] -) { - // [ [0, 1, ... , 19], [0, 1, ... , 19] , ..., [0, 1, ... 19], ... ] - const userCalls: EthCall[][] = userAddresses.map((user: string) => { - const method = 'getStakerBalance'; - // map to objects to prevent flatting eth call arrays - const vr1Calls = NODES_INDICES.map( - (node: number) => new EthCall(vr1Address, method, [node, user]) - ); - const vr2Calls = NODES_INDICES.map( - (node: number) => new EthCall(vr2Address, method, [node, user]) - ); - // * [0-9] calls for each node in VR1 - // * [10-19] calls for each node in VR2 - return vr1Calls.concat(vr2Calls); - }); - // flat it and map to a list of the call primitives - const objCalls = userCalls.flat(); - return objCalls.map((obj) => obj.ethCall); -} - -/** splits array into chunks */ -function chunkArray(arr: T[], length: number): T[][] { - const chunks: T[][] = []; - let i = 0; - const n = arr.length; - - while (i < n) { - chunks.push(arr.slice(i, (i += length))); - } - - return chunks; -} - -/** sums big numbers in array */ -function sumNumbers(arr: BigNumber[]): BigNumber { - return arr.reduce((previus, current) => { - return previus.add(current[0]); - }, BigNumber.from(0)); -} - -/** - * Parses multicall response - * @param response multicall response - * @returns summed AVT staked for each user in every node in every VR contract - */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function parseVrResponse(response: BigNumber[], users: string[]): BigNumber[] { - return chunkArray(response, 2 * NUM_NODES).map(sumNumbers); -} - -export async function strategy( - _space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - // users AVTs - const avtResponses: Array<[BigNumber]> = await multicall( - network, - provider, - AVT_ABI, - addresses.map((address: any) => [ - options.tokenAddress, - 'balanceOf', - [address] - ]), - { blockTag } - ); - const avtValues = avtResponses.map((value) => value[0]); - - // users AVT staked in VR contracts - const vrMultiResponse = await multicall( - network, - provider, - VR_ABI, - serializeVrMultiCalls(options.vrAddress, options.vr2Address, addresses), - { blockTag } - ); - const stakes = parseVrResponse(vrMultiResponse, addresses); - - // calculate the scores - const vrVotes = stakes.map((v) => v.mul(STAKES_MULTIPLIER)); - const scores = avtValues.map((value, i) => { - return value.add(vrVotes[i]); - }); - - return Object.fromEntries( - scores.map((value, i) => [ - addresses[i], - parseFloat(formatUnits(value.toString(), options.decimals)) - ]) - ); -} diff --git a/src/strategies/babywealthyclub/examples.json b/src/strategies/babywealthyclub/examples.json deleted file mode 100644 index d77fe1f8a..000000000 --- a/src/strategies/babywealthyclub/examples.json +++ /dev/null @@ -1,23 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "babywealthyclub", - "params": { - "address": "0x1f9655c660B915b4344b71e2A6d3155C57AD1bB6", - "symbol": "BRC", - "decimals": "9", - "weighted": 20000000000 - } - }, - "network": "56", - "addresses": [ - "0x7F57612b01689a0E4f133730AE20cC19E3dF0208", - "0x4A7998DF2Cd16815271bb6b7d3aE7EB30f50a73a", - "0x08D816526BdC9d077DD685Bd9FA49F58A5Ab8e48", - "0x21fF20E7e1B820020415707298b92299CF0951fE", - "0xa7A01B93B889ff0639d5ec02914A77529924a46F" - ], - "snapshot": 23976608 - } -] diff --git a/src/strategies/babywealthyclub/index.ts b/src/strategies/babywealthyclub/index.ts deleted file mode 100644 index aea541d04..000000000 --- a/src/strategies/babywealthyclub/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; - -export const author = 'gp6284'; -export const version = '0.1.0'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)' -]; -const BWC_ADDRESS = '0xb7F7c7D91Ede27b019e265F8ba04c63333991e02'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const erc20Balance = await erc20BalanceOfStrategy( - space, - network, - provider, - addresses, - options, - snapshot - ); - const response = await multicall( - network, - provider, - abi, - addresses.map((address: any) => [BWC_ADDRESS, 'balanceOf', [address]]), - { blockTag } - ); - - return Object.fromEntries( - addresses.map((address, i) => [ - address, - parseFloat(formatUnits(response[i].toString(), 0)) > 0 - ? Math.floor( - erc20Balance[addresses[i]] / (options.weighted || 10000000000) - ) - : 0 - ]) - ); -} diff --git a/src/strategies/badgeth/examples.json b/src/strategies/badgeth/examples.json deleted file mode 100644 index 7e78410a1..000000000 --- a/src/strategies/badgeth/examples.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "name": "Badgeth", - "strategy": { - "name": "badgeth", - "params": { - "symbol": "BADGETH" - } - }, - "network": "4", - "snapshot": 8876318, - "addresses": [ - "0xBcE0b2edcA1fBE32891Ac6096b8ea7408dd099c2", - "0x8cABEC8fe71A3604E21e6E2BB55463EC6e26fBf8", - "0x85924aA0B2cb5a0BbeC583Dd090bF7CEdBa5D2Ea", - "0x9149B2b87159c4CC9e2f10C2711357720Da4DA08", - "0xa0710d3b4BA0f848f7edf9CC827aF70A183EAd26", - "0xAE1220f6bFEb414Ed0A95fbb5A6Ecc303b10aa46", - "0xebe986802F7858E1919451C6Ff893e294F31CE54", - "0x2d7cAA8462023af022A5004dA7b781b8ccF81Da7", - "0xE26217836Dd71f49c58a68aD70DabFA1E6d0B75b", - "0x7C572bE1751DdCFeE930286836bF21E6d87c10e6", - "0x6fDcfFDBa2699543a926f0C092F769f3302D3519", - "0x0f6feb3ba20c56e94cfbcd98339e99bce629d912" - ] - } -] diff --git a/src/strategies/badgeth/index.ts b/src/strategies/badgeth/index.ts deleted file mode 100644 index 5135caf32..000000000 --- a/src/strategies/badgeth/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { subgraphRequest } from '../../utils'; -import { getAddress } from '@ethersproject/address'; - -export const author = 'Badgeth'; -export const version = '0.1.0'; - -const BADGETH_SUBGRAPH_URL = - 'https://api.thegraph.com/subgraphs/name/hardforksoverknives/badgeth-dev'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const params = { - voters: { - __args: { - where: { - id_in: addresses, - votingPower_gt: 0 - }, - first: 1000, - orderBy: 'votingPower', - orderDirection: 'desc' - }, - id: true, - votingPower: true - } - }; - if (snapshot !== 'latest') { - // @ts-ignore - params.voters.__args.block = { number: snapshot }; - } - - const score = {}; - const result = await subgraphRequest(BADGETH_SUBGRAPH_URL, params); - if (result && result.voters) { - result.voters.forEach((voter) => { - score[getAddress(voter.id)] = parseInt(voter.votingPower); - }); - } - - return score || {}; -} diff --git a/src/strategies/balancer-delegation/examples.json b/src/strategies/balancer-delegation/examples.json deleted file mode 100644 index b6a21bf60..000000000 --- a/src/strategies/balancer-delegation/examples.json +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "balancer-delegation", - "params": { - "address": "0xba100000625a3754423978a60c9317c58a424e3D", - "symbol": "BAL BPT V1+V2 (delegated)", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x808A023B72260170c95d831F589A1ae0DCa1e43E", - "0xC0810E5f17915dFe97b57947A3d84094572689ac", - "0x7dd508a1e4Da1243789B799a480f8B45e58b1B5b", - "0x512fce9B07Ce64590849115EE6B32fd40eC0f5F3", - "0x055b441729041DC9F25a296f48604bf6a36B12F2", - "0x3839AcF1ee7699D1F46b1BE840D8aD8317FDf757", - "0x2FAf55a544c5F73666438BC185aeCC9D685E6E3C", - "0xa586Cbf75fB4b8987bAb7d24BE4545fcaa0e757C", - "0x78993bfE37DA5a8DF2D5a8d7213A41c26B20B49D", - "0xB0331b22161cA290A15f825A29C008dCB5e1ff68", - "0x8b6545E4Fd7D5Cc858BE7Ad449F88eF1b2f7a577", - "0xdB19c47E87Ed3Ff37425a99B9Dee1f4920F755b9", - "0x562821C81BBbFFa42443064917Ee4D90036Fba7c", - "0x2fcDa75f0038D54BD0f0d88D5Db2a62EA298AF36", - "0x39D787fdf7384597C7208644dBb6FDa1CcA4eBdf", - "0xD142c9cfE2F0BEF4c14594358fB65aeE1B726d2C" - ], - "snapshot": 11706500 - } -] diff --git a/src/strategies/balancer-delegation/index.ts b/src/strategies/balancer-delegation/index.ts deleted file mode 100644 index 5151cb484..000000000 --- a/src/strategies/balancer-delegation/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { strategy as delegation } from '../delegation'; - -export const author = 'bonustrack'; -export const version = '0.1.0'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - return await delegation( - space, - network, - provider, - addresses, - { - strategies: [ - { - name: 'balancer', - params: options - } - ] - }, - snapshot - ); -} diff --git a/src/strategies/balancer-erc20-internal-balance-of/README.md b/src/strategies/balancer-erc20-internal-balance-of/README.md deleted file mode 100644 index 78369ef8f..000000000 --- a/src/strategies/balancer-erc20-internal-balance-of/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# balancer-erc20-internal-balance-of - -This returns the internal balances of the voters for a specific ERC20 token in the Balancer V2 Vault. - -Here is an example of parameters: - -```json -{ - "vault": "0xba12222222228d8ba445958a75a0704d566bf2c8", - "token": "0xba100000625a3754423978a60c9317c58a424e3D", - "symbol": "BAL", - "decimals": 18 -} -``` diff --git a/src/strategies/balancer-erc20-internal-balance-of/examples.json b/src/strategies/balancer-erc20-internal-balance-of/examples.json deleted file mode 100644 index b5fa0811e..000000000 --- a/src/strategies/balancer-erc20-internal-balance-of/examples.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "name": "Internal Balance", - "strategy": { - "name": "balancer-erc20-internal-balance-of", - "params": { - "vault": "0xba12222222228d8ba445958a75a0704d566bf2c8", - "token": "0xba100000625a3754423978a60c9317c58a424e3D", - "symbol": "BAL", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0xa478c2975ab1ea89e8196811f51a7b7ade33eb11", - "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e", - "0x1d5E65a087eBc3d03a294412E46CE5D6882969f4", - "0x1f254336E5c46639A851b9CfC165697150a6c327", - "0x2ec3F80BeDA63Ede96BA20375032CDD3aAfb3030", - "0x4AcBcA6BE2f8D2540bBF4CA77E45dA0A4a095Fa2", - "0x4F3D348a6D09837Ae7961B1E0cEe2cc118cec777", - "0x6D7f23A509E212Ba7773EC1b2505d1A134f54fbe", - "0x07a1f6fc89223c5ebD4e4ddaE89Ac97629856A0f", - "0x8d5F05270da470e015b67Ab5042BDbE2D2FEFB48", - "0x8d07D225a769b7Af3A923481E1FdF49180e6A265", - "0x8f60501dE5b9b01F9EAf1214dbE1924aA97F7fd0", - "0x9B8e8dD9151260c21CB6D7cc59067cd8DF306D58", - "0x17ea92D6FfbAA1c7F6B117c1E9D0c88ABdc8b84C", - "0x38C0039247A31F3939baE65e953612125cB88268", - "0x794846f3291E55E00662d37Ef048Aa716dF9ECbf" - ], - "snapshot": 13043171 - } -] diff --git a/src/strategies/balancer-erc20-internal-balance-of/index.ts b/src/strategies/balancer-erc20-internal-balance-of/index.ts deleted file mode 100644 index b3a808a2d..000000000 --- a/src/strategies/balancer-erc20-internal-balance-of/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = 'gerrrg'; -export const version = '0.0.1'; - -const abi = [ - 'function getInternalBalance(address user, address[] tokens) external view returns (uint256[] balances)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - addresses.forEach((address) => - multi.call(address, options.vault, 'getInternalBalance', [ - address, - [options.token] - ]) - ); - const result: Record = await multi.execute(); - - return Object.fromEntries( - Object.entries(result).map(([address, balance]) => [ - address, - parseFloat(formatUnits(balance[0], options.decimals)) - ]) - ); -} diff --git a/src/strategies/blockzerolabs-cryptonauts/README.md b/src/strategies/blockzerolabs-cryptonauts/README.md deleted file mode 100644 index 9bd8c17bd..000000000 --- a/src/strategies/blockzerolabs-cryptonauts/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# blockzerolabs-cryptonauts - -This strategy is to be used for Blockzero Labs. It will return the underlying XIO tokens held in the NFT Vault. These -XIO can then be used towards voting. - -Here is an example of parameters: - -```json -{ - "symbol": "CRYPTONAUTS", - "totalSupply": 110, - "decimals": 18, - "_nftTokenAddress": "0x1d48ddb875d1e540ee0715dbb7b019d02a3ba4db", - "_vaultAddress": "0x6d9d87fd57fb8ae3bf005e564a950124fc83dd1a" -} -``` diff --git a/src/strategies/blockzerolabs-cryptonauts/examples.json b/src/strategies/blockzerolabs-cryptonauts/examples.json deleted file mode 100644 index e2c7d07b5..000000000 --- a/src/strategies/blockzerolabs-cryptonauts/examples.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "blockzerolabs-cryptonauts", - "params": { - "symbol": "CRYPTONAUTS", - "totalSupply": 110, - "decimals": 18, - "_nftTokenAddress": "0x1d48ddb875d1e540ee0715dbb7b019d02a3ba4db", - "_vaultAddress": "0x6d9d87fd57fb8ae3bf005e564a950124fc83dd1a" - } - }, - "network": "1", - "addresses": [ - "0xa478c2975ab1ea89e8196811f51a7b7ade33eb11", - "0x693a3fc85e373dc89239e9eda24edbf4c1d8fefd", - "0x2231e35dae9f0c33141c9eab05b1c1bc4fb1df36" - ], - "snapshot": 13594719 - } -] diff --git a/src/strategies/blockzerolabs-cryptonauts/index.ts b/src/strategies/blockzerolabs-cryptonauts/index.ts deleted file mode 100644 index 36a23f879..000000000 --- a/src/strategies/blockzerolabs-cryptonauts/index.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; -import { getAddress } from '@ethersproject/address'; -import { Multicaller } from '../../utils'; -import { formatUnits } from '@ethersproject/units'; - -export const author = 'blockzerolabs'; -export const version = '0.1.0'; - -const abi = [ - 'function totalSupply() external view returns (uint256)', - 'function exists(uint256) external view returns (bool)', - 'function ownerOf(uint256) external view returns (address)', - 'function balanceOf(uint256) external view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - // Step 1. Retrieve the total supply of Cryptonauts - const totalSupply = options.totalSupply; - - // Step 2. Determine which ones still exist (i.e not burned) - const nftExistsMulti = new Multicaller(network, provider, abi, { blockTag }); - for (let _nftId = 0; _nftId < totalSupply; _nftId++) { - nftExistsMulti.call( - _nftId, - options._nftTokenAddress, // This is a static address - 'exists', - [_nftId] - ); - } - const nftExists: Record = await nftExistsMulti.execute(); - - // Step 3. Determine owners for NFTs that still exist - const nftOwnersMulti = new Multicaller(network, provider, abi, { blockTag }); - for (let _nftId = 0; _nftId < totalSupply; _nftId++) { - // If the NFT exists, get the owner - if (nftExists[_nftId]) { - nftOwnersMulti.call(_nftId, options._nftTokenAddress, 'ownerOf', [ - _nftId - ]); - } - } - const nftOwners: Record = await nftOwnersMulti.execute(); - - // Step 4. Get the balance of each NFT from the Vault - const vaultBalanceMulti = new Multicaller(network, provider, abi, { - blockTag - }); - for (let _nftId = 0; _nftId < totalSupply; _nftId++) { - // If the NFT exists, get the owner - if (nftExists[_nftId]) { - vaultBalanceMulti.call(_nftId, options._vaultAddress, 'balanceOf', [ - _nftId - ]); - } - } - const vaultBalance: Record = - await vaultBalanceMulti.execute(); - - // Iterate over each address provided - const balances: Record = {}; - addresses.forEach((address) => { - let totalBalance = BigNumber.from(0); - // Iterate over each NFT - for (let _nftId = 0; _nftId < totalSupply; _nftId++) { - // Ensure the NFT Exists before continuing to add balance - if (nftExists[_nftId]) { - // Ensure this address is the owner before continuing to add balance - if (nftOwners[_nftId] == getAddress(address)) { - // Add the balance - totalBalance = totalBalance.add(vaultBalance[_nftId]); - } - } - } - // Format the balance with 18 decimals (fixed) - balances[address] = parseFloat(formatUnits(totalBalance, options.decimals)); - }); - - return balances; -} diff --git a/src/strategies/bubblegum-kids/README.md b/src/strategies/bubblegum-kids/README.md new file mode 100644 index 000000000..a5571fdb1 --- /dev/null +++ b/src/strategies/bubblegum-kids/README.md @@ -0,0 +1,3 @@ +# bubblegum-kids + +This strategy determines voting power based on an address' holdings of Bubblegum Kids and Bubblegum Puppies NFTs, including staked NFTs. diff --git a/src/strategies/bubblegum-kids/examples.json b/src/strategies/bubblegum-kids/examples.json new file mode 100644 index 000000000..d659782ef --- /dev/null +++ b/src/strategies/bubblegum-kids/examples.json @@ -0,0 +1,22 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "bubblegum-kids" + }, + "network": "1", + "addresses": [ + "0xfec4465b6aeac9c90e552ae7766bdc0ee0d8dbc9", + "0x146860d31ab0f6dc858106f1a5d7a52bd6c86d1d", + "0x8edc7272a444057d1556df2a1173e6799c3c5ae0", + "0x8af2bd409ea0290029c14aac5d1be77d5c9a114f", + "0x41636ddf4abb1aa78863101626acdc861d4e8a0b", + "0xd341108b067cd308cf8cc7e13fb9982b0cba2f4e", + "0xdf05770b388ee62362917bea9dc96f0dee3b246e", + "0xf98e849c4b0de9750630f1c23c1bea9e79b4edb3", + "0xf68b74b89093315c5bf018348886c9fdd8308d7f", + "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5" + ], + "snapshot": 18589032 + } +] diff --git a/src/strategies/bubblegum-kids/index.ts b/src/strategies/bubblegum-kids/index.ts new file mode 100644 index 000000000..d9382344b --- /dev/null +++ b/src/strategies/bubblegum-kids/index.ts @@ -0,0 +1,98 @@ +import { getAddress } from '@ethersproject/address'; +import { BigNumberish } from '@ethersproject/bignumber'; +import { formatUnits } from '@ethersproject/units'; +import { Multicaller } from '../../utils'; + +export const author = 'flaflafla'; +export const version = '0.1.1'; + +// contracts available on Ethereum mainnet and Goerli testnet +const kidsAddressByNetwork = { + 1: '0xa5ae87B40076745895BB7387011ca8DE5fde37E0', + 5: '0x66B04973b83ea796960D6f8ea22856714e01765f' +}; + +const puppiesAddressByNetwork = { + 1: '0x86e9C5ad3D4b5519DA2D2C19F5c71bAa5Ef40933', + 5: '0x053A3213E75b78c0b80b2f88e243cf519e834c02' +}; + +const stakingAddressByNetwork = { + 1: '0xf48415039913DBdF17e337e681de922A9cb04010', + 5: '0xe5f1433b6eCc6bE74E413b54f4c1eA2671b1cA0F' +}; + +const abi = [ + // Kids and Puppies contracts + 'function balanceOf(address owner) external view returns (uint256)', + // staking contract + 'function depositsOf(address account) external view returns (uint256[][2])' +]; + +/** + * Voting power is calculated as the sum of a user's Bubblegum Kids + * and Bubblegum Puppies holdings, with each NFT counting as 1. Kids + * and Puppies deposited to the staking contract also count as 1. + */ +export async function strategy( + _space, + network, + provider, + addresses, + _options, + snapshot +): Promise> { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + const multi = new Multicaller(network, provider, abi, { blockTag }); + + addresses.forEach((address) => { + // get Kids balance + multi.call(`${address}.kids`, kidsAddressByNetwork[network], 'balanceOf', [ + address + ]); + + // get Puppies balance + multi.call( + `${address}.puppies`, + puppiesAddressByNetwork[network], + 'balanceOf', + [address] + ); + + // get staking deposits (returns and array of two arrays: + // a list of Kids ids and a list of Puppies ids) + multi.call( + `${address}.staking`, + stakingAddressByNetwork[network], + 'depositsOf', + [address] + ); + }); + + const result: { + address: { + kids: BigNumberish; + puppies: BigNumberish; + staking: [Array, Array]; + }; + } = await multi.execute(); + + return Object.fromEntries( + Object.entries(result).map(([address, { kids, puppies, staking }]) => { + const [stakedKids, stakedPuppies] = staking; + + const kidsCount = parseFloat(formatUnits(kids, 0)); + const stakedKidsCount = stakedKids.length; + + const puppiesCount = parseFloat(formatUnits(puppies, 0)); + const stakedPuppiesCount = stakedPuppies.length; + + // calculate total voting power + const votingPower = + kidsCount + stakedKidsCount + puppiesCount + stakedPuppiesCount; + + return [getAddress(address), votingPower]; + }) + ); +} diff --git a/src/strategies/cake/examples.json b/src/strategies/cake/examples.json index ea44a6127..9a243885a 100644 --- a/src/strategies/cake/examples.json +++ b/src/strategies/cake/examples.json @@ -9,15 +9,15 @@ }, "network": "56", "addresses": [ - "0x8b017905DC96B38f817473dc885F84D4C76bC113", - "0x21fF20E7e1B820020415707298b92299CF0951fE", - "0xa7A01B93B889ff0639d5ec02914A77529924a46F", - "0x68e0c606036CAcb83D1755ca31f79630468F044e" + "0x4385c3ca394021a9411B925C94b23a89a3814Ed5", + "0x5B7A2dd16767dD598BA6a432c5c93D167477fB99", + "0x5224F9c440589b2D96F74ac9BA65C648260A4480", + "0xcD8efe4072A489DAB0f662FbA328ECc755E7A66f" ], - "snapshot": 16308675 + "snapshot": 34337773 }, { - "name": "Example query - before 16300686 snapshot", + "name": "Example query - before 34337773 snapshot", "strategy": { "name": "cake", "params": { @@ -28,8 +28,9 @@ "addresses": [ "0x8b017905DC96B38f817473dc885F84D4C76bC113", "0x21fF20E7e1B820020415707298b92299CF0951fE", + "0xa7A01B93B889ff0639d5ec02914A77529924a46F", "0x68e0c606036CAcb83D1755ca31f79630468F044e" ], - "snapshot": 15204010 + "snapshot": 16308675 } ] diff --git a/src/strategies/cake/index.ts b/src/strategies/cake/index.ts index 497928e99..68d51ff29 100644 --- a/src/strategies/cake/index.ts +++ b/src/strategies/cake/index.ts @@ -36,6 +36,8 @@ const onChainVotingPower = { } }; +const veCakeBlockNumber = 34371669; + const abi = [ 'function getVotingPowerWithoutPool(address _user) view returns (uint256)' ]; @@ -142,6 +144,31 @@ async function getSmartChefStakedCakeAmount( }, {}); } +async function veCakeStrategy(addresses, network, provider, blockTag, options) { + let callData = addresses.map((address: any) => [ + onChainVotingPower.v1.address, + 'getVotingPowerWithoutPool', + [address.toLowerCase()] + ]); + + callData = [...chunk(callData, options.max || 400)]; + + const response: any[] = []; + for (const call of callData) { + const multiRes = await multicall(network, provider, abi, call, { + blockTag + }); + response.push(...multiRes); + } + + return Object.fromEntries( + response.map((value, i) => [ + addresses[i], + parseFloat(formatEther(value.toString())) + ]) + ); +} + export async function strategy( space, network, @@ -150,6 +177,15 @@ export async function strategy( options, snapshot ) { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + if ( + blockTag === 'latest' || + (typeof blockTag === 'number' && blockTag >= veCakeBlockNumber) + ) { + return veCakeStrategy(addresses, network, provider, blockTag, options); + } + const pools = await getPools(provider, snapshot); const userPoolBalance = await getSmartChefStakedCakeAmount( @@ -158,11 +194,9 @@ export async function strategy( addresses ); - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; if ( - blockTag === 'latest' || - (typeof blockTag === 'number' && - blockTag >= onChainVotingPower.v0.blockNumber) + typeof blockTag === 'number' && + blockTag >= onChainVotingPower.v0.blockNumber ) { let callData = addresses.map((address: any) => [ typeof blockTag === 'number' && diff --git a/src/strategies/capitaldao-staking/README.md b/src/strategies/capitaldao-staking/README.md deleted file mode 100644 index c90f799d3..000000000 --- a/src/strategies/capitaldao-staking/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Capital Dao Staking Token - -This strategy returns balances of the underlying token in Capital Dao Staking pools - -Here is an example of parameters: - -```json -{ - "symbol": "CDS", - "decimals": 18, - "tokenAddress": "0x6139b11c7CE407fb2AAEc3bFdFa97e3A21330843", - "stakingAddress": "0xe157B35C9E8798f61F537a117a5d059A883C8A6F", - "poolIndex": 0 -} -``` \ No newline at end of file diff --git a/src/strategies/capitaldao-staking/examples.json b/src/strategies/capitaldao-staking/examples.json deleted file mode 100644 index 8b76e2f53..000000000 --- a/src/strategies/capitaldao-staking/examples.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "name": "Capital Dao Staked Token", - "strategy": { - "name": "capitaldao-staking", - "params": { - "symbol": "CDS", - "decimals": 18, - "tokenAddress": "0x6139b11c7CE407fb2AAEc3bFdFa97e3A21330843", - "stakingAddress": "0xe157B35C9E8798f61F537a117a5d059A883C8A6F", - "poolIndex": 0 - } - }, - "network": "4", - "addresses": [ - "0x95298790beb442f204e3864c5bd4073905185108", - "0xCe44798440952EBd75Cf1FDC62a996d28137eF30" - ], - "snapshot": 10343903 - } -] diff --git a/src/strategies/capitaldao-staking/index.ts b/src/strategies/capitaldao-staking/index.ts deleted file mode 100644 index 70d65a9b8..000000000 --- a/src/strategies/capitaldao-staking/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { multicall } from '../../utils'; -import { formatUnits } from '@ethersproject/units'; -import { BigNumber } from '@ethersproject/bignumber'; - -export const author = 'capitaldao'; -export const version = '0.1.0'; - -const masterChefAbi = [ - 'function users(uint256, address) view returns (uint256 amount, uint256 rewardDebt, uint256 lastDepositAt)' -]; - -function bn(num: any): BigNumber { - return BigNumber.from(num.toString()); -} - -export async function strategy( - _space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - // Get staked LP in staking contract - const response = await multicall( - network, - provider, - masterChefAbi, - [ - ...addresses.map((address: any) => [ - options.stakingAddress, - 'users', - [options.poolIndex, address] - ]) - ], - { blockTag } - ); - - return Object.fromEntries( - response.map((user, i) => { - const parsedAmount = parseFloat( - formatUnits(bn(user.amount), options.decimal) - ); - return [addresses[i], parsedAmount]; - }) - ); -} diff --git a/src/strategies/clipper-staked-sail/README.md b/src/strategies/clipper-staked-sail/README.md new file mode 100644 index 000000000..caab3c5b5 --- /dev/null +++ b/src/strategies/clipper-staked-sail/README.md @@ -0,0 +1,15 @@ +# AdmiralDao Staked Sail + +This strategy returns the voting power of an address that has staked sail in the vesail staking contract [VeSail](https://etherscan.io/token/0x26fe2f89a1fef1bc90b8a89d8ad18a1891166ff5). +The voting power is calculated as: +- The amount of vesail held in their address. +- The result is then used to interract with the tosail method in the contract to get the sail equivalent. +- Lastly it will take the equivalent sail amount and apply a square root operation. + +```JSON +{ + "strategies": [ + ["clipper-staked-sail"] + ] +} +``` diff --git a/src/strategies/clipper-staked-sail/examples.json b/src/strategies/clipper-staked-sail/examples.json new file mode 100644 index 000000000..16cc02da6 --- /dev/null +++ b/src/strategies/clipper-staked-sail/examples.json @@ -0,0 +1,19 @@ +[ + { + "name": "Example of clipper-staked-sail Strategy", + "strategy": { + "name": "clipper-staked-sail" + }, + "network": "1", + "decimals": 18, + "addresses": [ + "0x3334829670F9e8D309C9D9F318C4E6876755eDe2", + "0x8e70Ca936a2f2d81cBbF1Dc84Aabe4213C87b8E9", + "0x314C0695273Ba259Bb60074f2C92c67AC7ae6D40", + "0x2c2e209465D5312e6dF0cd5F7D1066f1aff9a953", + "0x4d768cFDb6E0077aD0a971678fa84DBcac32CE62", + "0x26f8435Bf2a7B8b4771F0D5317beb09fB1F197C3" + ], + "snapshot": 18558302 + } +] diff --git a/src/strategies/clipper-staked-sail/index.ts b/src/strategies/clipper-staked-sail/index.ts new file mode 100644 index 000000000..83154b61f --- /dev/null +++ b/src/strategies/clipper-staked-sail/index.ts @@ -0,0 +1,90 @@ +import { multicall } from '../../utils'; +import { BigNumber } from '@ethersproject/bignumber'; + +const vesailTokenAddress = '0x26fE2f89a1FEf1bC90b8a89D8AD18a1891166ff5'; +const decimals = 18; + +export const author = 'cryptotrades20'; +export const version = '0.1.0'; + +//read vesail balance +const vesailBalanceOfABI = [ + 'function balanceOf(address account) view returns (uint256)' +]; + +//vesail to sail conversion +const toSAILABI = [ + 'function toSAIL(uint256 sailAmount) view returns (uint256)' +]; + +/** + * Voting power is calculated as the conversion of their vesail balance to sail + * Then take that sail amount and apply square root operation to it + */ +async function getVesailBalance(network, provider, snapshot, addresses) { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + const response = await multicall( + network, + provider, + vesailBalanceOfABI, + addresses.map((address) => [vesailTokenAddress, 'balanceOf', [address]]), + { blockTag } + ); + return response.map((result) => result[0]); +} + +//read vesail to sail balance +async function readToSail( + network, + provider, + snapshot, + addresses, + vesailBalances +) { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + const response = await multicall( + network, + provider, + toSAILABI, + addresses.map((address, index) => [ + vesailTokenAddress, + 'toSAIL', + [vesailBalances[index]] + ]), + { blockTag } + ); + return response.map((result) => result[0]); +} + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +) { + const vesailBalances = await getVesailBalance( + network, + provider, + snapshot, + addresses + ); + const sailAmounts = await readToSail( + network, + provider, + snapshot, + addresses, + vesailBalances + ); + + return Object.fromEntries( + addresses.map((address, index) => [ + address, + //square root the resulting vesail to sail amount + Math.sqrt( + Number(BigNumber.from(sailAmounts[index].toString())) / 10 ** decimals + ) + ]) + ); +} diff --git a/src/strategies/clqdr-balance-with-lp/examples.json b/src/strategies/clqdr-balance-with-lp/examples.json deleted file mode 100644 index e497c6675..000000000 --- a/src/strategies/clqdr-balance-with-lp/examples.json +++ /dev/null @@ -1,35 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "clqdr-balance-with-lp", - "params": { - "address": "0x814c66594a22404e101FEcfECac1012D8d75C156", - "symbol": "cLQDR", - "decimals": 18 - } - }, - "network": "250", - "addresses": [ - "0xf4c5b06ff9cd8f685ddcc58202597e56f1c0faee", - "0x70ECC7FecAea8D67e820035ED48c53706E7F2079", - "0xb5ae3c648709913ef9739e9f6edb5a821c6ab160", - "0x9675fe51fcfa05dbff4d027706f0a97b74fe5dc7", - "0x85644679fd440cd55c7046f2748ec5479cb3c3ab", - "0x1d5E65a087eBc3d03a294412E46CE5D6882969f4", - "0x1f254336E5c46639A851b9CfC165697150a6c327", - "0x2ec3F80BeDA63Ede96BA20375032CDD3aAfb3030", - "0x4AcBcA6BE2f8D2540bBF4CA77E45dA0A4a095Fa2", - "0x4F3D348a6D09837Ae7961B1E0cEe2cc118cec777", - "0x6D7f23A509E212Ba7773EC1b2505d1A134f54fbe", - "0x07a1f6fc89223c5ebD4e4ddaE89Ac97629856A0f", - "0x8d5F05270da470e015b67Ab5042BDbE2D2FEFB48", - "0x8d07D225a769b7Af3A923481E1FdF49180e6A265", - "0x8f60501dE5b9b01F9EAf1214dbE1924aA97F7fd0", - "0x9B8e8dD9151260c21CB6D7cc59067cd8DF306D58", - "0x17ea92D6FfbAA1c7F6B117c1E9D0c88ABdc8b84C", - "0x38C0039247A31F3939baE65e953612125cB88268" - ], - "snapshot": 49467339 - } -] diff --git a/src/strategies/clqdr-balance-with-lp/index.ts b/src/strategies/clqdr-balance-with-lp/index.ts deleted file mode 100644 index 6dcd9d743..000000000 --- a/src/strategies/clqdr-balance-with-lp/index.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller, multicall } from '../../utils'; - -export const author = 'LiquidDriver-finance'; -export const version = '0.0.1'; - -const liquidMasterAddress = '0x6e2ad6527901c9664f016466b8DA1357a004db0f'; -const beetsMasterAddress = '0x8166994d9ebBe5829EC86Bd81258149B87faCfd3'; -const lpAddress = '0xEAdCFa1F34308b144E96FcD7A07145E027A8467d'; -const beetsVaultAddress = '0x20dd72Ed959b6147912C2e529F0a0C651c33c9ce'; -const clqdrPoolId = - '0xeadcfa1f34308b144e96fcd7a07145e027a8467d000000000000000000000331'; - -const contractAbi = [ - 'function userInfo(uint256, address) view returns (uint256 amount, int256 rewardDebt)', - 'function totalSupply() view returns (uint256)', - 'function balanceOf(address _owner) view returns (uint256 balance)', - 'function getPoolTokens(bytes32 poolId) view returns (uint256[], uint256[], uint256)', - 'function getVirtualSupply() external view returns (uint256)' -]; - -const bn = (num: any): BigNumber => { - return BigNumber.from(num.toString()); -}; - -const addUserBalance = (userBalances, user: string, balance) => { - if (userBalances[user]) { - return (userBalances[user] = userBalances[user].add(balance)); - } else { - return (userBalances[user] = balance); - } -}; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const res = await multicall( - network, - provider, - contractAbi, - [ - [beetsVaultAddress, 'getPoolTokens', [clqdrPoolId]], - [lpAddress, 'getVirtualSupply', []] - ], - { blockTag } - ); - - const totalClqdrInBeets = bn(res[0][1][1]); - - const virtualSupply = bn(res[1]); - - const userCLqdrBalances: any = []; - for (let i = 0; i < addresses.length - 1; i++) { - userCLqdrBalances[addresses[i]] = bn(0); - } - - const clqdrMulti = new Multicaller(network, provider, contractAbi, { - blockTag - }); - addresses.forEach((address) => - clqdrMulti.call(address, options.address, 'balanceOf', [address]) - ); - const clqdrToken: Record = await clqdrMulti.execute(); - - Object.fromEntries( - Object.entries(clqdrToken).map(([address, balance]) => { - return addUserBalance(userCLqdrBalances, address, balance); - }) - ); - - const userLpBalances: any = []; - for (let i = 0; i < addresses.length - 1; i++) { - userLpBalances[addresses[i]] = bn(0); - } - - const multi = new Multicaller(network, provider, contractAbi, { blockTag }); - addresses.forEach((address) => - multi.call(address, lpAddress, 'balanceOf', [address]) - ); - const resultToken: Record = await multi.execute(); - - Object.fromEntries( - Object.entries(resultToken).map(([address, balance]) => { - return addUserBalance(userLpBalances, address, balance); - }) - ); - - const multiLiquidMaster = new Multicaller(network, provider, contractAbi, { - blockTag - }); - - addresses.forEach((address) => - multiLiquidMaster.call(address, liquidMasterAddress, 'userInfo', [ - '43', - address - ]) - ); - const resultLiquidMaster: Record = - await multiLiquidMaster.execute(); - - Object.fromEntries( - Object.entries(resultLiquidMaster).map(([address, balance]) => { - return addUserBalance(userLpBalances, address, balance[0]); - }) - ); - - const multiBeetsMaster = new Multicaller(network, provider, contractAbi, { - blockTag - }); - - addresses.forEach((address) => - multiBeetsMaster.call(address, beetsMasterAddress, 'userInfo', [ - '69', - address - ]) - ); - const resultBeetsMaster: Record = - await multiBeetsMaster.execute(); - - Object.fromEntries( - Object.entries(resultBeetsMaster).map(([address, balance]) => { - return addUserBalance(userLpBalances, address, balance[0]); - }) - ); - - return Object.fromEntries( - Object.entries(userLpBalances).map(([address, balance]) => { - const clqdrBalanceInLp = totalClqdrInBeets - // @ts-ignore - .mul(balance) - .div(virtualSupply); - const totalBalance = userCLqdrBalances[address].add(clqdrBalanceInLp); - return [ - address, - // @ts-ignore - parseFloat(formatUnits(totalBalance, options.decimals)) - ]; - }) - ); -} diff --git a/src/strategies/contract-call/README.md b/src/strategies/contract-call/README.md index 40becdc7b..011d62155 100644 --- a/src/strategies/contract-call/README.md +++ b/src/strategies/contract-call/README.md @@ -79,3 +79,13 @@ You can call methods with multiple inputs in any contract: ] } ``` + +### Params + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| address | `string` | | Contract address | +| decimals | `number` | 18 | Decimals of the output | +| symbol | `string` | optional | Symbol of the output | +| methodABI | `object` | | ABI of the method to call | +| output | `string` | optional | Output type of the method to call | diff --git a/src/strategies/coordinape/README.md b/src/strategies/coordinape/README.md deleted file mode 100644 index a318bd020..000000000 --- a/src/strategies/coordinape/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Coordinape - -Use Coordinape circle epoch tokens as voting power. - -Here is an example of parameters: -```json -{ - "symbol": "CIRCLE", - "circle": "92" -} -``` diff --git a/src/strategies/coordinape/examples.json b/src/strategies/coordinape/examples.json deleted file mode 100644 index 181bc2a5a..000000000 --- a/src/strategies/coordinape/examples.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "coordinape", - "params": { - "symbol": "CIRCLE", - "circle": "92" - } - }, - "network": "1", - "addresses": [ - "0x823b92d6a4b2AED4b15675c7917c9f922ea8ADAD", - "0xd337fccaec6ea113baacca3a41eb8766706a0706", - "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7" - ], - "snapshot": 13219000 - } -] diff --git a/src/strategies/coordinape/index.ts b/src/strategies/coordinape/index.ts deleted file mode 100644 index 620881e17..000000000 --- a/src/strategies/coordinape/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import fetch from 'cross-fetch'; -import { getAddress } from '@ethersproject/address'; - -export const author = 'bonustrack'; -export const version = '0.1.0'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const ts = (await provider.getBlock(snapshot)).timestamp; - const url = `https://api.coordinape.com/api/${options.circle}/token-gifts?latest_epoch=1×tamp=${ts}&snapshot=${snapshot}`; - const res = await fetch(url, { - method: 'GET', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - } - }); - const gifts = await res.json(); - const scores = {}; - gifts.forEach((gift) => { - const address = getAddress(gift.recipient_address); - if (!scores[address]) scores[address] = 0; - scores[address] += gift.tokens; - }); - return Object.fromEntries( - addresses.map((address) => [address, scores[getAddress(address)] || 0]) - ); -} diff --git a/src/strategies/cream/examples.json b/src/strategies/cream/examples.json deleted file mode 100644 index 56ead3dbd..000000000 --- a/src/strategies/cream/examples.json +++ /dev/null @@ -1,58 +0,0 @@ -[ - { - "name": "CREAM", - "strategy": { - "name": "cream", - "params": { - "token": "0x2ba592F78dB6436527729929AAf6c908497cB200", - "symbol": "CREAM", - "crCREAM": "0x892B14321a4FCba80669aE30Bd0cd99a7ECF6aC0", - "sushiswap": "0xf169CeA51EB51774cF107c88309717ddA20be167", - "uniswap": "0xddF9b7a31b32EBAF5c064C80900046C9e5b7C65F", - "balancer": "0x280267901C175565C64ACBD9A3c8F60705A72639", - "masterChef": "0xc2EdaD668740f1aA35E4D8f227fB8E17dcA888Cd", - "pid": 22, - "periods": 3, - "minVote": 1, - "pools": [ - { - "name": "CREAM", - "address": "0x2ba592F78dB6436527729929AAf6c908497cB200" - }, - { - "name": "1 Year", - "address": "0x780F75ad0B02afeb6039672E6a6CEDe7447a8b45" - }, - { - "name": "2 Year", - "address": "0xBdc3372161dfd0361161e06083eE5D52a9cE7595" - }, - { - "name": "3 Year", - "address": "0xD5586C1804D2e1795f3FBbAfB1FBB9099ee20A6c" - }, - { - "name": "4 Year", - "address": "0xE618C25f580684770f2578FAca31fb7aCB2F5945" - } - ] - } - }, - "network": "1", - "addresses": [ - "0x7dd508a1e4Da1243789B799a480f8B45e58b1B5b", - "0xB157ba30e3467DdBC844f14F02b4ba741f1d549F", - "0x6595732468A241312bc307F327bA0D64F02b3c20", - "0xdd4C3B2860f7C747C4F69414022D5FA7354Eef28", - "0xC51FA42503942cafa7b1ffc02F0Cd9564189773e", - "0x0430605323465E26Dc21fBAaA9A1A4Be6ae9d496", - "0xAdC24d7b630759A3AF7f52716d91299c999a2213", - "0x5E0b772FC4E58C470CE4551EeF2391A3B5dA5bb4", - "0x90aBCf1598ed3077861bCFb3B11EFcd1D7277223", - "0xF800d8407b1488BB6Dc3789c2D45147c25C38AF5", - "0xB662ACEAF435C5F21568FC138Ab202C6a43FFc13", - "0x99eb33756a2eAa32f5964A747722c4b59e6aF351" - ], - "snapshot": 12315029 - } -] diff --git a/src/strategies/cream/index.ts b/src/strategies/cream/index.ts deleted file mode 100644 index 16e016371..000000000 --- a/src/strategies/cream/index.ts +++ /dev/null @@ -1,301 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { formatUnits, parseUnits } from '@ethersproject/units'; -import { strategy as erc20BalanceOf } from '../erc20-balance-of'; -import { getBlockNumber } from '../../utils'; -import { Multicaller } from '../../utils'; - -export const author = 'jeremyHD'; -export const version = '0.2.1'; - -const ONE_E18 = parseUnits('1', 18); - -const abi = [ - { - constant: true, - inputs: [ - { - internalType: 'address', - name: '', - type: 'address' - } - ], - name: 'balanceOf', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function' - }, - { - inputs: [], - name: 'totalSupply', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256' - } - ], - stateMutability: 'view', - type: 'function' - }, - { - inputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256' - }, - { - internalType: 'address', - name: '', - type: 'address' - } - ], - name: 'userInfo', - outputs: [ - { - internalType: 'uint256', - name: 'amount', - type: 'uint256' - }, - { - internalType: 'uint256', - name: 'rewardDebt', - type: 'uint256' - } - ], - stateMutability: 'view', - type: 'function' - }, - { - constant: true, - inputs: [], - name: 'exchangeRateStored', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256' - } - ], - payable: false, - stateMutability: 'view', - type: 'function' - }, - { - constant: true, - inputs: [ - { - internalType: 'address', - name: 'account', - type: 'address' - } - ], - name: 'borrowBalanceStored', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256' - } - ], - payable: false, - stateMutability: 'view', - type: 'function' - } -]; - -const CREAM_VOTING_POWER = '0xb146BF59f30a54750209EF529a766D952720D0f9'; -const CREAM_VOTING_POWER_DEPLOY_BLOCK = 12315028; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const snapshotBlock = - typeof snapshot === 'number' ? snapshot : await getBlockNumber(provider); - const snapshotBlocks: number[] = []; - - for (let i = 0; i < options.periods; i++) { - const blocksPerPeriod = 80640; // 2 weeks per period, assume 15s per block - const blockTag = - snapshotBlock > blocksPerPeriod * i - ? snapshotBlock - blocksPerPeriod * i - : snapshotBlock; - snapshotBlocks.push(blockTag); - } - - const scores = await Promise.all([ - ...snapshotBlocks.map((blockTag) => - blockTag > CREAM_VOTING_POWER_DEPLOY_BLOCK - ? getScores(provider, addresses, options, blockTag) - : getLegacyScores(provider, addresses, options, blockTag) - ) - ]); - - const averageScore = {}; - addresses.forEach((address) => { - const userScore = scores - .map((score) => score[address]) - .reduce((accumulator, score) => (accumulator += score), 0); - averageScore[address] = userScore / options.periods; - }); - - return Object.fromEntries( - Array(addresses.length) - .fill('') - .map((_, i) => { - const score = averageScore[addresses[i]]; - // ignore score < minimum voting amount - if (score < options.minVote) { - return [addresses[i], 0]; - } - return [addresses[i], score]; - }) - ); -} - -async function getScores(provider, addresses, options, blockTag) { - return erc20BalanceOf( - 'cream', - '1', - provider, - addresses, - { - address: CREAM_VOTING_POWER, - decimals: 18 - }, - blockTag - ); -} - -async function getLegacyScores(provider, addresses, options, blockTag) { - const score = {}; - // Ethereum only - const multi1 = new Multicaller('1', provider, abi, { blockTag }); - multi1.call('sushiswap.cream', options.token, 'balanceOf', [ - options.sushiswap - ]); - multi1.call('sushiswap.totalSupply', options.sushiswap, 'totalSupply'); - - addresses.forEach((address) => { - multi1.call( - `sushiswap.${address}.balanceOf`, - options.sushiswap, - 'balanceOf', - [address] - ); - multi1.call( - `sushiswap.${address}.userInfo`, - options.masterChef, - 'userInfo', - [options.pid, address] - ); - }); - - const multi2 = new Multicaller('1', provider, abi, { blockTag }); - multi2.call('uniswap.cream', options.token, 'balanceOf', [options.uniswap]); - multi2.call('uniswap.totalSupply', options.uniswap, 'totalSupply'); - multi2.call('balancer.cream', options.token, 'balanceOf', [options.balancer]); - multi2.call('balancer.totalSupply', options.balancer, 'totalSupply'); - addresses.forEach((address) => { - multi2.call(`uniswap.${address}.balanceOf`, options.uniswap, 'balanceOf', [ - address - ]); - multi2.call( - `balancer.${address}.balanceOf`, - options.balancer, - 'balanceOf', - [address] - ); - }); - - const multi3 = new Multicaller('1', provider, abi, { blockTag }); - multi3.call('crCREAM.exchangeRate', options.crCREAM, 'exchangeRateStored'); - addresses.forEach((address) => { - multi3.call(`crCREAM.${address}.balanceOf`, options.crCREAM, 'balanceOf', [ - address - ]); - multi3.call( - `crCREAM.${address}.borrow`, - options.crCREAM, - 'borrowBalanceStored', - [address] - ); - }); - - const multi4 = new Multicaller('1', provider, abi, { blockTag }); - addresses.forEach((address) => { - options.pools.forEach((pool) => { - multi4.call(`pool.${address}.${pool.name}`, pool.address, 'balanceOf', [ - address - ]); - }); - }); - - const results = await Promise.all([ - multi1.execute(), - multi2.execute(), - multi3.execute(), - multi4.execute() - ]); - - const result = results.reduce((sumResult, partialResult) => { - Object.entries(partialResult).forEach(([key, value]) => { - sumResult[key] = value; - }); - return sumResult; - }, {}); - - const creamPerSushiswapLP = parseUnits( - result.sushiswap.cream.toString(), - 18 - ).div(result.sushiswap.totalSupply); - const creamPerUniswapLP = parseUnits(result.uniswap.cream.toString(), 18).div( - result.uniswap.totalSupply - ); - const creamPerBalancerLP = parseUnits( - result.balancer.cream.toString(), - 18 - ).div(result.balancer.totalSupply); - - addresses.forEach((address) => { - const userScore = score[address] || BigNumber.from(0); - const sushi = result.sushiswap[address].balanceOf - .add(result.sushiswap[address].userInfo.amount) - .mul(creamPerSushiswapLP) - .div(ONE_E18); - const uniswap = result.uniswap[address].balanceOf - .mul(creamPerUniswapLP) - .div(ONE_E18); - const balancer = result.balancer[address].balanceOf - .mul(creamPerBalancerLP) - .div(ONE_E18); - const crCREAM = result.crCREAM[address].balanceOf - .mul(result.crCREAM.exchangeRate) - .div(ONE_E18) - .sub(result.crCREAM[address].borrow); - const pools = Object.values(result.pool[address]).reduce( - (accumulator: any, poolBalance: any) => { - return accumulator.add(poolBalance); - }, - BigNumber.from(0) - ); - - score[address] = userScore - .add(sushi) - .add(uniswap) - .add(balancer) - .add(crCREAM) - .add(pools); - }); - - Object.keys(score).map((address) => { - score[address] = parseFloat(formatUnits(score[address], 18)); - }); - return score; -} diff --git a/src/strategies/cronaswap/README.md b/src/strategies/cronaswap/README.md deleted file mode 100644 index 00894b4fa..000000000 --- a/src/strategies/cronaswap/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# CronaSwap - -This is the most common strategy, it returns the balances of the voters for a balances CRONA token -in CronaSwap project(pools, farms, Liquidity, token). - -Here is an example of parameters: -```json -[ - { - "name": "Example query CronaSwap", - "strategy": { - "name": "cronaswap", - "params": { - "address": "0xadbd1231fb360047525BEdF962581F3eee7b49fe", - "masterChef": "0x77ea4a4cF9F77A034E4291E8f457Af7772c2B254", - "autoCrona": "0xDf3EBc46F283eF9bdD149Bb24c9b201a70d59389", - "cronaLPs": [ - { - "address": "0xeD75347fFBe08d5cce4858C70Df4dB4Bbe8532a0", - "pid": 1 - }, - { - "address": "0x482E0eEb877091cfca439D131321bDE23ddf9bB5", - "pid": 13 - }, - { - "address": "0x0427F9C304b0028f67A5fD61ffdD613186c1894B", - "pid": 14 - } - ], - "symbol": "CRONA", - "decimals": 18 - } - }, - "network": "25", - "addresses": [ - "0xd758B37Aff75F8Ee847D606fcEfE7BD18C8ed029", - "0xB6E6d031db616cF8aC338293dC2ecFa0F01C55EC" - ], - "snapshot": 599576 - } -] - - -``` diff --git a/src/strategies/cronaswap/examples.json b/src/strategies/cronaswap/examples.json deleted file mode 100644 index 90a6c5ab0..000000000 --- a/src/strategies/cronaswap/examples.json +++ /dev/null @@ -1,35 +0,0 @@ -[ - { - "name": "Example query CronaSwap", - "strategy": { - "name": "cronaswap", - "params": { - "address": "0xadbd1231fb360047525BEdF962581F3eee7b49fe", - "masterChef": "0x77ea4a4cF9F77A034E4291E8f457Af7772c2B254", - "autoCrona": "0xDf3EBc46F283eF9bdD149Bb24c9b201a70d59389", - "cronaLPs": [ - { - "address": "0xeD75347fFBe08d5cce4858C70Df4dB4Bbe8532a0", - "pid": 1 - }, - { - "address": "0x482E0eEb877091cfca439D131321bDE23ddf9bB5", - "pid": 13 - }, - { - "address": "0x0427F9C304b0028f67A5fD61ffdD613186c1894B", - "pid": 14 - } - ], - "symbol": "CRONA", - "decimals": 18 - } - }, - "network": "25", - "addresses": [ - "0xd758B37Aff75F8Ee847D606fcEfE7BD18C8ed029", - "0xB6E6d031db616cF8aC338293dC2ecFa0F01C55EC" - ], - "snapshot": 599576 - } -] diff --git a/src/strategies/cronaswap/index.ts b/src/strategies/cronaswap/index.ts deleted file mode 100644 index 85ce55180..000000000 --- a/src/strategies/cronaswap/index.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits, parseUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; -import examplesFile from './examples.json'; - -export const author = 'CronaChef'; -export const version = '0.0.1'; -export const examples = examplesFile; - -const abi = [ - 'function totalSupply() view returns (uint256)', - 'function balanceOf(address _owner) view returns (uint256 balance)', - 'function userInfo(uint256, address) view returns (uint256 amount, uint256 rewardDebt)' -]; - -const autoCronaSwapAbi = [ - 'function userInfo(address) view returns (uint256 amount, uint256 rewardDebt)', - 'function getPricePerFullShare() view returns (uint256)' -]; - -const bn = (num: any): BigNumber => { - return BigNumber.from(num.toString()); -}; - -const addUserBalance = (userBalances, user: string, balance) => { - if (userBalances[user]) { - return (userBalances[user] = userBalances[user].add(balance)); - } else { - return (userBalances[user] = balance); - } -}; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multicall = new Multicaller(network, provider, abi, { blockTag }); - addresses.forEach((address: any) => { - multicall.call(`token.${address}`, options.address, 'balanceOf', [address]); - multicall.call(`masterChef.${address}`, options.masterChef, 'userInfo', [ - '0', - address - ]); - }); - options.cronaLPs.forEach((lp: { address: string; pid: number }) => { - multicall.call(`lp.${lp.pid}.totalSupply`, lp.address, 'totalSupply'); - multicall.call(`lp.${lp.pid}.balanceOf`, options.address, 'balanceOf', [ - lp.address - ]); - addresses.forEach((address: any) => { - multicall.call( - `lpUsers.${address}.${lp.pid}`, - options.masterChef, - 'userInfo', - [lp.pid, address] - ); - }); - }); - - const multicallAutoCompound = new Multicaller( - network, - provider, - autoCronaSwapAbi, - { - blockTag - } - ); - multicallAutoCompound.call( - 'priceShare', - options.autoCrona, - 'getPricePerFullShare' - ); - addresses.forEach((address) => { - multicallAutoCompound.call(address, options.autoCrona, 'userInfo', [ - address - ]); - }); - - const resultAutoCrona = await multicallAutoCompound.execute(); - const result = await multicall.execute(); - - const userBalances: any = []; - for (let i = 0; i < addresses.length - 1; i++) { - userBalances[addresses[i]] = bn(0); - } - - addresses.forEach((address: any) => { - addUserBalance(userBalances, address, result.token[address]); - addUserBalance(userBalances, address, result.masterChef[address][0]); - addUserBalance( - userBalances, - address, - resultAutoCrona[address][0] - .mul(resultAutoCrona.priceShare) - .div(bn(parseUnits('1', options.decimals))) - ); - options.cronaLPs.forEach((lp: { address: string; pid: number }) => { - addUserBalance( - userBalances, - address, - result.lpUsers[address][lp.pid][0] - .mul(result.lp[lp.pid].balanceOf) - .div(result.lp[lp.pid].totalSupply) - ); - }); - }); - - return Object.fromEntries( - Object.entries(userBalances).map(([address, balance]) => [ - address, - parseFloat(formatUnits(balance, options.decimals)) - ]) - ); -} diff --git a/src/strategies/ctsi-staking/README.md b/src/strategies/ctsi-staking/README.md deleted file mode 100644 index 4281a8eaf..000000000 --- a/src/strategies/ctsi-staking/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# ctsi-staking - -This strategy implements the rules of CTSI Staking, returning the staked balance of the voters. It supports direct staking, as well as delegated staking through staking pools. - -There are no parameters to configure. - -The diagram below represents Cartesi Staking system: - -```mermaid -graph TD - U1[User 1] -->|stake| Staking{Staking} - U2[User 2] -->|operate| P1 - P1(Pool 1) -->|stake| Staking - U3[User 3] -->|stake| P1 -``` - -Cartesi staking system is primarily consolidated in the [StakingImpl contract](https://etherscan.io/address/0x9EdEAdFDE65BCfD0907db3AcdB3445229c764A69#readContract). Stakers to this contract can be EOA who stake directly, as the `User 1` above, or can be a [Staking Pool](https://github.com/cartesi/staking-pool), as the `Pool 1` above. - -Staking Pools are smart contracts, so in this case the voting power is delegated to its operator, given by the `owner` of the pool smart contract, represented by `User 2` above. - -Those users who do not wish to stake directly or operate a pool can stake to a pool instead, like the `User 3` represented in the diagram. As described above, pool operators accumulates the voting power of all its stakers. - -Note that an EOA can be at the same time a direct staker and a pool operator. Voting powers are accumulated. diff --git a/src/strategies/ctsi-staking/examples.json b/src/strategies/ctsi-staking/examples.json deleted file mode 100644 index 389209388..000000000 --- a/src/strategies/ctsi-staking/examples.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "ctsi-staking", - "params": { - "expectedResults": { - "scores": { - "0xf977814e90da44bfa03b6295a0616a897441acec": 51704200, - "0xc71c504d2a2938a173660ae71b3e97b563196bea": 36765082, - "0x2942aa4356783892c624125acfbbb80d29629a9d": 0, - "0xb5ba4a130f9e30036d1c1db11a8913caf3acdeba": 34404167.25798815 - } - } - } - }, - "network": "1", - "addresses": [ - "0xf977814e90da44bfa03b6295a0616a897441acec", - "0xc71c504d2a2938a173660ae71b3e97b563196bea", - "0x2942aa4356783892c624125acfbbb80d29629a9d", - "0xb5ba4a130f9e30036d1c1db11a8913caf3acdeba" - ], - "snapshot": 15951825 - } -] diff --git a/src/strategies/ctsi-staking/index.ts b/src/strategies/ctsi-staking/index.ts deleted file mode 100644 index 802c55ee5..000000000 --- a/src/strategies/ctsi-staking/index.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { getAddress } from '@ethersproject/address'; -import { subgraphRequest } from '../../utils'; - -export const author = 'cartesi'; -export const version = '0.1.0'; - -const SUBGRAPH_URL_ROOT = 'https://api.thegraph.com/subgraphs/name/cartesi/pos'; - -const NETWORK_KEY = { - '1': '', - '5': '-goerli' -}; - -function buildSubgraphUrl(chainId) { - const networkString = NETWORK_KEY[chainId]; - return `${SUBGRAPH_URL_ROOT}${networkString}`; -} - -async function getStakingBalance( - url, - addresses, - options, - snapshot -): Promise> { - // query for direct stakers (no pools) - const query = { - users: { - __args: { - where: { - id_in: addresses, - pool: null - }, - first: 1000 - }, - id: true, - balance: true - } - }; - if (snapshot !== 'latest') { - // @ts-ignore - query.users.__args.block = { number: snapshot }; - } - const score: Record = {}; - const result = await subgraphRequest(url, query); - if (result && result.users) { - result.users.forEach((user) => { - const address = getAddress(user.id); - const balance = BigNumber.from(user.balance); - score[address] = balance; - }); - } - - return score; -} - -async function getStakingPoolOperatorBalance( - url, - addresses, - options, - snapshot -): Promise> { - // query for StakingPools by manager (pool operator) - const query = { - stakingPools: { - __args: { - where: { - manager_in: addresses - }, - first: 1000 - }, - manager: true, - user: { - balance: true - } - } - }; - if (snapshot !== 'latest') { - // @ts-ignore - query.stakingPools.__args.block = { number: snapshot }; - } - const score: Record = {}; - const result = await subgraphRequest(url, query); - if (result && result.stakingPools) { - result.stakingPools.forEach((pool) => { - const address = getAddress(pool.manager); - const balance = BigNumber.from(pool.user.balance); - // a pool operator can operate more than one pool, so we must add the value if there is already one - score[address] = score[address] ? score[address].add(balance) : balance; - }); - } - - return score; -} - -function combineBalanceScores( - records: Record[] -): Record { - return records.reduce((aggScore, currScore) => { - for (const [address, balance] of Object.entries(currScore)) { - if (!aggScore[address]) { - aggScore[address] = balance; - } else { - aggScore[address] = aggScore[address].add(balance); // sum(L1, L2) - } - } - return aggScore; - }, {}); -} - -function verifyResults( - results: Record, - expectedResults: Record -): void { - Object.entries(results).forEach(([address, score]) => { - const expectedScore = - expectedResults[address.toLowerCase()] ?? - expectedResults[getAddress(address)]; - if (score !== expectedScore) { - console.error( - `>>> ERROR: Score do not match for address ${address}, expected ${expectedScore}, got ${score}` - ); - } - }); -} - -export async function strategy( - _space, - network, - _provider, - addresses, - options, - snapshot -): Promise> { - // convert addresses to lowercase, as in subgraph they are all lowercase - addresses = addresses.map((address) => address.toLowerCase()); - - // build subgraph URL based on network, as we have one for mainnet and another for goerli - const url = buildSubgraphUrl(network); - - // get staking balance for all direct stakers as voters (no pools, no delegators) - const directStaking = await getStakingBalance( - url, - addresses, - options, - snapshot - ); - - // get balance of pools operated by voters - const operators = await getStakingPoolOperatorBalance( - url, - addresses, - options, - snapshot - ); - - const results = combineBalanceScores([directStaking, operators]); - const scores = Object.fromEntries( - Object.entries(results).map(([address, balance]) => [ - address, - parseFloat(formatUnits(balance, 18)) - ]) - ); - - if (options.expectedResults && snapshot !== 'latest') { - // validate testing expected results - verifyResults(scores, options.expectedResults.scores); - } - - return scores; -} diff --git a/src/strategies/ctsi-staking/schema.json b/src/strategies/ctsi-staking/schema.json deleted file mode 100644 index 006ba35fb..000000000 --- a/src/strategies/ctsi-staking/schema.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "Strategy", - "type": "object", - "properties": { - "expectedResults": { - "type": "object", - "properties": { - "scores": { - "type": "object", - "additionalProperties": { "type": "number" } - } - } - } - }, - "required": [], - "additionalProperties": false - } - } -} diff --git a/src/strategies/cyberkongz-v3/examples.json b/src/strategies/cyberkongz-v3/examples.json deleted file mode 100644 index 608d40280..000000000 --- a/src/strategies/cyberkongz-v3/examples.json +++ /dev/null @@ -1,58 +0,0 @@ -[ - { - "name": "Multichain CyberKongz, CyberKongz VX and Banana holdings", - "strategy": { - "name": "cyberkongz-v3", - "params": { - "symbol": "KONGZ", - "chains": [ - { - "network": "1", - "registries": { - "0x57a204aa1042f6e66dd7730813f4024114d74f37": "OG", - "0x7ea3cca10668b8346aec0bf1844a49e995527c8b": "VX", - "0xe2311ae37502105b442bbef831e9b53c5d2e9b3b": "BANANA" - } - }, - { - "network": "137", - "registries": { - "0xbc91347e80886453f3f8bbd6d7ac07c122d87735": "BANANA", - "0x05df72d911e52ab122f7d9955728bc96a718782c": "VX" - } - } - ], - "skipList": [ - "0xb14b87790643d2dab44b06692d37dd95b4b30e56", - "0x9d59eba4deaee09466ba9d4073bf912bc72982b0", - "0x0f4676178b5c53ae0a655f1b19a96387e4b8b5f2", - "0xcfa9a297a406a48d1137172c18de04c944b47ba9", - "0x820f92c1b3ad8e962e6c6d9d7caf2a550aec46fb", - "0x9ffad2ff3a59d8579e3b0edc6c8f2f591c94dfab", - "0xe058d87fc1185e38ab68893136834715b30961e1", - "0xe2311ae37502105b442bbef831e9b53c5d2e9b3b", - "0x7a08865A3E7c291f3b794210Bc51D559B49DFd15", - "0xe6f45376f64e1f568bd1404c155e5ffd2f80f7ad", - "0x40ec5b33f54e0e8a33a975908c5ba1c14e5bbbdf", - "0x0000000000000000000000000000000000000000", - "0xD6a92755Ac5384867083Abd79aD007DE389b955e", - "0x000000000000000000000000000000000000dead", - "0x70C575588B98C1F46B1382c706AdAf398A874e3E", - "0xab8eee3493a55a7bd8126865fd662b7097928088" - ] - } - }, - "network": "1", - "addresses": [ - "0xf521Bb7437bEc77b0B15286dC3f49A87b9946773", - "0x721931508df2764fd4f70c53da646cb8aed16ace", - "0xa63571f2ce7cf4e9a566a1f248f5d0ad3ba78726", - "0x9279c4cfb0e85e2dff8825ce141f9794c7c7170a", - "0x6f35b0cfc58eb1e21eef8a439bbb0ce4c929d32a", - "0xe34bded2b256430a9be53cbf5cba3b6d866d55f3", - "0xb14b87790643d2dab44b06692d37dd95b4b30e56", - "0xd32f25Dfa932b8064A81B8254E7997CAeBc85F97" - ], - "snapshot": 15021651 - } -] diff --git a/src/strategies/cyberkongz-v3/index.ts b/src/strategies/cyberkongz-v3/index.ts deleted file mode 100644 index 5788b4381..000000000 --- a/src/strategies/cyberkongz-v3/index.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { getProvider, getSnapshots, multicall } from '../../utils'; - -export const author = 'maxbrand99'; -export const version = '1.0.0'; - -const bananaContract = '0xe2311ae37502105b442bbef831e9b53c5d2e9b3b'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function totalSupply() external view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const allAddresses = {}; - const promises: any = []; - const blocks = await getSnapshots( - network, - snapshot, - provider, - options.chains.map((s) => s.network || network) - ); - const allCalls: any[] = []; - const chainCalls = { 1: [], 137: [] }; - options.chains.forEach((chain) => { - if (chain.network == 1 || chain.network == 137) { - Object.keys(chain.registries).forEach((registry) => { - allAddresses[registry] = chain.registries[registry]; - addresses.forEach((address: any) => { - chainCalls[chain.network].push([registry, 'balanceOf', [address]]); - allCalls.push([registry, 'balanceOf', [address]]); - }); - }); - } - }); - - Object.keys(chainCalls).forEach((chainID) => { - const blockTag = - typeof blocks[chainID] === 'number' ? blocks[chainID] : 'latest'; - promises.push( - multicall(chainID, getProvider(chainID), abi, chainCalls[chainID], { - blockTag - }) - ); - }); - - const results = await Promise.all(promises); - - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const nanaCall = [[bananaContract, 'totalSupply', []]]; - let nanaSupply = await multicall(network, provider, abi, nanaCall, { - blockTag - }); - - const response: any[] = []; - results.forEach((result) => { - result.forEach((value) => { - response.push(value); - }); - }); - - response.forEach((value: any, i: number) => { - const address = allCalls[i][2][0]; - if ( - allAddresses[allCalls[i][0]] == 'BANANA' && - options.skipList.find((add) => add === address) - ) { - nanaSupply -= value; - } - }); - - const merged = {}; - response.forEach((value: any, i: number) => { - const address = allCalls[i][2][0]; - if (options.skipList.find((add) => add === address)) { - return; - } - merged[address] = (merged[address] || 0) as number; - if (allAddresses[allCalls[i][0]] == 'OG') - merged[address] += parseFloat(formatUnits((3 * value).toString(), 0)); - else if (allAddresses[allCalls[i][0]] == 'VX') - merged[address] += parseFloat(formatUnits(value.toString(), 0)); - else if (allAddresses[allCalls[i][0]] == 'BANANA') - merged[address] += parseFloat( - formatUnits(Math.floor((15000 * value) / nanaSupply).toString(), 0) - ); - }); - - return merged; -} diff --git a/src/strategies/cyberkongz/examples.json b/src/strategies/cyberkongz/examples.json deleted file mode 100644 index 9a51c16b1..000000000 --- a/src/strategies/cyberkongz/examples.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "name": "CyberKongz, CyberKongz VX and Banana holdings", - "strategy": { - "name": "cyberkongz", - "params": { - "symbol": "KONGZ", - "registries": [ - "0x57a204aa1042f6e66dd7730813f4024114d74f37", - "0x7ea3cca10668b8346aec0bf1844a49e995527c8b", - "0xe2311ae37502105b442bbef831e9b53c5d2e9b3b" - ] - } - }, - "network": "1", - "addresses": [ - "0xf521Bb7437bEc77b0B15286dC3f49A87b9946773", - "0x721931508df2764fd4f70c53da646cb8aed16ace", - "0xa63571f2ce7cf4e9a566a1f248f5d0ad3ba78726", - "0x9279c4cfb0e85e2dff8825ce141f9794c7c7170a", - "0x6f35b0cfc58eb1e21eef8a439bbb0ce4c929d32a", - "0xe34bded2b256430a9be53cbf5cba3b6d866d55f3", - "0x031c690be2932403cbdd85f8853f596794cff6c3" - ], - "snapshot": 13322697 - } -] diff --git a/src/strategies/cyberkongz/index.ts b/src/strategies/cyberkongz/index.ts deleted file mode 100644 index a47a1348f..000000000 --- a/src/strategies/cyberkongz/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; - -export const author = 'cesarsld'; -export const version = '0.1.0'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function totalSupply() external view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const calls: any[] = []; - options.registries.forEach((registry) => { - addresses.forEach((address: any) => { - calls.push([registry, 'balanceOf', [address]]); - }); - }); - - const response = await multicall(network, provider, abi, calls, { blockTag }); - const nanaCall = [ - ['0xe2311ae37502105b442bbef831e9b53c5d2e9b3b', 'totalSupply', []] - ]; - const nanaSupply = await multicall(network, provider, abi, nanaCall, { - blockTag - }); - - const merged = {}; - response.map((value: any, i: number) => { - const address = calls[i][2][0]; - merged[address] = (merged[address] || 0) as number; - if (Math.floor(i / addresses.length) == 0) - merged[address] += parseFloat(formatUnits((3 * value).toString(), 0)); - else if (Math.floor(i / addresses.length) == 1) - merged[address] += parseFloat(formatUnits(value.toString(), 0)); - else if (Math.floor(i / addresses.length) == 2) - merged[address] += parseFloat( - formatUnits(Math.floor((15000 * value) / nanaSupply).toString(), 0) - ); - }); - - return merged; -} diff --git a/src/strategies/delegate-registry-v2/examples.json b/src/strategies/delegate-registry-v2/examples.json index 930f4c022..c4f6a3a28 100644 --- a/src/strategies/delegate-registry-v2/examples.json +++ b/src/strategies/delegate-registry-v2/examples.json @@ -5,21 +5,24 @@ "name": "delegate-registry-v2", "params": { "backendUrl": "https://delegate-registry-backend.vercel.app", + "delegationV1VChainIds": [1, 100], "strategies": [ { "name": "erc20-balance-of", "params": { - "address": "0x6b175474e89094c44da98b954eedeac495271d0f", - "symbol": "DAI", + "symbol": "ST", + "address": "0xE666Ad68a6e2897CD06A9ff378ED8b0d71093398", + "network": "5", "decimals": 18 - } + }, + "network": "5" }, { - "name": "gno", + "name": "erc20-balance-of", "params": { - "symbol": "GNO", - "decimals": 18, - "SUBGRAPH_URL": "https://api.thegraph.com/subgraphs/id/QmduKVUHCPjR5tmNEgooXHBMGKqDJWrUPdp6dEMeJM6Kqa" + "address": "0x6b175474e89094c44da98b954eedeac495271d0f", + "symbol": "DAI", + "decimals": 18 } } ] @@ -27,6 +30,11 @@ }, "network": "1", "addresses": [ + "0x2011c83e8f75c0ceb90ec140d8e8adfc836e3685", + "0xde1e6a7ed0ad3f61d531a8a78e83ccddbd6e0c49", + "0x007de57773b6eb4ebbf6a740dfde1efdd5629630", + "0x6cc5b30cd0a93c1f85c7868f5f2620ab8c458190", + "0xd028d504316fec029cfa36bdc3a8f053f6e5a6e4", "0x000e37ed92d86a7667f520c53b73b01ff5c206eb", "0x000dbf2733da51135c1b21c8ef71a3d474383f0d", "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", @@ -40,6 +48,6 @@ "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad" ], - "snapshot": 17463998 + "snapshot": 18478898 } ] diff --git a/src/strategies/delegate-registry-v2/index.ts b/src/strategies/delegate-registry-v2/index.ts index 6304fee96..e22fb4805 100644 --- a/src/strategies/delegate-registry-v2/index.ts +++ b/src/strategies/delegate-registry-v2/index.ts @@ -5,13 +5,14 @@ import { getScoresDirect } from '../../utils'; import { getAddress } from '@ethersproject/address'; export const author = 'gnosis'; -export const version = '0.0.1'; +export const version = '0.0.2'; const DEFAULT_BACKEND_URL = 'https://delegate-registry-backend.vercel.app'; type Params = { backendUrl: string; strategies: Strategy[]; + delegationV1VChainIds?: number[]; // add this to include v1 delegations }; /* @@ -42,8 +43,11 @@ export async function strategy( 'Content-Type': 'application/json' }, body: JSON.stringify({ - addresses: addresses, - strategies: options.strategies + spaceParams: { + ...options, + mainChainId: Number(network) + }, + addresses }) } ); @@ -75,7 +79,7 @@ export async function strategy( ...addressesNotDelegatingOrDelegatedTo, ...addressesDelegatedTo.map(([address]) => address) ], - snapshot + blockTag ); const delegationObject = addressesDelegatedTo.reduce( diff --git a/src/strategies/delegation-with-cap/README.md b/src/strategies/delegation-with-cap/README.md new file mode 100644 index 000000000..354213c84 --- /dev/null +++ b/src/strategies/delegation-with-cap/README.md @@ -0,0 +1,34 @@ +# delegation-with-cap + +This strategy is based on the [delegation](https://github.com/snapshot-labs/snapshot-strategies/tree/master/src/strategies/delegation) strategy, with an additional `capPercentage` parameter that caps the voting power of any address to a percentage of the total supply, along with `address` and `decimals` parameters to get the total supply that the voting power should be capped against. + +| Param Name | Description | +|----------------------------|-----------------------------------------------------------------------------------------| +| strategies | list of sub strategies to calculate voting power based on delegation | +| capPercentage | Maximum voting power for any address as a percentage of total votes | +| address | Address of the token contract used to cap total supply against | +| decimals | Decimals of the token contract used to cap total supply against | +| delegationSpace (optional) | Get delegations of a particular space (by default it take delegations of current space) | + +Here is an example of parameters: + +```json +{ + "symbol": "veBAL (delegated)", + "address": "0xC128a9954e6c874eA3d62ce62B468bA073093F25", + "decimals": 18, + "strategies": [ + { + "name": "erc20-balance-of", + "params": { + "symbol": "veBAL", + "address": "0xC128a9954e6c874eA3d62ce62B468bA073093F25", + "decimals": 18 + } + } + ], + "delegationSpace": "balancer.eth", + "capPercentage": 30 +} + +``` diff --git a/src/strategies/delegation-with-cap/examples.json b/src/strategies/delegation-with-cap/examples.json new file mode 100644 index 000000000..8f554a491 --- /dev/null +++ b/src/strategies/delegation-with-cap/examples.json @@ -0,0 +1,32 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "delegation-with-cap", + "params": { + "symbol": "veBAL (delegated)", + "address": "0xC128a9954e6c874eA3d62ce62B468bA073093F25", + "decimals": 18, + "strategies": [ + { + "name": "erc20-balance-of", + "params": { + "symbol": "veBAL", + "address": "0xC128a9954e6c874eA3d62ce62B468bA073093F25", + "decimals": 18 + } + } + ], + "delegationSpace": "balancer.eth", + "capPercentage": 10 + } + }, + "network": "1", + "addresses": [ + "0xAD9992f3631028CEF19e6D6C31e822C5bc2442CC", + "0x512fce9B07Ce64590849115EE6B32fd40eC0f5F3", + "0x9f74662aD05840Ba35d111930501c617920dD68e" + ], + "snapshot": 17834154 + } +] diff --git a/src/strategies/delegation-with-cap/index.ts b/src/strategies/delegation-with-cap/index.ts new file mode 100644 index 000000000..daa31413c --- /dev/null +++ b/src/strategies/delegation-with-cap/index.ts @@ -0,0 +1,74 @@ +import { getDelegations } from '../../utils/delegation'; +import { call, getScoresDirect } from '../../utils'; + +export const author = '0xButterfield'; +export const version = '0.1.0'; +export const dependOnOtherAddress = true; + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +) { + const invalidStrategies = [ + '{"name":"erc20-balance-of","params":{"symbol":"HOP","address":"0xed8Bdb5895B8B7f9Fdb3C087628FD8410E853D48","decimals":18}}' //https://snapshot.org/#/hop.eth/proposal/0x603f0f6e54c7be8d5db7e16ae7145e6df4b439b8aac49654cdfd6b0c03eb6492 + ]; + + if ( + options.strategies.some((s) => + invalidStrategies.includes(JSON.stringify(s)) + ) + ) + return {}; + const delegationSpace = options.delegationSpace || space; + const delegations = await getDelegations( + delegationSpace, + network, + addresses, + snapshot + ); + if (Object.keys(delegations).length === 0) return {}; + + const scores = ( + await getScoresDirect( + space, + options.strategies, + network, + provider, + Object.values(delegations).reduce((a: string[], b: string[]) => + a.concat(b) + ), + snapshot + ) + ).filter((score) => Object.keys(score).length !== 0); + + const addressScores = Object.fromEntries( + addresses.map((address) => { + const addressScore = delegations[address] + ? delegations[address].reduce( + (a, b) => a + scores.reduce((x, y) => (y[b] ? x + y[b] : x), 0), + 0 + ) + : 0; + return [address, addressScore]; + }) + ); + + const totalSupply = await call( + provider, + ['function totalSupply() public view returns (uint256)'], + [options.address, 'totalSupply'] + ).then((res) => Number(res.toString()) / 10 ** options.decimals); + + const maxScore = (options.capPercentage * totalSupply) / 100; + + return Object.fromEntries( + Object.entries(addressScores).map(([address, addressScore]) => [ + address, + Math.min(addressScore, maxScore) + ]) + ); +} diff --git a/src/strategies/digitalax-deco-to-mona/README.md b/src/strategies/digitalax-deco-to-mona/README.md deleted file mode 100644 index 76c03f245..000000000 --- a/src/strategies/digitalax-deco-to-mona/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# DIGITALAX DECO to MONA conversion - -This is the DIGITALAX DECO to MONA conversion for voting purposes - -Here is an example of parameters: - -```json -{ - "symbol": "MONA", - "address": "0x200f9621cbce6ed740071ba34fde85ee03f2e113" -} -``` - - diff --git a/src/strategies/digitalax-deco-to-mona/examples.json b/src/strategies/digitalax-deco-to-mona/examples.json deleted file mode 100644 index 86fbc176d..000000000 --- a/src/strategies/digitalax-deco-to-mona/examples.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "name": "Digitalax Deco to Mona conversion", - "strategy": { - "name": "digitalax-deco-to-mona", - "params": { - "symbol": "MONA", - "address": "0x200f9621cbce6ed740071ba34fde85ee03f2e113" - } - }, - "network": "137", - "addresses": [ - "0xd4c4f5e108d09f4383f431d143e75ecabb703f2a", - "0x0e091edbc59d7089451b19bd54bddd3576232edc", - "0x5fc1f31a084ef781b436885d10ab8c3e24a29642", - "0x5e4038eb6f1e7316a45ed569e94c8cd1706a5ec5", - "0xab48edd90bdf367d326d827758bacd2460c59d17" - ], - "snapshot": 23986420 - } -] diff --git a/src/strategies/digitalax-deco-to-mona/index.ts b/src/strategies/digitalax-deco-to-mona/index.ts deleted file mode 100644 index 5558c0ad6..000000000 --- a/src/strategies/digitalax-deco-to-mona/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { multicall } from '../../utils'; -import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; - -export const author = 'onigiri-x'; -export const version = '0.1.0'; - -const abi = [ - 'function getReserves() external view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const pairAddresses = [ - '0x7ecb3be21714114d912469810aedd34e6fc27736', - '0x3203bf44d434452b4605c7657c51bfeaf2a0847c' - ]; - const priceResponse = await multicall( - network, - provider, - abi, - pairAddresses.map((address: any) => [address, 'getReserves', []]), - { blockTag } - ); - - const priceDecoToEth = - parseFloat(priceResponse[0]._reserve1) / - parseFloat(priceResponse[0]._reserve0); - const priceEthToMona = - parseFloat(priceResponse[1]._reserve0) / - parseFloat(priceResponse[1]._reserve1); - - const erc20Balances = await erc20BalanceOfStrategy( - space, - network, - provider, - addresses, - options, - snapshot - ); - - return Object.fromEntries( - addresses.map((address) => [ - address, - erc20Balances[address] * priceDecoToEth * priceEthToMona - ]) - ); -} diff --git a/src/strategies/digitalax-mona-quickswap/README.md b/src/strategies/digitalax-mona-quickswap/README.md index 52366e257..97208addd 100644 --- a/src/strategies/digitalax-mona-quickswap/README.md +++ b/src/strategies/digitalax-mona-quickswap/README.md @@ -10,12 +10,11 @@ Here is an example of parameters: "address": "0x82f1676ef298db09da935f4cb7bd3c44fb73d83a" } ``` -There are two pools that are currently supported. - -Mona Quick Pool: -``` "address": "0x82f1676ef298db09da935f4cb7bd3c44fb73d83a"``` -Mona USDT Pool: -``` "address": "0x856ad56defbb685db8392d9e54441df609bc5ce1"``` +There are two pools that are currently supported. +Mona Quick Pool: +` "address": "0x82f1676ef298db09da935f4cb7bd3c44fb73d83a"` +Mona USDT Pool: +` "address": "0x856ad56defbb685db8392d9e54441df609bc5ce1"` diff --git a/src/strategies/dopamine/README.md b/src/strategies/dopamine/README.md deleted file mode 100644 index 87262fd4c..000000000 --- a/src/strategies/dopamine/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# dopamine - -Combines veDope governance token holders and NFT holders, returning a combined voting power as a percentage of the total. - -Here is an example of parameters: - -```json -{ - "erc20Address": "0x6b175474e89094c44da98b954eedeac495271d0f", - "erc721Address": "0x96b0f2cf48ad9ab21bb7a8a052a3d8391ee64798", - "nftMultiplier": 10000, - "decimals": 4 -} -``` diff --git a/src/strategies/dopamine/examples.json b/src/strategies/dopamine/examples.json deleted file mode 100644 index 430389562..000000000 --- a/src/strategies/dopamine/examples.json +++ /dev/null @@ -1,36 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "dopamine", - "params": { - "tokenAddress": "0x6b175474e89094c44da98b954eedeac495271d0f", - "nftAddress": "0x96b0f2cf48ad9ab21bb7a8a052a3d8391ee64798", - "nftMultiplier": 10000, - "decimals": 8 - } - }, - "network": "1", - "addresses": [ - "0xd99c7975ef9b339d93fec21a4ab24a567d686d73", - "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e", - "0x1d5E65a087eBc3d03a294412E46CE5D6882969f4", - "0x1f254336E5c46639A851b9CfC165697150a6c327", - "0x2ec3F80BeDA63Ede96BA20375032CDD3aAfb3030", - "0x4AcBcA6BE2f8D2540bBF4CA77E45dA0A4a095Fa2", - "0x4F3D348a6D09837Ae7961B1E0cEe2cc118cec777", - "0x6D7f23A509E212Ba7773EC1b2505d1A134f54fbe", - "0x07a1f6fc89223c5ebD4e4ddaE89Ac97629856A0f", - "0x8d5F05270da470e015b67Ab5042BDbE2D2FEFB48", - "0x8d07D225a769b7Af3A923481E1FdF49180e6A265", - "0x8f60501dE5b9b01F9EAf1214dbE1924aA97F7fd0", - "0x9B8e8dD9151260c21CB6D7cc59067cd8DF306D58", - "0x17ea92D6FfbAA1c7F6B117c1E9D0c88ABdc8b84C", - "0x38C0039247A31F3939baE65e953612125cB88268" - ], - "snapshot": 15092760 - } -] diff --git a/src/strategies/dopamine/index.ts b/src/strategies/dopamine/index.ts deleted file mode 100644 index 9fe5ef5e6..000000000 --- a/src/strategies/dopamine/index.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller, call } from '../../utils'; - -export const author = 'crypto-dump'; -export const version = '0.1.0'; - -const nftContractAbi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function totalSupply() external view returns (uint256)' -]; - -const tokenContractAbi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function totalSupply() external view returns (uint256)', - 'function decimals() external view returns (uint256)' -]; - -interface StrategyOptions { - decimals: number; - tokenAddress: string; - nftAddress: string; - nftMultiplier: number; -} - -type MultiCallResult = Record; - -export async function strategy( - space, - network, - provider, - addresses, - options: StrategyOptions, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const callTokenDecimal = () => { - return call(provider, tokenContractAbi, [ - options.tokenAddress, - 'decimals', - [] - ]); - }; - - const makeMulticaller = (abi, contractAddress) => { - const multiCaller = new Multicaller(network, provider, abi, { - blockTag - }); - addresses.forEach((address) => - multiCaller.call(address, contractAddress, 'balanceOf', [address]) - ); - return multiCaller; - }; - - const erc20Multi = makeMulticaller(tokenContractAbi, options.tokenAddress); - const erc721Multi = makeMulticaller(nftContractAbi, options.nftAddress); - - const [tokenDecimal, tokenResults, nftResults]: [ - BigNumber, - MultiCallResult, - MultiCallResult - ] = await Promise.all([ - callTokenDecimal(), - erc20Multi.execute(), - erc721Multi.execute() - ]); - - const scores: Record = {}; - - for (const address of addresses) { - const tokenScore = BigNumber.from(tokenResults[address] || 0); - - const nftScore = BigNumber.from(nftResults[address] || 0) - .mul(options.nftMultiplier) - .mul(BigNumber.from(10).pow(tokenDecimal)); - scores[address] = tokenScore.add(nftScore); - } - - return Object.fromEntries( - Object.entries(scores).map(([address, score]) => [ - address, - parseFloat(formatUnits(score, options.decimals)) - ]) - ); -} diff --git a/src/strategies/dopamine/schema.json b/src/strategies/dopamine/schema.json deleted file mode 100644 index f09f221c1..000000000 --- a/src/strategies/dopamine/schema.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "Dopamine", - "type": "object", - "properties": { - "nftMultiplier": { - "type": "number", - "title": "Symbol", - "examples": ["e.g. 10000"] - }, - "tokenAddress": { - "type": "string", - "title": "ERC20 Token Contract address", - "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "nftAddress": { - "type": "string", - "title": "ERC721 NFT Token Contract address", - "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "decimals": { - "type": "number", - "title": "Decimals", - "examples": ["e.g. 4"] - } - }, - "required": ["tokenAddress", "nftAddress", "nftMultiplier", "decimals"], - "additionalProperties": false - } - } -} diff --git a/src/strategies/dsla-parametric-staking-service-credits/README.md b/src/strategies/dsla-parametric-staking-service-credits/README.md deleted file mode 100644 index ec8724192..000000000 --- a/src/strategies/dsla-parametric-staking-service-credits/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# dsla - -DSLA's custom snapshot strategy, calculates voting score based of the staked balances of the voters for users/provider pools of Staking SLA. - -Here is an example of parameters: - -```json -{ - "LP_TOKEN": "0xAC104C0438A7bb15C714503537c6FA271FDB284E", - "SP_TOKEN": "0xcf4ea46eba95fe3643b6c954d29516d7376913dc", - "DSLA": "0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe", - "StakingSLA": "0x091ee4d4D8FfD00698c003C21F1ba69EA6310241", - "decimals": 18 -} -``` diff --git a/src/strategies/dsla-parametric-staking-service-credits/examples.json b/src/strategies/dsla-parametric-staking-service-credits/examples.json deleted file mode 100644 index bdf267123..000000000 --- a/src/strategies/dsla-parametric-staking-service-credits/examples.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "name": "Example query of DSLA Staking", - "strategy": { - "name": "dsla-parametric-staking-service-credits", - "params": { - "xDSLA": "0xcf4ea46eba95fe3643b6c954d29516d7376913dc", - "xDSLA_LP": "0xAC104C0438A7bb15C714503537c6FA271FDB284E", - "DSLA": "0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe", - "StakingSLA": "0x091ee4d4D8FfD00698c003C21F1ba69EA6310241", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x8b0049beb72893c290a026398571a26ea4a03892", - "0x4d5c314adac838d1906992d83894fe47d6931165", - "0xe7a3b897fa52fe3e7f7d0e9803bea00fab78d12f", - "0xb6d568233e81442ded126fad8e8230ed02b6be3e", - "0x5d64d4c35345e9588f3f2233cf6e93978763012c", - "0x9e1b10bf75f339805ed3b266070001100bf87a85", - "0x57a43c9bc2b4d4447bfc1cf4e050bc6dcb72d753", - "0xb44b476a7f6a83b5e0559433b0d3ad374a0c1682", - "0x4baac6507bdcb0c9d059284c539bcf35d433d22e", - "0x66fc061aa4d8d3809876f5a6ddb3506d2d16168f", - "0x443a391e33241798d69295149cdf018e543049ea", - "0x645b28857b19c0046b10caa3bd93fcd31ce15905", - "0x20850bcea7476a0623e276c2cb9ec2e3024dcb0c", - "0xce049d2afc746f96d2ca2b6425efcbf9d44fda2c", - "0x397ef1462cf750ae2e4c387a70474c1f1c5a1637", - "0xb270814532f90969b62b37ccd04dfadfadcd3267", - "0x12b50bdab12a633d992ebfecff6eb9785aa5a09a", - "0x88c0356a46823938bcb233be05d7634aab7f40d9" - ], - "snapshot": 15825878 - } -] diff --git a/src/strategies/dsla-parametric-staking-service-credits/index.ts b/src/strategies/dsla-parametric-staking-service-credits/index.ts deleted file mode 100644 index 4e768ea62..000000000 --- a/src/strategies/dsla-parametric-staking-service-credits/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = 'gmtesla'; -export const version = '0.1.1'; - -// const DSLA = '0x3aFfCCa64c2A6f4e3B6Bd9c64CD2C969EFd1ECBe'; -// const StakingSLA = '0x091ee4d4D8FfD00698c003C21F1ba69EA6310241'; -// const LP_TOKEN = '0xAC104C0438A7bb15C714503537c6FA271FDB284E'; // dpToken -// const SP_TOKEN = '0xcf4ea46eba95fe3643b6c954d29516d7376913dc' // duToken - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function totalSupply() external view returns (uint256)', - 'function usersPool(address token) external view returns (uint256)', - 'function providersPool(address token) external view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - // Get dpToken Balances - const multi = new Multicaller(network, provider, abi, { blockTag }); - addresses.forEach((address) => - multi.call(address, options.xDSLA_LP, 'balanceOf', [address]) - ); - const dpTokenBalances: Record = await multi.execute(); - - // Get duToken Balances - addresses.forEach((address) => - multi.call(address, options.xDSLA, 'balanceOf', [address]) - ); - const duTokenBalances: Record = await multi.execute(); - - // Get totalSupply of user/provider pools - const multi2 = new Multicaller(network, provider, abi, { blockTag }); - multi2.call('userTotalSupply', options.xDSLA, 'totalSupply', []); - multi2.call('providerTotalSupply', options.xDSLA_LP, 'totalSupply', []); - multi2.call('usersPool', options.StakingSLA, 'usersPool', [options.DSLA]); - multi2.call('providersPool', options.StakingSLA, 'providersPool', [ - options.DSLA - ]); - const res2: Record = await multi2.execute(); - - // Sum up duTokenBalance and dpTokenBalances - // dTokenBalance = staked amount * total supply / (userPools or providerPools) - // staked amount = dTokenBalance * (userPools or providerPools) / total supply - const balances = Object.fromEntries( - Object.entries(dpTokenBalances).map(([address, balance]) => [ - address, - BigNumber.from(balance) - .mul(res2.providersPool) - .div(res2.providerTotalSupply) - ]) - ); - Object.entries(duTokenBalances).forEach(([address, balance]) => { - const prevBal = BigNumber.from(balances[address]); - balances[address] = prevBal.add( - BigNumber.from(balance).mul(res2.usersPool).div(res2.userTotalSupply) - ); - }); - - const result = Object.fromEntries( - Object.entries(balances).map(([address, balance]) => [ - address, - parseFloat(formatUnits(balance, options.decimals)) - ]) - ); - - return result; -} diff --git a/src/strategies/dsla-parametric-staking-service-credits/schema.json b/src/strategies/dsla-parametric-staking-service-credits/schema.json deleted file mode 100644 index 4b27e279c..000000000 --- a/src/strategies/dsla-parametric-staking-service-credits/schema.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "DSLA", - "type": "object", - "properties": { - "xDSLA": { - "type": "string", - "title": "xDSLA", - "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "xDSLA_LP": { - "type": "string", - "title": "xDSLA_LP", - "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "DSLA": { - "type": "string", - "title": "DSLA", - "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "StakingSLA": { - "type": "string", - "title": "StakingSLA", - "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "decimals": { - "type": "number", - "title": "Decimals", - "examples": ["e.g. 18"] - } - }, - "required": ["xDSLA", "xDSLA_LP", "StakingSLA", "DSLA", "decimals"], - "additionalProperties": false - } - } -} diff --git a/src/strategies/dss-vest-unpaid/index.ts b/src/strategies/dss-vest-unpaid/index.ts index 5347cd157..4076e3ef8 100644 --- a/src/strategies/dss-vest-unpaid/index.ts +++ b/src/strategies/dss-vest-unpaid/index.ts @@ -4,7 +4,7 @@ import { Contract } from '@ethersproject/contracts'; import { Multicaller } from '../../utils'; export const author = 'espendk'; -export const version = '1.0.1'; +export const version = '1.1.0'; // To avoid future memory issues, we limit the number of vestings supported by the strategy const MAX_VESTINGS = 500; @@ -12,21 +12,27 @@ const MAX_VESTINGS = 500; const abi = [ 'function ids() external view returns (uint256)', 'function usr(uint256 id) external view returns (address)', + 'function accrued(uint256 id) external view returns (uint256)', 'function unpaid(uint256 id) external view returns (uint256)' ]; -export async function strategy( - space, +export type Vesting = { + id: number; + usr: string; + accrued: number; + unpaid: number; +}; + +export async function getAllVestings( network, provider, - addresses, - options, - snapshot -): Promise> { + snapshot, + dssVestAddress: string, + decimals: number +): Promise { const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - // Get the number of vestings - const dssVestContract = new Contract(options.address, abi, provider); + const dssVestContract = new Contract(dssVestAddress, abi, provider); const idCount = await dssVestContract.ids({ blockTag }); if (idCount > MAX_VESTINGS) { throw new Error( @@ -34,14 +40,46 @@ export async function strategy( ); } - // Get the vesting addresses and unpaid amounts const multi = new Multicaller(network, provider, abi, { blockTag }); for (let id = 1; id <= idCount; ++id) { - multi.call('usr' + id, options.address, 'usr', [id]); - multi.call('unpaid' + id, options.address, 'unpaid', [id]); + multi.call('usr' + id, dssVestAddress, 'usr', [id]); + multi.call('accrued' + id, dssVestAddress, 'accrued', [id]); + multi.call('unpaid' + id, dssVestAddress, 'unpaid', [id]); + } + const vestings: Record = await multi.execute(); + + const result: Vesting[] = []; + + for (let id = 1; id <= idCount; ++id) { + const usr = vestings['usr' + id] as string; + const accrued = parseFloat(formatUnits(vestings['accrued' + id], decimals)); + const unpaid = parseFloat(formatUnits(vestings['unpaid' + id], decimals)); + result.push({ + id, + usr, + accrued, + unpaid + }); } - const unclaimedVestings: Record = - await multi.execute(); + + return result; +} + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +): Promise> { + const vestings = await getAllVestings( + network, + provider, + snapshot, + options.address, + options.decimals + ); // Set score to 0 for all addresses const result = {}; @@ -50,12 +88,10 @@ export async function strategy( }); // Add the unclaimed vesting amounts to the addresses - for (let id = 1; id <= idCount; ++id) { - const address = unclaimedVestings['usr' + id] as string; + for (const vesting of vestings) { + const address = vesting.usr; if (addresses.includes(address)) { - result[address] += parseFloat( - formatUnits(unclaimedVestings['unpaid' + id], options.decimals) - ); + result[address] += vesting.unpaid; } } diff --git a/src/strategies/echelon-cached-erc1155-decay/README.md b/src/strategies/echelon-cached-erc1155-decay/README.md deleted file mode 100644 index 6d80e7d98..000000000 --- a/src/strategies/echelon-cached-erc1155-decay/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# echelon-cached-erc1155-decay - -This strategy looks at an ERC1155 caching (staking) contract and assigns a linearly decaying amount of voting power. The business context behind this is that each cached asset is eligible to claim a set amount of ERC20s over the same period; and thus can use those tokens to vote as well. - -For example, at block 0, the voting should have equivalent to 4000 units of voting power. A year later, they should have 0 units. - -As parameters, we pass in the base amount of voting power (e.g. 4000), starting block where there's no decay, and number of months until complete decay (e.g. 12). - -At a high level, the strategy grabs the UNIX timestamp in seconds for starting block, current block, and project timestamp of final block. It then queries the contract for the amount of cached ERC1155s. A simple slope formula is then applied to calculate the decay rate; which is then applied to determine the voting power per asset at current block. - -The final value is square rooted. - -Example of parameters: - -```json - "params": { - "symbol": "PK - PRIME", - "address": "0x3399eff96D4b6Bae8a56F4852EB55736c9C2b041", - "baseValue": 4000, - "startingBlock": 15166749, - "monthsToDecay": 12 - } -``` - diff --git a/src/strategies/echelon-cached-erc1155-decay/examples.json b/src/strategies/echelon-cached-erc1155-decay/examples.json deleted file mode 100644 index fb289d27d..000000000 --- a/src/strategies/echelon-cached-erc1155-decay/examples.json +++ /dev/null @@ -1,28 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "echelon-cached-erc1155-decay", - "params": { - "symbol": "PK - PRIME", - "address": "0x3399eff96D4b6Bae8a56F4852EB55736c9C2b041", - "baseValue": 4000, - "startingBlock": 15166749, - "monthsToDecay": 12 - } - }, - "network": "1", - "addresses": [ - "0xE6be99cbC7796F90baff870a2ffE838a540E27C9", - "0xf98A4A42853cC611eED664627087d4ae19740ED8", - "0xbdc3C931387e2c6647b0D7237Ed30c702260fa80", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e", - "0x1d5E65a087eBc3d03a294412E46CE5D6882969f4", - "0x1f254336E5c46639A851b9CfC165697150a6c327", - "0x2ec3F80BeDA63Ede96BA20375032CDD3aAfb3030" - ], - "snapshot": 15169750 - } -] diff --git a/src/strategies/echelon-cached-erc1155-decay/index.ts b/src/strategies/echelon-cached-erc1155-decay/index.ts deleted file mode 100644 index 1f932adca..000000000 --- a/src/strategies/echelon-cached-erc1155-decay/index.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Multicaller } from '../../utils'; - -export const author = 'brandonleung'; -export const version = '1.0.0'; - -const cachingAbi = [ - 'function cacheInfo(uint256, address) view returns (uint256 amount, int256 rewardDebt)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const stakingPool = new Multicaller(network, provider, cachingAbi, { - blockTag - }); - - const startingBlockTimestamp = ( - await provider.getBlock(options.startingBlock) - ).timestamp; - const endingBlockTimestamp = - startingBlockTimestamp + 2628288 * options.monthsToDecay; - const currentBlockTimestamp = (await provider.getBlock(snapshot)).timestamp; - - const decayRate = - (0 - options.baseValue) / (endingBlockTimestamp - startingBlockTimestamp); - - const votingPowerPerKey = - options.baseValue + - decayRate * (currentBlockTimestamp - startingBlockTimestamp); - - addresses.forEach((address) => { - stakingPool.call(address, options.address, 'cacheInfo', [0, address]); - }); - const response = await stakingPool.execute(); - - return Object.fromEntries( - addresses.map((address) => { - return [ - address, - Math.sqrt(response[address][0].toNumber() * votingPowerPerKey) - ]; - }) - ); -} diff --git a/src/strategies/echelon-wallet-prime-and-cached-key-gated/README.md b/src/strategies/echelon-wallet-prime-and-cached-key-gated/README.md deleted file mode 100644 index 6824fa491..000000000 --- a/src/strategies/echelon-wallet-prime-and-cached-key-gated/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# echelon-wallet-prime-cached-key-gated - -This strategy looks at an ERC1155 caching (staking) contract and assigns a linearly decaying amount of voting power. The business context behind this is that each cached asset is eligible to claim a set amount of ERC20s over the same period; and thus can use those tokens to vote as well. - -For example, at block 0, the voting should have equivalent to 4000 units of voting power. A year later, they should have 0 units. - -As parameters, we pass in the base amount of voting power (e.g. 4000), starting block where there's no decay, and number of months until complete decay (e.g. 12). - -At a high level, the strategy grabs the UNIX timestamp in seconds for starting block, current block, and project timestamp of final block. It then queries the contract for the amount of cached ERC1155s. A simple slope formula is then applied to calculate the decay rate; which is then applied to determine the voting power per asset at current block. - -This strategy also makes use of the `erc20-balance-of` strategy. The erc20 balance is added to the equivalent value of the cached NFT. - -The weighted voting power is square rooted. - -In order to be eligible to vote, the address has to have a non-zero wallet erc1155 balance (using the `erc1155-all-balances-of` strategy) or be whitelisted. Additionally, the address cannot be blacklisted. - -Example of parameters: - -```json - "params": { - "symbol": "PRIME VOTE", - "address": "0xb23d80f5FefcDDaa212212F028021B41DEd428CF", - "decimals": 18, - "stakingAddress": "0x3399eff96D4b6Bae8a56F4852EB55736c9C2b041", - "baseValue": 4000, - "startingBlock": 15166749, - "monthsToDecay": 12, - "erc1155Address": "0x76BE3b62873462d2142405439777e971754E8E77", - "whitelist": [ - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0xFCfcC87E312f323768f9553255250A9357a04109" - ], - "blacklist": ["0xE6be99cbC7796F90baff870a2ffE838a540E27C9"] - } -``` - diff --git a/src/strategies/echelon-wallet-prime-and-cached-key-gated/examples.json b/src/strategies/echelon-wallet-prime-and-cached-key-gated/examples.json deleted file mode 100644 index 387c32c66..000000000 --- a/src/strategies/echelon-wallet-prime-and-cached-key-gated/examples.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "echelon-wallet-prime-and-cached-key-gated", - "params": { - "symbol": "PRIME VOTE", - "address": "0xb23d80f5FefcDDaa212212F028021B41DEd428CF", - "decimals": 18, - "stakingAddress": "0x3399eff96D4b6Bae8a56F4852EB55736c9C2b041", - "baseValue": 4000, - "startingBlock": 15166749, - "monthsToDecay": 12, - "erc1155Address": "0x76BE3b62873462d2142405439777e971754E8E77", - "whitelist": [ - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0xFCfcC87E312f323768f9553255250A9357a04109" - ], - "blacklist": ["0xE6be99cbC7796F90baff870a2ffE838a540E27C9"] - } - }, - "network": "1", - "addresses": [ - "0xE6be99cbC7796F90baff870a2ffE838a540E27C9", - "0xf98A4A42853cC611eED664627087d4ae19740ED8", - "0xbdc3C931387e2c6647b0D7237Ed30c702260fa80", - "0x5566eec3684F3ED896740590cc372758f25f056f", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0xFCfcC87E312f323768f9553255250A9357a04109", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e", - "0x1d5E65a087eBc3d03a294412E46CE5D6882969f4", - "0x1f254336E5c46639A851b9CfC165697150a6c327", - "0x2ec3F80BeDA63Ede96BA20375032CDD3aAfb3030" - ], - "snapshot": 15540462 - } -] diff --git a/src/strategies/echelon-wallet-prime-and-cached-key-gated/index.ts b/src/strategies/echelon-wallet-prime-and-cached-key-gated/index.ts deleted file mode 100644 index 79c7068a7..000000000 --- a/src/strategies/echelon-wallet-prime-and-cached-key-gated/index.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Multicaller } from '../../utils'; -import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; -import { strategy as erc1155AllBalancesOf } from '../erc1155-all-balances-of'; - -export const author = 'brandonleung'; -export const version = '1.0.0'; - -const cachingAbi = [ - 'function cacheInfo(uint256, address) view returns (uint256 amount, int256 rewardDebt)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const stakingPool = new Multicaller(network, provider, cachingAbi, { - blockTag - }); - - const startingBlockTimestamp = ( - await provider.getBlock(options.startingBlock) - ).timestamp; - const endingBlockTimestamp = - startingBlockTimestamp + 2628288 * options.monthsToDecay; - const currentBlockTimestamp = (await provider.getBlock(snapshot)).timestamp; - - const decayRate = - (0 - options.baseValue) / (endingBlockTimestamp - startingBlockTimestamp); - - const votingPowerPerKey = - options.baseValue + - decayRate * (currentBlockTimestamp - startingBlockTimestamp); - - addresses.forEach((address) => { - stakingPool.call(address, options.stakingAddress, 'cacheInfo', [ - 0, - address - ]); - }); - const contractResponse = await stakingPool.execute(); - - const cachedKeyScore = Object.fromEntries( - addresses.map((address) => { - return [ - address, - contractResponse[address][0].toNumber() * votingPowerPerKey - ]; - }) - ); - - const walletScore = await erc20BalanceOfStrategy( - space, - network, - provider, - addresses, - options, - snapshot - ); - - const erc1155Options = { - address: options.erc1155Address - }; - const erc1155Balances = await erc1155AllBalancesOf( - space, - network, - provider, - addresses, - erc1155Options, - snapshot - ); - - const votingPower = Object.entries(walletScore).reduce( - (address, [key, value]) => ({ - ...address, - [key]: (address[key] || 0) + value - }), - { ...cachedKeyScore } - ); - - Object.keys(votingPower).forEach((key) => { - const whitelistedAddress = - options.whitelist.indexOf(key) !== -1 || - (key in erc1155Balances && erc1155Balances[key] > 0); - const blacklistedAddress = options.blacklist.indexOf(key) !== -1; - - votingPower[key] = - whitelistedAddress && !blacklistedAddress - ? Math.sqrt(votingPower[key]) - : 0; - }); - - return votingPower; -} diff --git a/src/strategies/erc1155-all-balances-of/index.ts b/src/strategies/erc1155-all-balances-of/index.ts index d73fce64e..8bf056aea 100644 --- a/src/strategies/erc1155-all-balances-of/index.ts +++ b/src/strategies/erc1155-all-balances-of/index.ts @@ -28,6 +28,11 @@ export async function strategy( const subgraphURL = isHosted ? HOSTED_SUBGRAPH_URL[network] : SUBGRAPH_URL[network]; + + if (!subgraphURL) { + throw new Error(`Unsupported network with id:${network}`); + } + const eip1155BalancesParams: any = { balances: { __aliasFor: 'erc1155Balances', diff --git a/src/strategies/erc20-rebase-wrapper/README.md b/src/strategies/erc20-rebase-wrapper/README.md deleted file mode 100644 index 72e244f15..000000000 --- a/src/strategies/erc20-rebase-wrapper/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# erc20-rebase-wrapper - -This returns the underlying token balance from an ERC20 rebasing wrapper that exposes `exchangeRate` and `exchangeRatePrecision` methods. - -Here is an example of parameters: - -```json -{ - "wrapperAddress": "0x93Dede06AE3B5590aF1d4c111BC54C3f717E4b35", - "symbol": "gALCX", - "decimals": 18 -} -``` diff --git a/src/strategies/erc20-rebase-wrapper/examples.json b/src/strategies/erc20-rebase-wrapper/examples.json deleted file mode 100644 index b9b291ea1..000000000 --- a/src/strategies/erc20-rebase-wrapper/examples.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "erc20-rebase-wrapper", - "params": { - "wrapperAddress": "0x93Dede06AE3B5590aF1d4c111BC54C3f717E4b35", - "symbol": "gALCX", - "decimals": 18 - } - }, - "network": "1", - "addresses": ["0x628Ece38B0FF97e1e98F4F79aD5500596deF66F9"], - "snapshot": 14399823 - } -] diff --git a/src/strategies/erc20-rebase-wrapper/index.ts b/src/strategies/erc20-rebase-wrapper/index.ts deleted file mode 100644 index f1bc31b08..000000000 --- a/src/strategies/erc20-rebase-wrapper/index.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = '0xfoobar'; -export const version = '0.1.0'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function exchangeRate() external view returns (uint256)', - 'function exchangeRatePrecision() external view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - multi.call('exchangeRate', options.wrapperAddress, 'exchangeRate'); - multi.call( - 'exchangeRatePrecision', - options.wrapperAddress, - 'exchangeRatePrecision' - ); - const { exchangeRate, exchangeRatePrecision } = await multi.execute(); - const rate = parseFloat(exchangeRate) / parseFloat(exchangeRatePrecision); - - addresses.forEach((address) => - multi.call(address, options.wrapperAddress, 'balanceOf', [address]) - ); - const result: Record = await multi.execute(); - - return Object.fromEntries( - Object.entries(result).map(([address, balance]) => [ - address, - parseFloat(formatUnits(balance, options.decimals)) * rate - ]) - ); -} diff --git a/src/strategies/erc3525-vesting-voucher/examples.json b/src/strategies/erc3525-vesting-voucher/examples.json deleted file mode 100644 index 3e51d4982..000000000 --- a/src/strategies/erc3525-vesting-voucher/examples.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "erc3525-vesting-voucher", - "params": { - "symbol": "fvSOLV", - "address": "0x4B0dd1aDEdA251ACec75140608bAd663fB0c4cAB", - "decimals": 18 - } - }, - "network": "4", - "addresses": ["0x1a71c8EF63aB6f578b1702a35367cA81c9281A8c"], - "snapshot": 11090903 - } -] diff --git a/src/strategies/erc3525-vesting-voucher/index.ts b/src/strategies/erc3525-vesting-voucher/index.ts deleted file mode 100644 index edf5d5ca0..000000000 --- a/src/strategies/erc3525-vesting-voucher/index.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { hexZeroPad } from '@ethersproject/bytes'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; -import { claimCoefficient, maturitiesCoefficient } from './utils'; - -export const author = 'buchaoqun'; -export const version = '0.1.3'; - -const abi = [ - 'function getSnapshot(uint256 tokenId_) view returns (uint8 claimType_, uint64 term_, uint256 vestingAmount_, uint256 principal_, uint64[] maturities_, uint32[] percentages_, uint256 availableWithdrawAmount_, string originalInvestor_, bool isValid_)', - 'function balanceOf(address owner) view returns (uint256)', - 'function tokenOfOwnerByIndex(address owner,uint256 index) view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - // vesting voucher banlanceOf - const callWalletToCrucibleCount = new Multicaller(network, provider, abi, { - blockTag - }); - for (const walletAddress of addresses) { - callWalletToCrucibleCount.call( - walletAddress, - options.address, - 'balanceOf', - [walletAddress] - ); - } - - // wallet Owner Index - const walletToCrucibleCount: Record = - await callWalletToCrucibleCount.execute(); - - const callWalletToCrucibleAddresses = new Multicaller( - network, - provider, - abi, - { - blockTag - } - ); - for (const [walletAddress, crucibleCount] of Object.entries( - walletToCrucibleCount - )) { - for (let index = 0; index < crucibleCount.toNumber(); index++) { - callWalletToCrucibleAddresses.call( - walletAddress.toString() + '-' + index.toString(), - options.address, - 'tokenOfOwnerByIndex', - [walletAddress, index] - ); - } - } - const walletIDToCrucibleAddresses: Record = - await callWalletToCrucibleAddresses.execute(); - - // voucher snapshot - const callCrucibleToSnapshot = new Multicaller(network, provider, abi, { - blockTag - }); - // walletID: walletAddress-index - for (const [walletID, crucibleAddress] of Object.entries( - walletIDToCrucibleAddresses - )) { - callCrucibleToSnapshot.call(walletID, options.address, 'getSnapshot', [ - hexZeroPad(crucibleAddress.toHexString(), 20) - ]); - } - const walletIDToSnapshot: Record< - string, - Array - > = await callCrucibleToSnapshot.execute(); - - const walletToWeights = {} as Record; - for (const [walletID, snapshot] of Object.entries(walletIDToSnapshot)) { - const address = walletID.split('-')[0]; - - const value = - parseFloat(formatUnits(snapshot[3].toString(), options.decimals)) * - claimCoefficient(snapshot[0]) * - maturitiesCoefficient(snapshot[4]); - walletToWeights[address] = walletToWeights[address] - ? walletToWeights[address] + value - : value; - } - - return Object.fromEntries( - Object.entries(walletToWeights).map(([address, balance]) => [ - address, - balance - ]) - ); -} diff --git a/src/strategies/erc3525-vesting-voucher/utils.ts b/src/strategies/erc3525-vesting-voucher/utils.ts deleted file mode 100644 index 417785e1c..000000000 --- a/src/strategies/erc3525-vesting-voucher/utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -const oneDaySeconds = 86400; - -export const maturitiesCoefficient = (maturities: Array) => { - const nowData = Date.parse(new Date().toString()) / 1000; - const difference = maturities[maturities.length - 1].toNumber() - nowData; - - if (difference <= 0) { - return 1; - } else if (difference > 0 && difference <= 90 * oneDaySeconds) { - return 1.1; - } else if ( - difference > 90 * oneDaySeconds && - difference <= 183 * oneDaySeconds - ) { - return 1.2; - } else if ( - difference > 183 * oneDaySeconds && - difference <= 365 * oneDaySeconds - ) { - return 1.5; - } else { - return 2; - } -}; - -export const claimCoefficient = (claimType: number) => { - if (claimType == 0) { - return 1.2; - } else if (claimType == 1) { - return 2; - } else if (claimType == 2) { - return 1.5; - } else { - return 1; - } -}; diff --git a/src/strategies/esd-delegation/examples.json b/src/strategies/esd-delegation/examples.json deleted file mode 100644 index f45e63fb7..000000000 --- a/src/strategies/esd-delegation/examples.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "name": "Empty Set Dollar", - "strategy": { - "name": "esd-delegation", - "params": { - "uniswap": "0x88ff79eB2Bc5850F27315415da8685282C7610F9", - "rewards": "0x4082D11E506e3250009A991061ACd2176077C88f", - "dao": "0x443d2f2755db5942601fa062cc248aaa153313d3", - "token": "0x36F3FD68E7325a35EB768F1AedaAe9EA0689d723", - "symbol": "ESD", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x37Ed74A0dA66c0392C4c5901c3b3B97675871FE1", - "0x07C867770C43B1c6b715Aa8AC3A55DfD7f835a82" - ], - "snapshot": "latest" - } -] diff --git a/src/strategies/esd-delegation/index.ts b/src/strategies/esd-delegation/index.ts deleted file mode 100644 index d1837c45a..000000000 --- a/src/strategies/esd-delegation/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { strategy as esd } from '../esd'; -import { getDelegations } from '../../utils/delegation'; - -export const author = 'l3wi'; -export const version = '0.1.0'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const delegations = await getDelegations(space, network, addresses, snapshot); - if (Object.keys(delegations).length === 0) return {}; - - const score = await esd( - space, - network, - provider, - Object.values(delegations).reduce((a: string[], b: string[]) => - a.concat(b) - ), - options, - snapshot - ); - - return Object.fromEntries( - addresses.map((address) => { - const addressScore = delegations[address] - ? delegations[address].reduce((a, b) => a + score[b], 0) - : 0; - return [address, addressScore]; - }) - ); -} diff --git a/src/strategies/esd/README.md b/src/strategies/esd/README.md deleted file mode 100644 index 2bc565197..000000000 --- a/src/strategies/esd/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# ESD Call Strategy - -Allows bonded ESD to be counted towards a valid score. - -## Params - -- `dao` - Address of the DAO proxy address -- `token` - Address of the ESD token -- `Rewards` - Address of the current LP rewards contract -- `uniswap` - Address of the Uniswap pool -- `decimals` - Decimals used by the ESD token - -### Examples - -Can be used instead of the erc20-balance-of strategy, the space config will look like this: - -```JSON -"strategies": [ - { - "name": "esd", - "params": { - "uniswap": "0x88ff79eB2Bc5850F27315415da8685282C7610F9", - "rewards": "0x4082D11E506e3250009A991061ACd2176077C88f", - "dao": "0x443d2f2755db5942601fa062cc248aaa153313d3", - "token": "0x36F3FD68E7325a35EB768F1AedaAe9EA0689d723", - "decimals": 18 - } - } -] \ No newline at end of file diff --git a/src/strategies/esd/examples.json b/src/strategies/esd/examples.json deleted file mode 100644 index be6e0aa9a..000000000 --- a/src/strategies/esd/examples.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "name": "Empty Set Dollar", - "strategy": { - "name": "esd", - "params": { - "uniswap": "0x88ff79eB2Bc5850F27315415da8685282C7610F9", - "rewards": "0x4082D11E506e3250009A991061ACd2176077C88f", - "dao": "0x443d2f2755db5942601fa062cc248aaa153313d3", - "token": "0x36F3FD68E7325a35EB768F1AedaAe9EA0689d723", - "symbol": "ESD", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x0b7376f2a063c771d460210a4fa8787c9a7379f9", - "0x635b230c3fdf6a466bb6dc3b9b51a8ceb0659b67", - "0x22fa8cc33a42320385cbd3690ed60a021891cb32", - "0xcF63E1C31805254b6fB3Ed7829206c2b2505e3a7", - "0xd5d5a7cb1807364cde0bad51d0a7d758943ab114", - "0x7055ef0557dc6ff56cdf0c36d28b346d40a1b8ed", - "0xd1991b6fd521a3f357d96a15956702ce33342fec" - ], - "snapshot": 11350000 - } -] diff --git a/src/strategies/esd/index.ts b/src/strategies/esd/index.ts deleted file mode 100644 index 6ae697571..000000000 --- a/src/strategies/esd/index.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; - -export const author = 'l3wi'; -export const version = '0.1.0'; - -const abi = [ - { - constant: true, - inputs: [{ internalType: 'address', name: 'account', type: 'address' }], - name: 'balanceOfBonded', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function' - }, - { - constant: true, - inputs: [{ internalType: 'address', name: '', type: 'address' }], - name: 'balanceOf', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function' - }, - { - constant: true, - inputs: [], - name: 'totalSupply', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function' - } -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const daoQuery = addresses.map((address: any) => [ - options.dao, - 'balanceOfBonded', - [address] - ]); - - const lpQuery = addresses.map((address: any) => [ - options.rewards, - 'balanceOfBonded', - [address] - ]); - - const response = await multicall( - network, - provider, - abi, - [ - [options.token, 'balanceOf', [options.uniswap]], - [options.uniswap, 'totalSupply'], - ...daoQuery, - ...lpQuery - ], - { blockTag } - ); - - const uniswapESD = response[0]; - const uniswapTotalSupply = response[1]; - const daoBalances = response.slice(2, addresses.length + 2); - const lpBalances = response.slice( - addresses.length + 2, - addresses.length * 2 + 2 - ); - - return Object.fromEntries( - Array(addresses.length) - .fill('x') - .map((_, i) => [ - addresses[i], - parseFloat( - formatUnits( - uniswapESD[0] - .div(uniswapTotalSupply[0]) - .mul(lpBalances[i][0]) - .add(daoBalances[i][0]) - .toString(), - options.decimals - ) - ) - ]) - ); -} diff --git a/src/strategies/eth-philanthropy/README.md b/src/strategies/eth-philanthropy/README.md deleted file mode 100644 index f0be0baaa..000000000 --- a/src/strategies/eth-philanthropy/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# Contract call strategy - -Enables addresses to increase their score by donating to charity, adding `params.coeff` ETH to their score for every 1 ETH donated. This helps to level the playing field for less wealthy participants and incentivizes altruistic behavior from all participants. - -Implementation of [eth-received](./../eth-received) - -### Currently Included Charities: -- [GiveDirectly](https://www.givedirectly.org/): 0xc7464dbcA260A8faF033460622B23467Df5AEA42 -- [Unsung.org](http://Unsung.org): 0x02a13ED1805624738Cc129370Fee358ea487B0C6 -- [Heifer.org](http://Heifer.org): 0xD3F81260a44A1df7A7269CF66Abd9c7e4f8CdcD1 -- [GraceAid.org.uk](http://GraceAid.org.uk): 0x236dAA98f115caa9991A3894ae387CDc13eaaD1B -- [SENS.org](http://SENS.org): 0x542EFf118023cfF2821b24156a507a513Fe93539 -- [350.org](http://350.org): 0x50990F09d4f0cb864b8e046e7edC749dE410916b -- [EFF.org](http://EFF.org): 0xb189f76323678E094D4996d182A792E52369c005 -- [WikiLeaks](http://WikiLeaks.org): 0xE96E2181F6166A37EA4C04F6E6E2bD672D72Acc1 -- [GiveWell.org](http://GiveWell.org): 0x7cF2eBb5Ca55A8bd671A020F8BDbAF07f60F26C1 -- [CoolEarth.org](http://CoolEarth.org): 0x3c8cB169281196737c493AfFA8F49a9d823bB9c5 -- [Run2Rescue.org](http://Run2Rescue.org): 0xd17bcbFa6De9E3741aa43Ed32e64696F6a9FA996 -- [Archive.org](http://Archive.org): 0xFA8E3920daF271daB92Be9B87d9998DDd94FEF08 -- [Alt.eco](http://alt.eco): 0x5E7ecc1fC819f937fbEe043b40388C0809361ae9 - -## Params - -- `coeff` - (**Optional**, `number`, Default: `100`) Amount to multiply the sum of a voter's ether sent to charity. When used in conjunction with other strategies, this enables the increase or decrease of leverage given to voters who donate. - - -## Examples - -Can be used instead of, or in conjunction with eth-balance strategy. -In this example, the `params.coeff` of `1000` makes a 1 ETH donation equivalent to a 1000 ETH address balance. Thus, giving voters a massive incentive to donate. - -The space config will look like this: - -```JSON -"strategies": [ - { - "name": "Example ETH-Philanthropy Strategy", - "strategy": { - "name": "eth-philanthropy", - "params": { - "coeff": 1000, - "receivingAddresses": [ - "0xc7464dbcA260A8faF033460622B23467Df5AEA42", - "0x02a13ED1805624738Cc129370Fee358ea487B0C6", - "0xD3F81260a44A1df7A7269CF66Abd9c7e4f8CdcD1", - "0x236dAA98f115caa9991A3894ae387CDc13eaaD1B", - "0x542EFf118023cfF2821b24156a507a513Fe93539", - "0x50990F09d4f0cb864b8e046e7edC749dE410916b", - "0xb189f76323678E094D4996d182A792E52369c005", - "0xE96E2181F6166A37EA4C04F6E6E2bD672D72Acc1", - "0x7cF2eBb5Ca55A8bd671A020F8BDbAF07f60F26C1", - "0x3c8cB169281196737c493AfFA8F49a9d823bB9c5", - "0xd17bcbFa6De9E3741aa43Ed32e64696F6a9FA996", - "0xFA8E3920daF271daB92Be9B87d9998DDd94FEF08", - "0x5E7ecc1fC819f937fbEe043b40388C0809361ae9" - ] - } - }, - "network": "1", - "addresses": [ - "0x100fb703c8b84466f79e838835df6fc180aef740", - "0xbcB5f94590904A64e16Acb08D4Fa4b7baFdC8c3A", - "0x0a0249179f559f60496e23e3907e9a3a54aa6537", - "0x6c65fb326e7734ba5508b5d043718288b43b9ed9", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e" - ], - "snapshot": 11414195 - }, - { - "name": "Example ETH-Balance Strategy", - "strategy": { - "name": "eth-balance", - "params": {} - }, - "network": "1", - "addresses": [ - "0x100fb703c8b84466f79e838835df6fc180aef740", - "0xbcB5f94590904A64e16Acb08D4Fa4b7baFdC8c3A", - "0x0a0249179f559f60496e23e3907e9a3a54aa6537", - "0x6c65fb326e7734ba5508b5d043718288b43b9ed9", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e" - ], - "snapshot": 11414195 - } -] -``` - -Valid test addresses and snapshot block number: -```typescript -const addresses = [ - "0x100fb703c8b84466f79e838835df6fc180aef740", - "0xbcB5f94590904A64e16Acb08D4Fa4b7baFdC8c3A", - "0x0a0249179f559f60496e23e3907e9a3a54aa6537", - "0x6c65fb326e7734ba5508b5d043718288b43b9ed9", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e" -]; - -const snapshot = 11414195; -``` diff --git a/src/strategies/eth-philanthropy/examples.json b/src/strategies/eth-philanthropy/examples.json deleted file mode 100644 index 1a2e01d0a..000000000 --- a/src/strategies/eth-philanthropy/examples.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "name": "Example ETH Philanthropy Query", - "strategy": { - "name": "eth-philanthropy", - "params": { - "symbol": "ETH", - "coeff": 50 - } - }, - "network": "1", - "addresses": [ - "0x100fb703c8b84466f79e838835df6fc180aef740", - "0xbcB5f94590904A64e16Acb08D4Fa4b7baFdC8c3A", - "0x0a0249179f559f60496e23e3907e9a3a54aa6537", - "0x6c65fb326e7734ba5508b5d043718288b43b9ed9", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e", - "0x5E7ecc1fC819f937fbEe043b40388C0809361ae9" - ], - "snapshot": 11486650 - } -] diff --git a/src/strategies/eth-philanthropy/index.ts b/src/strategies/eth-philanthropy/index.ts deleted file mode 100644 index 920f56a71..000000000 --- a/src/strategies/eth-philanthropy/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Web3Provider } from '@ethersproject/providers'; -import { strategy as ethReceivedStrategy } from '../eth-received'; - -export const author = 'mccallofthewild'; -export const version = '0.1.0'; - -const ethCharities = [ - ['GiveDirectly', '0xc7464dbcA260A8faF033460622B23467Df5AEA42'], - ['Unsung.org', '0x02a13ED1805624738Cc129370Fee358ea487B0C6'], - ['Heifer.org', '0xD3F81260a44A1df7A7269CF66Abd9c7e4f8CdcD1'], - ['GraceAid.org.uk', '0x236dAA98f115caa9991A3894ae387CDc13eaaD1B'], - ['SENS.org', '0x542EFf118023cfF2821b24156a507a513Fe93539'], - ['350.org', '0x50990F09d4f0cb864b8e046e7edC749dE410916b'], - ['EFF.org', '0xb189f76323678E094D4996d182A792E52369c005'], - ['WikiLeaks', '0xE96E2181F6166A37EA4C04F6E6E2bD672D72Acc1'], - ['GiveWell.org', '0x7cF2eBb5Ca55A8bd671A020F8BDbAF07f60F26C1'], - ['CoolEarth.org', '0x3c8cB169281196737c493AfFA8F49a9d823bB9c5'], - ['Run2Rescue.org', '0xd17bcbFa6De9E3741aa43Ed32e64696F6a9FA996'], - ['Archive.org', '0xFA8E3920daF271daB92Be9B87d9998DDd94FEF08'] -]; - -export async function strategy( - ...args: [string, string, Web3Provider, string[], { coeff?: number }, number] -) { - const [space, network, provider, addresses, options, snapshot] = args; - const { coeff = 100 } = options; - return ethReceivedStrategy( - space, - network, - provider, - addresses, - { - receivingAddresses: ethCharities.map(([, address]) => address), - coeff - }, - snapshot - ); -} diff --git a/src/strategies/eth-received/ElasticSearchTxResult.ts b/src/strategies/eth-received/ElasticSearchTxResult.ts deleted file mode 100644 index 07f0e116a..000000000 --- a/src/strategies/eth-received/ElasticSearchTxResult.ts +++ /dev/null @@ -1,81 +0,0 @@ -export interface ElasticSearchTxResult { - took: number; - timed_out: boolean; - _shards: Shards; - hits: Hits; -} - -export interface Shards { - total: number; - successful: number; - skipped: number; - failed: number; -} - -export interface Hits { - total: Total; - max_score: number; - hits: Hit[]; -} - -export interface Hit { - _index: Index; - _type: Type; - _id: string; - _score: number; - _source: Source; -} - -export enum Index { - The0011EthereumEthereumMainnetTx = '0011-ethereum-ethereum-mainnet-tx' -} - -export interface Source { - blockHash: string; - blockNumber: BlockNumber; - cumulativeGasUsed: BlockNumber; - from: string; - gas: BlockNumber; - gasPrice: BlockNumber; - gasUsed: BlockNumber; - hash: string; - input: string; - logsBloom: string; - nonce: BlockNumber; - r: string; - s: string; - status: boolean; - timestamp: number; - to: string; - publicKey?: string; - transactionIndex: BlockNumber; - standardV?: string; - v: V; - value: Value; -} - -export interface BlockNumber { - raw: string; - num: number; -} - -export enum V { - The0X25 = '0x25', - The0X26 = '0x26' -} - -export interface Value { - raw: string; - padded: string; - eth: number; - num: number; -} - -export enum Type { - Doc = '_doc' -} - -export interface Total { - value: number; - relation: string; -} diff --git a/src/strategies/eth-received/README.md b/src/strategies/eth-received/README.md deleted file mode 100644 index 36e2c5210..000000000 --- a/src/strategies/eth-received/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Contract call strategy - -Scores addresses by how much ETH they have sent to `params.receivingAddresses`, adding `params.coeff` ETH to their score for every 1 ETH sent. - -This creates a new fundraising opportunity for projects & organizations, levels the playing field for less wealthy participants, and encourages voters to "put their money where their mouth is". - -## Params - -- `receivingAddresses` - (**Required**, `string[]`) Array of addresses to check for ether transactions from voters -- `coeff` - (**Optional**, `number`, Default: `1`) Amount to multiply the sum of a voter's ether sent to `receivingAddresses`. When used in conjunction with other strategies, this enables the increase or decrease of leverage given to voter who send ETH. - - -## Examples - -Can be used instead of, or in conjunction with eth-balance strategy. -In this example, the `params.coeff` of `100` makes a 1 ETH donation equivalent to a 100 ETH address balance. Thus, giving voters a massive incentive to donate. - -The space config will look like this: - -```JSON -"strategies": [ - { - "name": "Example ETH-Received Strategy", - "strategy": { - "name": "eth-received", - "params": { - "coeff": 100, - "receivingAddresses": [ - "0xc7464dbcA260A8faF033460622B23467Df5AEA42", - "0x02a13ED1805624738Cc129370Fee358ea487B0C6", - "0xD3F81260a44A1df7A7269CF66Abd9c7e4f8CdcD1", - "0x236dAA98f115caa9991A3894ae387CDc13eaaD1B", - "0x542EFf118023cfF2821b24156a507a513Fe93539", - "0x50990F09d4f0cb864b8e046e7edC749dE410916b", - "0xb189f76323678E094D4996d182A792E52369c005", - "0xE96E2181F6166A37EA4C04F6E6E2bD672D72Acc1", - "0x7cF2eBb5Ca55A8bd671A020F8BDbAF07f60F26C1", - "0x3c8cB169281196737c493AfFA8F49a9d823bB9c5", - "0xd17bcbFa6De9E3741aa43Ed32e64696F6a9FA996", - "0xFA8E3920daF271daB92Be9B87d9998DDd94FEF08" - ] - } - }, - "network": "1", - "addresses": [ - "0x100fb703c8b84466f79e838835df6fc180aef740", - "0xbcB5f94590904A64e16Acb08D4Fa4b7baFdC8c3A", - "0x0a0249179f559f60496e23e3907e9a3a54aa6537", - "0x6c65fb326e7734ba5508b5d043718288b43b9ed9", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e" - ], - "snapshot": 11414195 - }, - { - "name": "Example ETH-Balance Strategy", - "strategy": { - "name": "eth-balance", - "params": {} - }, - "network": "1", - "addresses": [ - "0x100fb703c8b84466f79e838835df6fc180aef740", - "0xbcB5f94590904A64e16Acb08D4Fa4b7baFdC8c3A", - "0x0a0249179f559f60496e23e3907e9a3a54aa6537", - "0x6c65fb326e7734ba5508b5d043718288b43b9ed9", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e" - ], - "snapshot": 11414195 - } -] -``` - -Valid test addresses and snapshot block number: -```typescript -const addresses = [ - "0x100fb703c8b84466f79e838835df6fc180aef740", - "0xbcB5f94590904A64e16Acb08D4Fa4b7baFdC8c3A", - "0x0a0249179f559f60496e23e3907e9a3a54aa6537", - "0x6c65fb326e7734ba5508b5d043718288b43b9ed9", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e" -]; - -const snapshot = 11414195; -``` diff --git a/src/strategies/eth-received/examples.json b/src/strategies/eth-received/examples.json deleted file mode 100644 index e78a1eb45..000000000 --- a/src/strategies/eth-received/examples.json +++ /dev/null @@ -1,70 +0,0 @@ -[ - { - "name": "Example ETH Received Strategy", - "strategy": { - "name": "eth-received", - "params": { - "symbol": "ETH", - "receivingAddresses": [ - "0xc7464dbcA260A8faF033460622B23467Df5AEA42", - "0x02a13ED1805624738Cc129370Fee358ea487B0C6", - "0xD3F81260a44A1df7A7269CF66Abd9c7e4f8CdcD1", - "0x236dAA98f115caa9991A3894ae387CDc13eaaD1B", - "0x542EFf118023cfF2821b24156a507a513Fe93539", - "0x50990F09d4f0cb864b8e046e7edC749dE410916b", - "0xb189f76323678E094D4996d182A792E52369c005", - "0xE96E2181F6166A37EA4C04F6E6E2bD672D72Acc1", - "0x7cF2eBb5Ca55A8bd671A020F8BDbAF07f60F26C1", - "0x3c8cB169281196737c493AfFA8F49a9d823bB9c5", - "0xd17bcbFa6De9E3741aa43Ed32e64696F6a9FA996", - "0xFA8E3920daF271daB92Be9B87d9998DDd94FEF08" - ] - } - }, - "network": "1", - "addresses": [ - "0x100fb703c8b84466f79e838835df6fc180aef740", - "0xbcB5f94590904A64e16Acb08D4Fa4b7baFdC8c3A", - "0x0a0249179f559f60496e23e3907e9a3a54aa6537", - "0x6c65fb326e7734ba5508b5d043718288b43b9ed9", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e" - ], - "snapshot": 11486650 - }, - { - "name": "Example ETH Received Query", - "strategy": { - "name": "eth-received", - "params": { - "coeff": 100, - "receivingAddresses": [ - "0xc7464dbcA260A8faF033460622B23467Df5AEA42", - "0x02a13ED1805624738Cc129370Fee358ea487B0C6", - "0xD3F81260a44A1df7A7269CF66Abd9c7e4f8CdcD1", - "0x236dAA98f115caa9991A3894ae387CDc13eaaD1B", - "0x542EFf118023cfF2821b24156a507a513Fe93539", - "0x50990F09d4f0cb864b8e046e7edC749dE410916b", - "0xb189f76323678E094D4996d182A792E52369c005", - "0xE96E2181F6166A37EA4C04F6E6E2bD672D72Acc1", - "0x7cF2eBb5Ca55A8bd671A020F8BDbAF07f60F26C1", - "0x3c8cB169281196737c493AfFA8F49a9d823bB9c5", - "0xd17bcbFa6De9E3741aa43Ed32e64696F6a9FA996", - "0xFA8E3920daF271daB92Be9B87d9998DDd94FEF08" - ] - } - }, - "network": "1", - "addresses": [ - "0x100fb703c8b84466f79e838835df6fc180aef740", - "0xbcB5f94590904A64e16Acb08D4Fa4b7baFdC8c3A", - "0x0a0249179f559f60496e23e3907e9a3a54aa6537", - "0x6c65fb326e7734ba5508b5d043718288b43b9ed9", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e" - ], - "snapshot": 11486650 - } -] diff --git a/src/strategies/eth-received/index.ts b/src/strategies/eth-received/index.ts deleted file mode 100644 index 76616bc59..000000000 --- a/src/strategies/eth-received/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -import fetch from 'cross-fetch'; -import { Web3Provider } from '@ethersproject/providers'; -import { ElasticSearchTxResult } from './ElasticSearchTxResult'; - -export const author = 'mccallofthewild'; -export const version = '0.1.0'; - -export async function strategy( - ...args: [ - string, - string, - Web3Provider, - string[], - { coeff?: number; receivingAddresses: string[] }, - number - ] -) { - const [, , , addresses, options] = args; - const { coeff = 1, receivingAddresses } = options; - // queries AnyBlock ElasticSearch https://www.anyblockanalytics.com/ - // Account: yidirel126@95ta.com Pass: xU5KKfys76wb633FvGS6 - const charitableTransactions: ElasticSearchTxResult = await fetch( - 'https://api.anyblock.tools/ethereum/ethereum/mainnet/es/tx/search/', - { - method: 'POST', - body: JSON.stringify({ - from: 0, - size: 10000, - query: { - bool: { - must: [ - { - bool: { - should: [ - ...addresses.map((a) => ({ - match: { - from: a - } - })) - ] - } - }, - { - bool: { - should: [ - ...receivingAddresses.map((a) => ({ - match: { - to: a - } - })) - ] - } - } - ] - } - } - }), - headers: { - Authorization: 'Bearer 8c8b3826-afd5-4535-a8be-540562624fbe', - 'Content-Type': 'application/json' - } - } - ) - .then((r) => r.json()) - .catch((e) => { - console.error('Eth-Received AnyBlock ElasticSearch Query Failed:'); - throw e; - }); - - const scores = {}; - for (const address of addresses) { - scores[address] = charitableTransactions.hits.hits - .filter((tx) => { - const validAddress = - tx._source.from.toLowerCase() == address.toLowerCase(); - return validAddress; - }) - .reduce((prev, curr) => { - return prev + curr._source.value.eth * coeff; - }, 0); - } - return scores; -} diff --git a/src/strategies/ethercats-founder-series/examples.json b/src/strategies/ethercats-founder-series/examples.json deleted file mode 100644 index 9efec74a2..000000000 --- a/src/strategies/ethercats-founder-series/examples.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "ethercats-founder-series", - "params": { - "symbol": "EtherCat" - } - }, - "network": "1", - "addresses": ["0x0e917dcffb792dbfb36b75afcc1a5d6174ec94e2"], - "snapshot": 14121348 - } -] diff --git a/src/strategies/ethercats-founder-series/index.ts b/src/strategies/ethercats-founder-series/index.ts deleted file mode 100644 index 2a4736594..000000000 --- a/src/strategies/ethercats-founder-series/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { subgraphRequest } from '../../utils'; - -export const author = 'woodydeck'; -export const version = '1.0.0'; - -// Constants -const url = { - '1': 'https://gateway.thegraph.com/api/656e05ff867c74eeb11bf0199ff5de86/subgraphs/id/0x7859821024e633c5dc8a4fcf86fc52e7720ce525-1' -}; - -const getPower = (id, value) => { - if (value == 0) return 0; - return value * (parseInt(id.slice(2, 4)) * parseInt(id[4])); -}; - -// Strategy -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const requests = addresses.map((a) => ({ - erc1155Balances: { - __args: { - block: { number: snapshot !== 'latest' ? snapshot : undefined }, - where: { - contract: '0xff3559412c4618af7c6e6f166c74252ff6364456', - account: a.toLowerCase() - } - }, - valueExact: true, - token: { - identifier: true - } - } - })); - - const responses = await Promise.all( - requests.map((request) => subgraphRequest(url[network], request)) - ); - - return Object.fromEntries( - responses.map((response, i) => [ - addresses[i], - response.erc1155Balances?.length > 0 - ? response.erc1155Balances - .map((balance) => { - return getPower( - balance?.token?.identifier || '0', - balance?.valueExact || '0' - ); - }) - .reduce((prev, curr) => prev + curr, 0) - : 0 - ]) - ); -} diff --git a/src/strategies/floki/README.md b/src/strategies/floki/README.md new file mode 100644 index 000000000..9a1d903b4 --- /dev/null +++ b/src/strategies/floki/README.md @@ -0,0 +1,14 @@ +# FLOKI + +This is the strategy used by FLOKI DAO. It returns the FLOKI balance, as well as staked amount with pool's multiplier taken into account (which depends on how long the tokens were staked for). + +Here is an example of parameters: + +```json +{ + "tokenAddress": "0xcf0C122c6b73ff809C693DB761e7BaeBe62b6a2E", + "stakingPoolAddress": "0xb8D2471E35eE033Db509e0456c8eFc4135f4EE43", + "stakingPoolMultiplierAddress": "0xB254CC6c1D178C2dE8182CEDE6113A986bB90721", + "decimals": 9 +} +``` diff --git a/src/strategies/floki/examples.json b/src/strategies/floki/examples.json new file mode 100644 index 000000000..e2cf4483f --- /dev/null +++ b/src/strategies/floki/examples.json @@ -0,0 +1,22 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "floki", + "params": { + "tokenAddress": "0xcf0C122c6b73ff809C693DB761e7BaeBe62b6a2E", + "stakingPoolAddress": "0xb8D2471E35eE033Db509e0456c8eFc4135f4EE43", + "stakingPoolMultiplierAddress": "0xB254CC6c1D178C2dE8182CEDE6113A986bB90721", + "decimals": 9 + } + }, + "network": "1", + "addresses": [ + "0x78C4f5CEF16333804394fE736fC5868351968Fd7", + "0x2cc848EA4C5313Fe5a264D12c7c2181f14f0A5E7", + "0x5BA9e392Baf5D082d968E1BFA945F99e54BF8123", + "0x308DA792A0b9c332D5254bB2afB78640d363e2d1" + ], + "snapshot": 18458827 + } +] diff --git a/src/strategies/floki/index.ts b/src/strategies/floki/index.ts new file mode 100644 index 000000000..911a8e0d9 --- /dev/null +++ b/src/strategies/floki/index.ts @@ -0,0 +1,80 @@ +import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; +import { formatUnits } from '@ethersproject/units'; +import { Multicaller } from '../../utils'; + +export const author = 'AlissonRS'; +export const version = '0.0.1'; + +const tokenAbi = [ + 'function balanceOf(address account) external view returns (uint256)' +]; + +const poolAbi = [ + 'function getUserStakes(address _user) external view returns (tuple(uint256 stakedAmount, uint256 minimumStakeTimestamp, uint256 duration, uint256 rewardPerTokenPaid, uint256 rewards)[])' +]; + +const multiplierAbi = [ + 'function applyMultiplier(uint256 _amount, uint256 _duration) external view returns (uint256)' +]; + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +): Promise> { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + const balanceMulti = new Multicaller(network, provider, tokenAbi, { + blockTag + }); + addresses.forEach((address) => + balanceMulti.call(address, options.tokenAddress, 'balanceOf', [address]) + ); + const balanceResult: Record = + await balanceMulti.execute(); + + // Find the staked tokens + const stakingMulti = new Multicaller(network, provider, poolAbi, { + blockTag + }); + addresses.forEach((address) => + stakingMulti.call(address, options.stakingPoolAddress, 'getUserStakes', [ + address + ]) + ); + const stakingResult: Record = await stakingMulti.execute(); + + // Get the multiplier factor for each wallet + const multiplierMulti = new Multicaller(network, provider, multiplierAbi, { + blockTag + }); + Object.entries(stakingResult).forEach(([address, stakesInfo]) => { + stakesInfo.forEach((stakeInfo, i) => + multiplierMulti.call( + `${address}-${i}`, + options.stakingPoolMultiplierAddress, + 'applyMultiplier', + [stakeInfo.stakedAmount, stakeInfo.duration] + ) + ); + }); + const multiResult: Record = await multiplierMulti.execute(); + + // Add staking tokens to the balance + Object.entries(multiResult).forEach(([addressPos, stakeBalance]) => { + const address = addressPos.substring(0, 42); + balanceResult[address] = BigNumber.from(balanceResult[address]).add( + stakeBalance + ); + }); + + return Object.fromEntries( + Object.entries(balanceResult).map(([address, balance]) => [ + address, + parseFloat(formatUnits(balance, options.decimals)) + ]) + ); +} diff --git a/src/strategies/floki/schema.json b/src/strategies/floki/schema.json new file mode 100644 index 000000000..bc871a542 --- /dev/null +++ b/src/strategies/floki/schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/Strategy", + "definitions": { + "Strategy": { + "title": "Strategy", + "type": "object", + "properties": { + "symbol": { + "type": "string", + "title": "Symbol", + "examples": ["e.g. FLOKI"], + "maxLength": 16 + }, + "tokenAddress": { + "type": "string", + "title": "Token address", + "examples": ["e.g. 0xcf0C122c6b73ff809C693DB761e7BaeBe62b6a2E"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "stakingPoolAddress": { + "type": "string", + "title": "Staking Pool address", + "examples": ["e.g. 0xb8D2471E35eE033Db509e0456c8eFc4135f4EE43"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "stakingPoolMultiplierAddress": { + "type": "string", + "title": "Staking Pool Multiplier address", + "examples": ["e.g. 0xB254CC6c1D178C2dE8182CEDE6113A986bB90721"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "decimals": { + "type": "number", + "title": "Decimals", + "examples": ["e.g. 9"] + } + }, + "required": [ + "tokenAddress", + "stakingPoolAddress", + "stakingPoolMultiplierAddress", + "decimals" + ], + "additionalProperties": false + } + } +} diff --git a/src/strategies/gin-finance/examples.json b/src/strategies/gin-finance/examples.json deleted file mode 100644 index ff70d8925..000000000 --- a/src/strategies/gin-finance/examples.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "gin-finance", - "params": { - "address": "0x66a2a913e447d6b4bf33efbec43aaef87890fbbc", - "symbol": "USDC" - } - }, - "network": "288", - "addresses": ["0xb50edc1F33b0905A9D70A8733712244E8C6E035b"], - "snapshot": 649260 - } -] diff --git a/src/strategies/gin-finance/index.ts b/src/strategies/gin-finance/index.ts deleted file mode 100644 index 168cb3501..000000000 --- a/src/strategies/gin-finance/index.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { getAddress } from '@ethersproject/address'; -import { subgraphRequest } from '../../utils'; - -const GIN_FINANCE_SUBGRAPH_URL = { - '288': 'https://api.thegraph.com/subgraphs/name/gindev2/gin-subgraph' -}; - -export const author = 'perpetuum7'; -export const version = '1.0.0'; - -export async function strategy( - _space: string, - network: string, - _provider, - addresses: string[], - options: { address: string }, - snapshot?: number | string -) { - const params = { - users: { - __args: { - where: { - id_in: addresses.map((address) => address.toLowerCase()) - }, - first: 1000 - }, - id: true, - liquidityPositions: { - __args: { - where: { - liquidityTokenBalance_gt: 0 - } - }, - liquidityTokenBalance: true, - pair: { - id: true, - token0: { - id: true - }, - reserve0: true, - token1: { - id: true - }, - reserve1: true, - totalSupply: true - } - } - } - }; - - if (snapshot !== 'latest') { - // @ts-ignore - params.users.liquidityPositions.__args.block = { number: snapshot }; - } - - const tokenAddress = options.address.toLowerCase(); - const result = await subgraphRequest( - GIN_FINANCE_SUBGRAPH_URL[network], - params - ); - - const score = {}; - - if (result && result.users) { - result.users.forEach((u) => { - u.liquidityPositions - .filter( - (lp) => - lp.pair.token0.id == tokenAddress || - lp.pair.token1.id == tokenAddress - ) - .forEach((lp) => { - const token0perUni = lp.pair.reserve0 / lp.pair.totalSupply; - const token1perUni = lp.pair.reserve1 / lp.pair.totalSupply; - const userScore = - lp.pair.token0.id == tokenAddress - ? token0perUni * lp.liquidityTokenBalance - : token1perUni * lp.liquidityTokenBalance; - - const userAddress = getAddress(u.id); - if (!score[userAddress]) score[userAddress] = 0; - score[userAddress] = score[userAddress] + userScore; - }); - }); - } - - return score || {}; -} diff --git a/src/strategies/giveth-xdai-balance/README.md b/src/strategies/giveth-xdai-balance/README.md deleted file mode 100644 index 6bc76c77b..000000000 --- a/src/strategies/giveth-xdai-balance/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Giveth xDai balance - -This strategy sums up all the GIV on xDai, including the ones that are staked in Honeyswap pools, Sushiswap pools, and single staked on GIV pool. - -Here is an example of parameters: - -```json -{ - "symbol": "GIV", - "decimals": 18 -} -``` diff --git a/src/strategies/giveth-xdai-balance/examples.json b/src/strategies/giveth-xdai-balance/examples.json deleted file mode 100644 index 8bcc93079..000000000 --- a/src/strategies/giveth-xdai-balance/examples.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "giveth-xdai-balance", - "params": { - "symbol": "GIV", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x839395e20bbb182fa440d08f850e6c7a8f6f0780", - "0x960a16c9070a9bbbb03e1bfd418982636d56d77d", - "0x00d18ca9782be1caef611017c2fbc1a39779a57c" - ], - "snapshot": 14036425 - } -] diff --git a/src/strategies/giveth-xdai-balance/index.ts b/src/strategies/giveth-xdai-balance/index.ts deleted file mode 100644 index 0f8ece58e..000000000 --- a/src/strategies/giveth-xdai-balance/index.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { subgraphRequest } from '../../utils'; -import { BigNumber } from '@ethersproject/bignumber'; -import { getAddress } from '@ethersproject/address'; -import { parseUnits, formatUnits } from '@ethersproject/units'; - -export const author = 'pkretzschmar'; -export const version = '0.1.0'; - -const GIVETH_SUBGRAPH_API = - 'https://api.thegraph.com/subgraphs/name/giveth/giveth-economy-xdai'; -const XDAI_BLOCKS_API = - 'https://api.thegraph.com/subgraphs/name/elkfinance/xdai-blocks'; -const PAIR_IDS = [ - '0x08ea9f608656a4a775ef73f5b187a2f1ae2ae10e', - '0x55ff0cef43f0df88226e9d87d09fa036017f5586' -]; -const PAIR_APIS = [ - 'https://api.thegraph.com/subgraphs/name/1hive/honeyswap-xdai', - 'https://api.thegraph.com/subgraphs/name/sushiswap/xdai-exchange' -]; - -const blockParams = { - blocks: { - __args: { - first: 1, - orderBy: 'timestamp', - orderDirection: 'desc', - where: { - timestamp_lte: '' - } - }, - number: true - } -}; - -const pairParams = { - pair: { - __args: { - id: '' - }, - reserve0: true, - totalSupply: true - } -}; - -const params = { - balances: { - __args: { - orderBy: 'id', - orderDirection: 'asc', - where: { - id_in: [] - } - }, - id: true, - balance: true, - givStaked: true, - honeyswapLp: true, - honeyswapLpStaked: true, - sushiswapLp: true, - sushiSwapLpStaked: true - } -}; - -const formatReserveBalance = (data, decimals) => { - const reserve = parseUnits(data.pair.reserve0, decimals); - const totalSupply = parseUnits(data.pair.totalSupply, decimals); - return { reserve, totalSupply }; -}; - -const calcGivAmount = ( - amountLP: BigNumber, - totalLP: BigNumber, - givBalance: BigNumber -): BigNumber => { - return amountLP.mul(givBalance).div(totalLP); -}; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const block = await provider.getBlock(blockTag); - blockParams.blocks.__args.where.timestamp_lte = block.timestamp; - const xDaiBlock = await subgraphRequest(XDAI_BLOCKS_API, blockParams); - const blockNumber = Number(xDaiBlock.blocks[0].number); - // @ts-ignore - params.balances.__args.block = { number: blockNumber }; - if (snapshot !== 'latest') { - // @ts-ignore - pairParams.pair.__args.block = { number: blockNumber }; - } - - params.balances.__args.where.id_in = addresses.map((address) => - address.toLowerCase() - ); - - const requests = PAIR_APIS.map((API, index) => { - pairParams.pair.__args.id = PAIR_IDS[index]; - return subgraphRequest(API, pairParams); - }); - requests.push(subgraphRequest(GIVETH_SUBGRAPH_API, params)); - const [hnyData, sushiData, data] = await Promise.all(requests); - - const hnyFormatedData = formatReserveBalance(hnyData, options.decimals); - const sushiFormatedData = formatReserveBalance(sushiData, options.decimals); - const dataBalances = data.balances; - - const score = {}; - dataBalances.map((addressBalance) => { - const { - id, - balance, - givStaked, - honeyswapLp, - honeyswapLpStaked, - sushiswapLp, - sushiSwapLpStaked - } = addressBalance; - const totalGIV = BigNumber.from(balance).add(givStaked); - const hnyGIV = calcGivAmount( - BigNumber.from(honeyswapLp).add(honeyswapLpStaked), - hnyFormatedData.totalSupply, - hnyFormatedData.reserve - ); - const sushiGIV = calcGivAmount( - BigNumber.from(sushiswapLp).add(sushiSwapLpStaked), - sushiFormatedData.totalSupply, - sushiFormatedData.reserve - ); - score[getAddress(id)] = parseFloat( - formatUnits(totalGIV.add(hnyGIV).add(sushiGIV), options.decimals) - ); - }); - return score; -} diff --git a/src/strategies/gysr-pending-rewards/README.md b/src/strategies/gysr-pending-rewards/README.md deleted file mode 100644 index ce3b200ba..000000000 --- a/src/strategies/gysr-pending-rewards/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# GYSR Pending Rewards - -This strategy returns the pending rewards for each user in a specified GYSR pool. It supports the networks the GYSR Pool Info is available. - -Here is an example of parameters: - -```json -{ - // Required - "pool": "0xE48eddbBcA614be5416f20dE57D858562b72479d", - - // Optional - "rewardToken": "0xb521022EeaD7E7eEe95D30BA1A1f0aB657F83a61", - "symbol": "REX" -} -``` diff --git a/src/strategies/gysr-pending-rewards/examples.json b/src/strategies/gysr-pending-rewards/examples.json deleted file mode 100644 index 6d42e5dbc..000000000 --- a/src/strategies/gysr-pending-rewards/examples.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "name": "Example GYSR Pending Rewards query", - "strategy": { - "name": "gysr-pending-rewards", - "params": { - "pool": "0x30c0f65d9b27ebe2cc2a49cbcb4133230b3fb381", - "rewardToken": "0xbEa98c05eEAe2f3bC8c3565Db7551Eb738c8CCAb", - "symbol": "GYSR" - } - }, - "network": 1, - "addresses": [ - "0xc56e357599de4418c3a33ad611130d2b2b36b19a", - "0x3a5a5ed68b3eea83df82fbd3d16b16562e205ffb", - "0x6c2448c22a7947bd4fd886b719618b913cd09538", - "0x30c0f65d9b27ebe2cc2a49cbcb4133230b3fb381" - ], - "snapshot": 15410000 - } -] diff --git a/src/strategies/gysr-pending-rewards/index.ts b/src/strategies/gysr-pending-rewards/index.ts deleted file mode 100644 index 9b4d59b19..000000000 --- a/src/strategies/gysr-pending-rewards/index.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { multicall, Multicaller } from '../../utils'; - -export const author = 'mitesh-mutha'; -export const version = '0.1.0'; - -const poolInfoAddressForNetwork = { - 1: '0x01356d78c770840166C1654691D19Bd33C52EaAd', - 10: '0x3cAA041d1a0a78d141703C0E95408c1801Ed74dd', - 42: '0x91EB59690526b748FE1046D27BdB1B3dadeaf958', - 137: '0x53590f017d73bAb31A6CbCBF6500A66D92fecFbE' -}; - -const poolInfoABI = [ - 'function rewards(address pool, address addr) external view returns (uint256[])' -]; - -const poolABI = ['function rewardTokens() external view returns (address[])']; - -const tokenABI = ['function decimals() external view returns (uint8)']; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - // Network specific pool info contract address - const poolInfoContractAddress = poolInfoAddressForNetwork[network]; - - // Determine which token rewards to count for voting power - let tokenIndex = 0; - - const tokenCallResult = await multicall( - network, - provider, - poolABI, - [[options.pool, 'rewardTokens', []]], - { blockTag } - ); - - const rewardTokens = tokenCallResult[0].map((tokenAddress) => [ - tokenAddress.toString().toLowerCase() - ]); - - if (!(options.rewardToken == null)) { - tokenIndex = Math.max( - rewardTokens.indexOf(options.rewardToken.toLowerCase()), - 0 - ); - } - - // Determine decimals - const rewardTokenAddress = tokenCallResult[0][tokenIndex].toString(); - const decimalCallResult = await multicall( - network, - provider, - tokenABI, - [[rewardTokenAddress, 'decimals', []]], - { blockTag } - ); - const decimals = decimalCallResult[0]; - - // Get the pending rewards for addresses - const multi = new Multicaller(network, provider, poolInfoABI, { blockTag }); - addresses.forEach((address) => - multi.call(address, poolInfoContractAddress, 'rewards', [ - options.pool, - address - ]) - ); - const result: Record = await multi.execute(); - - return Object.fromEntries( - Object.entries(result).map(([address, balances]) => [ - address, - parseFloat(formatUnits(balances[tokenIndex], decimals)) - ]) - ); -} diff --git a/src/strategies/h2o/README.md b/src/strategies/h2o/README.md index 7fa052053..893c3bb4d 100644 --- a/src/strategies/h2o/README.md +++ b/src/strategies/h2o/README.md @@ -12,6 +12,6 @@ Here is an example of parameters: ```json { "symbol": "OCEAN", - "collateralTypeId": "OCEAN-A", + "collateralTypeId": "OCEAN-A" } ``` diff --git a/src/strategies/hashflow-governance-power/README.md b/src/strategies/hashflow-governance-power/README.md deleted file mode 100644 index 6ff23c17c..000000000 --- a/src/strategies/hashflow-governance-power/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# hashflow-governance-power - -This strategy is used for the Hashflow DAO to consider both held HFT and LP'd HFT equally. - -It was implemented as a result of community proposal [#272](https://gov.hashflow.com/t/allow-hashgang-to-use-their-hft-lp-tokens-to-vote/272/20), which was passed via a [snapshot vote](https://snapshot.org/#/hashflowdao.eth/proposal/0x0fe602a533b10fab93a66b155770ba948ff23209412487ec1ba3c5a4d75357ef). - -It allows users who are LP'ing HFT in public HFT pools to use those for voting. This eliminates the need to withdraw from pools to participate in voting. - -The exact implementation uses the fact that the `payout` field of a pool's `function assetDetails()` call shares how many HFT are claimable in the pool. This can then be combined with the totalSupply of each pool's LP token to determine the HFT value of funds deposited by any given address. - -Here is an example of parameters: - -```json -{ - "hftContract": "0xb3999f658c0391d94a37f7ff328f3fec942bcadc", - "hftPool": "0x5afe266ab4e43c32bad5459fe8df116dd5541222", - "hToken": "0x8a96b94bee6636042f2019c60c43b4f1c8c177a9" -} -``` diff --git a/src/strategies/hashflow-governance-power/examples.json b/src/strategies/hashflow-governance-power/examples.json deleted file mode 100644 index 9220e982e..000000000 --- a/src/strategies/hashflow-governance-power/examples.json +++ /dev/null @@ -1,68 +0,0 @@ -[ - { - "name": "Mainnet query", - "strategy": { - "name": "hashflow-governance-power", - "params": { - "hftContract": "0xb3999f658c0391d94a37f7ff328f3fec942bcadc", - "hftPool": "0x5afe266ab4e43c32bad5459fe8df116dd5541222", - "hToken": "0x8a96b94bee6636042f2019c60c43b4f1c8c177a9" - } - }, - "network": "1", - "addresses": [ - "0x5caf573a688f21bb7a73a5f1cbeac1dfd6b505ca", - "0x5f09807819ca23d2b55d418a1cda9758725b35c1", - "0xb719e6279c5a9b5aa2b7a47f9e0e6453fa405808", - "0xaac0643b2a9f8351113388d935ff0718dae07e56", - "0x5409630192a9adaaa9ffbd46a3c695386be04e9c", - "0xd36ebe8e232955a444b4ab9a9297549c99ff09a9", - "0xee6d0298496153a026678488673a39c6153ab7f6", - "0xcabe8da7ce365392970437c9f4edfb4c89560257", - "0x910c60c049d833a7fd2f5bda310bbea1c5fa3598", - "0x8b5ce16637225516e2af14962407ec5afb8b9b73", - "0x720d2dad9cf67988e5b09d1d0dc4ebe5843cd611", - "0x1bbae2d283f2c14f43147e53d4f1b69601a1eac3", - "0x1e377d026dbb28dff1662927b2cb836b7c9f06d3", - "0xd340c943ae137ea0bab682a286d1af65e4e39a6b", - "0x71311a0e6167efcf28eeab78f87c008d05760b0b", - "0xc4b86bd3d432bd0b55dcd0e4bd3db91e05de2c26" - ], - "snapshot": 16093501 - }, - { - "name": "BSC query", - "strategy": { - "name": "hashflow-governance-power", - "params": { - "hftContract": "0x44ec807ce2f4a6f2737a92e985f318d035883e47", - "hftPool": "0x7286f5be4e531f6ea7660c739108d95aa2fef959", - "hToken": "0x1ddc58822f0071bf6ab973c94a185a97d000d036" - } - }, - "network": "56", - "addresses": [ - "0x884d6fa3a4b349880486ad4d7c833ca968c785d8", - "0xc0b570dc0363b1d3c9714f1060e716696acd4034", - "0xf4a290d914592a7f482fccce09ad73762a5998ca", - "0xdb4d32ca46310b4079fef1d135c88c1d1def32d7", - "0xc479328e6254bf692a3b22f051245069b307647a", - "0x66633cc6b84cd127a0bb84864c1a4ea0172469a6", - "0x1e46348fba3e75fd3b609351adfc22612c20be54", - "0xf41939922947f7c93cfa77364b35c6e85e53aef3", - "0x34f0431bec239c0828fbcf96c32092e5c926df21", - "0x67bcd41adbf12d1ecffd86e17af2b14179b556a3", - "0xf6e1acdb0a18cb397dde6ec0cca024d875e3e6e2", - "0x4d37f2a705377ac4c0827b79a8a4b84267b03ea1", - "0x4e97283bac00e1be24474e0671a1457e55991d11", - "0x9448e95d67fbe5ee576eeddb2adc637bae141e10", - "0xbe7d87e410a5c880f7a8ee83d4ae9d3db26b0d84", - "0x9f9d3b1c793a367310ebe5d159c4599914abdbd5", - "0x7a43dc785ced8554478b2ba2e491347c0826adde", - "0x04d29d747aa0abda9144fc15d1546a1bb959c40a", - "0xbe18f84532d8f7fb6d7919401c0096f3e257db8b", - "0x672c3b982325be86e221fe5d0eb5391ffa9cc8fc" - ], - "snapshot": 23545695 - } -] diff --git a/src/strategies/hashflow-governance-power/index.ts b/src/strategies/hashflow-governance-power/index.ts deleted file mode 100644 index cc53c4be7..000000000 --- a/src/strategies/hashflow-governance-power/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { getAddress } from '@ethersproject/address'; -import { BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { multicall, Multicaller } from '../../utils'; - -export const author = 'mib-hashflow'; -export const version = '0.1.1'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function assetDetails(address token) external view returns (uint128 withdrawalLimit, uint128 cap, uint128 netPayout, uint128 timestamp, address hToken, address hTokenXChain, bool listed)', - 'function totalSupply() external view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const contractData = await multicall( - network, - provider, - abi, - [ - [options.hftPool, 'assetDetails', [options.hftContract]], - [options.hToken, 'totalSupply', []] - ], - { blockTag } - ); - - const netPayout: BigNumberish = contractData[0].netPayout; - const totalSupply: BigNumberish = contractData[1][0]; - - const lpTokenWeight = - parseFloat(formatUnits(netPayout, 18)) / - parseFloat(formatUnits(totalSupply, 18)); - - const hftMulti = new Multicaller(network, provider, abi, { blockTag }); - addresses.forEach((address) => - hftMulti.call(address, options.hftContract, 'balanceOf', [address]) - ); - const hftBalances: Record = await hftMulti.execute(); - - const lpMulti = new Multicaller(network, provider, abi, { blockTag }); - addresses.forEach((address) => - lpMulti.call(address, options.hToken, 'balanceOf', [address]) - ); - const lpBalances: Record = await lpMulti.execute(); - - return Object.fromEntries( - Object.entries(hftBalances).map(([address, balance]) => [ - getAddress(address), - parseFloat(formatUnits(balance, 18)) + - lpTokenWeight * parseFloat(formatUnits(lpBalances[address], 18)) - ]) - ); -} diff --git a/src/strategies/hashflow-governance-power/schema.json b/src/strategies/hashflow-governance-power/schema.json deleted file mode 100644 index 6e5b10b51..000000000 --- a/src/strategies/hashflow-governance-power/schema.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "Strategy", - "type": "object", - "properties": { - "hftContract": { - "type": "string", - "title": "HFT Contract address", - "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "hftPool": { - "type": "string", - "title": "HFT Pool address", - "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "hToken": { - "type": "string", - "title": "HFT Pool HFT-hToken address", - "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - } - }, - "required": ["hftContract", "hftPool", "hToken"], - "additionalProperties": false - } - } -} diff --git a/src/strategies/hats-protocol-hat-id/README.md b/src/strategies/hats-protocol-hat-id/README.md new file mode 100644 index 000000000..6fc08b89e --- /dev/null +++ b/src/strategies/hats-protocol-hat-id/README.md @@ -0,0 +1,30 @@ +# hats-protocol-hat-id + +Grants voting power based on if voter has a specific hat (IP based). + +Here is an example of parameters: + +```json +{ + "address": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", + "hatId": "68" +} +``` + +or + +```json +{ + "address": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", + "hatId": "68.1" +} +``` + +or + +```json +{ + "address": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", + "hatId": "68.1.1" +} +``` diff --git a/src/strategies/hats-protocol-hat-id/examples.json b/src/strategies/hats-protocol-hat-id/examples.json new file mode 100644 index 000000000..e165ad767 --- /dev/null +++ b/src/strategies/hats-protocol-hat-id/examples.json @@ -0,0 +1,21 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "hats-protocol-hat-id", + "params": { + "address": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", + "hatId": "68.1.1.1" + } + }, + "network": "5", + "addresses": [ + "0xc4f6578c24c599F195c0758aD3D4861758d703A3", + "0xa6aF0566EF4eF7E8f38913f69d4e55c06F00A5aC", + "0x00e7332F9Cd4C05a0645AC959Fb1Be60ec24F94f", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x4D6Ed22Ed0850384622EF129932aE29D27a89eD3" + ], + "snapshot": 10015682 + } +] diff --git a/src/strategies/hats-protocol-hat-id/index.ts b/src/strategies/hats-protocol-hat-id/index.ts new file mode 100644 index 000000000..8943aba8a --- /dev/null +++ b/src/strategies/hats-protocol-hat-id/index.ts @@ -0,0 +1,76 @@ +import { BigNumberish, BigNumber } from '@ethersproject/bignumber'; +import { getAddress } from '@ethersproject/address'; +import { Multicaller } from '../../utils'; + +export const author = 'hotmanics'; +export const version = '1.0.0'; + +const abi = [ + 'function isWearerOfHat(address _user, uint256 _hatId) view returns (bool isWearer)' +]; + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +): Promise> { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + const hatId = BigNumber.from(HatIpToHex(options.hatId)); + + const multi = new Multicaller(network, provider, abi, { blockTag }); + addresses.forEach((address) => + multi.call(address, options.address, 'isWearerOfHat', [address, hatId]) + ); + const result: Record = await multi.execute(); + + const res = Object.fromEntries( + Object.entries(result).map(([address, isWearer]) => [ + getAddress(address), + isWearer ? 1 : 0 + ]) + ); + + return res; +} + +function HatIpToHex(hatIp) { + let observedChunk = hatIp; + + const sections: number[] = []; + + while (true) { + if (observedChunk.indexOf('.') === -1) { + const section = observedChunk.substring(0, observedChunk.length); + sections.push(Number(section)); + break; + } + + const section = observedChunk.substring(0, observedChunk.indexOf('.')); + observedChunk = observedChunk.substring( + observedChunk.indexOf('.') + 1, + observedChunk.length + ); + + sections.push(Number(section)); + } + + let constructedResult = '0x'; + + for (let i = 0; i < sections.length; i++) { + const hex = sections[i].toString(16); + + if (i === 0) { + constructedResult += hex.padStart(8, '0'); + } else { + constructedResult += hex.padStart(4, '0'); + } + } + + constructedResult = constructedResult.padEnd(66, '0'); + + return constructedResult; +} diff --git a/src/strategies/solv-voucher-claimable/schema.json b/src/strategies/hats-protocol-hat-id/schema.json similarity index 71% rename from src/strategies/solv-voucher-claimable/schema.json rename to src/strategies/hats-protocol-hat-id/schema.json index 7d01484de..1e21753bd 100644 --- a/src/strategies/solv-voucher-claimable/schema.json +++ b/src/strategies/hats-protocol-hat-id/schema.json @@ -6,22 +6,21 @@ "title": "Strategy", "type": "object", "properties": { - "symbol": { - "type": "string", - "title": "Underlying Token Symbol", - "examples": ["e.g. UNI"], - "maxLength": 16 - }, "address": { "type": "string", - "title": "Vesting Voucher Contract address", + "title": "Contract address", "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], "pattern": "^0x[a-fA-F0-9]{40}$", "minLength": 42, "maxLength": 42 + }, + "hatId": { + "type": "string", + "title": "Hat Id", + "examples": ["e.g. 68, 68.1, 68.1.1, 68.1.1.1 etc"] } }, - "required": ["address"], + "required": ["address", "hatId"], "additionalProperties": false } } diff --git a/src/strategies/hats-protocol-hat-ids/README.md b/src/strategies/hats-protocol-hat-ids/README.md new file mode 100644 index 000000000..5b6a324fc --- /dev/null +++ b/src/strategies/hats-protocol-hat-ids/README.md @@ -0,0 +1,30 @@ +# hats-protocol-hat-ids + +Grants voting power based on if voter has a set of specified hat (IP based). + +Here is an example of parameters: + +```json +{ + "address": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", + "hatIds": "[\"68\"]" +} +``` + +or + +```json +{ + "address": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", + "hatIds": "[\"68\", \"68.1\"]" +} +``` + +or + +```json +{ + "address": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", + "hatIds": "[\"68\", \"68.1\", \"68.1.1\"]" +} +``` diff --git a/src/strategies/hats-protocol-hat-ids/examples.json b/src/strategies/hats-protocol-hat-ids/examples.json new file mode 100644 index 000000000..c316ce14f --- /dev/null +++ b/src/strategies/hats-protocol-hat-ids/examples.json @@ -0,0 +1,21 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "hats-protocol-hat-ids", + "params": { + "address": "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137", + "hatIds": ["68", "68.1", "68.1.1", "68.1.1.1"] + } + }, + "network": "5", + "addresses": [ + "0xc4f6578c24c599F195c0758aD3D4861758d703A3", + "0xa6aF0566EF4eF7E8f38913f69d4e55c06F00A5aC", + "0x00e7332F9Cd4C05a0645AC959Fb1Be60ec24F94f", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x4D6Ed22Ed0850384622EF129932aE29D27a89eD3" + ], + "snapshot": 10015682 + } +] diff --git a/src/strategies/hats-protocol-hat-ids/index.ts b/src/strategies/hats-protocol-hat-ids/index.ts new file mode 100644 index 000000000..7491141d8 --- /dev/null +++ b/src/strategies/hats-protocol-hat-ids/index.ts @@ -0,0 +1,86 @@ +import { BigNumber } from '@ethersproject/bignumber'; +import { getAddress } from '@ethersproject/address'; +import { Multicaller } from '../../utils'; + +export const author = 'hotmanics'; +export const version = '1.0.0'; + +const abi = [ + 'function isWearerOfHat(address _user, uint256 _hatId) external view returns (bool isWearer)' +]; + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +): Promise> { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + const hatIds = options.hatIds.map((hatId) => + BigNumber.from(HatIpToHex(hatId)) + ); + + const multi = new Multicaller(network, provider, abi, { blockTag }); + addresses.forEach((address) => { + hatIds.forEach((hatId) => { + multi.call(`${address}.${hatId}`, options.address, 'isWearerOfHat', [ + address, + hatId + ]); + }); + }); + + const multicallResult: Record< + string, + Record + > = await multi.execute(); + + const res = Object.fromEntries( + Object.entries(multicallResult).map(([address, isWearerPerHat]) => [ + getAddress(address), + Object.values(isWearerPerHat).includes(true) ? 1 : 0 + ]) + ); + + return res; +} + +function HatIpToHex(hatIp) { + let observedChunk = hatIp; + + const sections: number[] = []; + + while (true) { + if (observedChunk.indexOf('.') === -1) { + const section = observedChunk.substring(0, observedChunk.length); + sections.push(Number(section)); + break; + } + + const section = observedChunk.substring(0, observedChunk.indexOf('.')); + observedChunk = observedChunk.substring( + observedChunk.indexOf('.') + 1, + observedChunk.length + ); + + sections.push(Number(section)); + } + + let constructedResult = '0x'; + + for (let i = 0; i < sections.length; i++) { + const hex = sections[i].toString(16); + + if (i === 0) { + constructedResult += hex.padStart(8, '0'); + } else { + constructedResult += hex.padStart(4, '0'); + } + } + + constructedResult = constructedResult.padEnd(66, '0'); + return constructedResult; +} diff --git a/src/strategies/riskharbor-underwriter/schema.json b/src/strategies/hats-protocol-hat-ids/schema.json similarity index 51% rename from src/strategies/riskharbor-underwriter/schema.json rename to src/strategies/hats-protocol-hat-ids/schema.json index 78f56d803..fe8c6a7f7 100644 --- a/src/strategies/riskharbor-underwriter/schema.json +++ b/src/strategies/hats-protocol-hat-ids/schema.json @@ -6,23 +6,24 @@ "title": "Strategy", "type": "object", "properties": { - "SUBGRAPH_URL": { + "address": { "type": "string", - "title": "Subgraph url", - "examples": [ - "https://api.thegraph.com/subgraphs/name/some-protocol/v1-protocol" - ] - }, - "VAULT_ADDR": { - "type": "string", - "title": "Vault address", - "examples": ["e.g. 0xbcA81A2118982182d897845571BE950aE94C619c"], + "title": "Contract address", + "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], "pattern": "^0x[a-fA-F0-9]{40}$", "minLength": 42, "maxLength": 42 + }, + "hatIds": { + "type": "array", + "title": "Hat Ids", + "examples": ["e.g. [\"68\", \"68.1\", \"68.1.1\", \"68.1.1.1\""], + "items": { + "type": "string" + } } }, - "required": ["SUBGRAPH_URL", "VAULT_ADDR"], + "required": ["address", "hatIds"], "additionalProperties": false } } diff --git a/src/strategies/hedgey-multi/README.md b/src/strategies/hedgey-multi/README.md deleted file mode 100644 index 42f2440e4..000000000 --- a/src/strategies/hedgey-multi/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Hedgey strategy - -Calculates voting rights based on the underlying tokens locked in the Hedgey protocol from multiple contracts with a multiplyer - -## Examples - -### Input parameters - -An array of ContractDetails - - - address: The address of the contract - - token: The address of the token that is taken into account for the score - - decimal: The decimal value the token uses - - contractType: Can be NFT for the standard NFT contract or TokenInfusedNFT for the token infused NFT contract - - lockedTokenMultiplier: a simple multiplyer for locked tokens - - lockedTokenMonthlyMultiplier: a multiplier based on the amount of time the tokens will be unlocked for diff --git a/src/strategies/hedgey-multi/examples.json b/src/strategies/hedgey-multi/examples.json deleted file mode 100644 index fca088df0..000000000 --- a/src/strategies/hedgey-multi/examples.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "name": "Example voting based on hedgey token underlying balance from multiple contracts with a multiplyer", - "strategy": { - "name": "hedgey-multi", - "params": { - "contracts": [ - { - "address": "0x4bc8ea84bdc3ebb01d495e5d1605d4f082aeb5d7", - "token": "0xccDE1F2B71573a865d3896eb019C41Bf4BF92e65", - "decimal": 18, - "contractType": "NFT", - "lockedTokenMultiplier": 2, - "lockedTokenMonthlyMultiplier": { - "default": 1, - "1": 1.5, - "2": 2 - } - } - ] - } - }, - "network": "4", - "snapshot": 11452455, - "addresses": ["0x92d9802eFcD0485876DDC13c16cEA67e6aD5EB35"] - } -] diff --git a/src/strategies/hedgey-multi/index.ts b/src/strategies/hedgey-multi/index.ts deleted file mode 100644 index 5e6867ba4..000000000 --- a/src/strategies/hedgey-multi/index.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { Multicaller } from '../../utils'; - -export const author = 'bark4mark'; -export const version = '0.1.0'; - -const MAX_CONTRACTS = 8; - -enum ContractType { - NFT = 'NFT', - TokenInfusedNFT = 'TokenInfusedNFT' -} - -type ContractDetails = { - address: string; - token: string; - decimal: number; - contractType: ContractType; - lockedTokenMultiplier: number; - lockedTokenMonthlyMultiplier: any; -}; - -const abis = { - NFT: [ - 'function balanceOf(address owner) external view returns (uint256 balance)', - 'function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId)', - 'function futures(uint256 index) external view returns (uint256 amount, address token, uint256 unlockDate)', - 'function token() external view returns (address token)' - ], - TokenInfusedNFT: [ - 'function futures(uint256 index) external view returns (uint256 amount, uint256 unlockDate)' - ] -}; - -const compareAddresses = (address1: string, address2: string): boolean => { - if (!address1 || !address2) return false; - return address1.toLowerCase() === address2.toLowerCase(); -}; - -const getMonthDifference = (start: Date, end: Date): number => { - return ( - end.getMonth() - - start.getMonth() + - 12 * (end.getFullYear() - start.getFullYear()) - ); -}; - -export async function strategy( - _space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const contractDetails: ContractDetails[] = options.contracts; - if (contractDetails.length > MAX_CONTRACTS) { - throw new Error(`Max number (${MAX_CONTRACTS}) of contracts exceeded`); - } - const balanceOfMulti = new Multicaller(network, provider, abis.NFT, { - blockTag - }); - - contractDetails.forEach((contractDetail) => { - addresses.forEach((address: string) => { - balanceOfMulti.call( - `${contractDetail.address}/${address}`, - contractDetail.address, - 'balanceOf', - [address] - ); - }); - if (contractDetail.contractType === ContractType.TokenInfusedNFT) { - balanceOfMulti.call( - `${contractDetail.address}/token`, - contractDetail.address, - 'token' - ); - } - }); - - const balanceOfResult = await balanceOfMulti.execute(); - const nftHolderMulti = new Multicaller(network, provider, abis.NFT, { - blockTag - }); - - contractDetails.forEach((contractDetail) => { - addresses.forEach((address: string) => { - const balance = balanceOfResult[`${contractDetail.address}/${address}`]; - for (let index = 0; index < balance; index++) { - nftHolderMulti.call( - `${contractDetail.contractType}/${contractDetail.address}/${address}/${index}`, - contractDetail.address, - 'tokenOfOwnerByIndex', - [address, index] - ); - } - }); - }); - - const nftHolders = await nftHolderMulti.execute(); - - const nftDealsMulti = new Multicaller(network, provider, abis.NFT, { - blockTag - }); - - const tiNFTDealsMulti = new Multicaller( - network, - provider, - abis.TokenInfusedNFT, - { blockTag } - ); - - for (const [path, nftId] of Object.entries(nftHolders)) { - const [contractType, contractAddress, address] = path.split('/'); - switch (contractType) { - case ContractType.NFT: - nftDealsMulti.call( - `${contractAddress}/${address}`, - contractAddress, - 'futures', - [nftId] - ); - break; - case ContractType.TokenInfusedNFT: - tiNFTDealsMulti.call( - `${contractAddress}/${address}`, - contractAddress, - 'futures', - [nftId] - ); - break; - } - } - - const nftDeals = await nftDealsMulti.execute(); - const tiNFTDeals = await tiNFTDealsMulti.execute(); - - const votes = {}; - - for (const [path, deal] of Object.entries(nftDeals)) { - const [contractAddress, address] = path.split('/'); - const contractDetail = contractDetails.find( - (element) => element.address === contractAddress - ); - - if (!contractDetail) continue; - if (!compareAddresses(deal.token, contractDetail.token)) continue; - - const amount = BigNumber.from(deal.amount).div( - BigNumber.from(10).pow(contractDetail.decimal) - ); - - let score = amount.toNumber(); - score = score * contractDetail.lockedTokenMultiplier; - const durationStart = new Date(); - const unlockDate = new Date(deal.unlockDate * 1000); - - const months = getMonthDifference(durationStart, unlockDate); - let monthlyMultiplier = contractDetail.lockedTokenMonthlyMultiplier[months]; - - if (!monthlyMultiplier) - monthlyMultiplier = - contractDetail.lockedTokenMonthlyMultiplier['default']; - - score = score * monthlyMultiplier; - - let existingAmount = votes[address]; - if (existingAmount) { - existingAmount = existingAmount + score; - } else { - votes[address] = score; - } - } - - for (const [path, deal] of Object.entries(tiNFTDeals)) { - const [contractAddress, address] = path.split('/'); - const contractDetail = contractDetails.find( - (element) => element.address === contractAddress - ); - - if (!contractDetail) continue; - - const contractToken = balanceOfResult[`${contractDetail.address}/token`]; - if (!compareAddresses(contractToken, contractDetail.token)) continue; - - const amount = BigNumber.from(deal.amount).div( - BigNumber.from(10).pow(contractDetail.decimal) - ); - - let score = amount.toNumber(); - score = score * contractDetail.lockedTokenMultiplier; - const durationStart = new Date(); - const unlockDate = new Date(deal.unlockDate * 1000); - - const months = getMonthDifference(durationStart, unlockDate); - if (months > 1) { - let monthlyMultiplier = - contractDetail.lockedTokenMonthlyMultiplier[months]; - - if (!monthlyMultiplier) - monthlyMultiplier = - contractDetail.lockedTokenMonthlyMultiplier['default']; - - score = score * monthlyMultiplier; - } - let existingAmount = votes[address]; - if (existingAmount) { - existingAmount = existingAmount + score; - } else { - votes[address] = score; - } - } - - return votes; -} diff --git a/src/strategies/hopr-stake-and-balance-qv/README.md b/src/strategies/hopr-stake-and-balance-qv/README.md index f66c4d948..e64db9ac9 100644 --- a/src/strategies/hopr-stake-and-balance-qv/README.md +++ b/src/strategies/hopr-stake-and-balance-qv/README.md @@ -1,31 +1,39 @@ # HOPR Stake and Balance QV This `hopr-stake-and-balance-qv` strategy calculates voting power with: -`(B1 + B2 + B3 + S1 + S2)^0.5` +`(B1 + B2 + sum((B_i + S_i) * F_i)) ^ exponent` where: -- B1: balance of HOPR token on mainnet -- B2: balance of HOPR token on Gnosis chain (xHOPR) -- B3: balance of wrapped HOPR token on Gnosis chain (wxHOPR) -- S1: amount of xHOPR token staked into the latest staking season -- S2: amount of wxHOPR token unclaimed from the latest staking season +- B1: HOPR token balance in the voting account on the mainnet +- B2: xHOPR token & wxHOPR token balance in the voting account on the Gnosis chain +- B_i: xHOPR token & wxHOPR token balance in the "Staking Safe", where the voting account is an owner, on the Gnosis chain +- S_i: wxHOPR token staked in outgoing HOPR Channels by HOPR nodes that are managed by the "Staking Safe", where the voting account is an owner, on the Gnosis chain +- F_i: Share of the voting account in the "Staking Safe", where the voting account is an owner, on the Gnosis chain +- exponent: Quadratic Voting-like exponent value. E.g., for quadratic-voting, the exponent is 0.5. This value can be set by the community to any value between 0 and 1, inclusive. Currently it is set at 0.75. + ## Parameters - "tokenAddress": Contract address of HOPR token on mainnet. Value should be `"0xf5581dfefd8fb0e4aec526be659cfab1f8c781da"` - "symbol": Token Symbol. Value should be `"HOPR"`. -- "season": Number of the ongoing season. E.g. `7`. - "fallbackGnosisBlock": Fallback block number on Gnosis chain, in case Gnosis block number cannot be translated from Ethereum mainnet due to subgraph issues. E.g. `27852687`, - "subgraphStudioProdQueryApiKey": Production decentralized subgraph studio query API key. If no key can be provided, use `null`. - "subgraphStudioDevAccountId": Development subgraph studio account ID. Note that this ID should not be exposed normally. If unknown, use `null`. - "subgraphHostedAccountName": Legacy hosted subgraph account name. Value is `"hoprnet"`. -- "useStake": If the staking program should be considered. If `false`, `S1 + S2 === 0`. Value should be set to `true`. -- "useHoprOnGnosis": If tokens on Gnosis chain should be considered. If `false`, `B2 + B3 === 0`. Value should be set to `true`. -- "useHoprOnMainnet": If tokens on Ethereum mainnet should be considered. If `false`, `B1 === 0`. Value should be set to `true`. -- "subgraphStudioProdAllSeasonQueryId": Production stake all season subgraph ID. Value is `"DrkbaCvNGVcNH1RghepLRy6NSHFi8Dmwp4T2LN3LqcjY"`. -- "subgraphStudioDevAllSeasonVersion": Latest development version of the stake all season subgraph. E.g. `"v0.0.9"` -- "subgraphStudioDevAllSeasonSubgraphName": Name of the staking subgraph in Graph Studio. Value should be `"hopr-stake-all-seasons"`. -- "subgraphHostedAllSeasonSubgraphName": Name of the staking subgraph in Graph Hosted service. Value should be `"hopr-stake-all-seasons"`. -- "subgraphStudioProdHoprOnGnosisQueryId": Latest development version of the HOPR token balances on Gnosis subgraph. Value should be `"njToE7kpetd3P9sJdYQPSq6yQjBs7w9DahQpBj6WAoD"`. +- "useSafeStake": If "Safe Staking" should be considered. If `false`, `S_i === 0` and `F_i === 0`. This value should be set to `true`, +- "useChannelStake": If tokens staked in outgoing channels should be considered. If `false`, `S_i === 0`. This value should be set to `true`, +- "useHoprOnGnosis": If tokens on Gnosis chain should be considered. If `false`, `B2 === 0` and `B_i === 0`. This value should be set to `true`. +- "useHoprOnMainnet": If tokens on Ethereum mainnet should be considered. If `false`, `B1 === 0`. This value should be set to `true`. + +- "subgraphStudioProdSafeStakeQueryId": ID of the "safe stake" subgraph in production. E.g. "DrkbaCvNGVcNH1RghepLRy6NSHFi8Dmwp4T2LN3LqcjY". +- "subgraphStudioDevSafeStakeSubgraphName": Name of the safe stake subgraph in Graph Studio. E.g. "hopr-nodes-dufour". +- "subgraphStudioDevSafeStakeVersion": Latest development version of the safe stake subgraph. E.g. "latest". +- "subgraphHostedSafeStakeSubgraphName": Name of the safe stake subgraph in Graph Hosted service. This servie does not exist, so the value should be `null`. +- "subgraphStudioProdChannelsQueryId": ID of the "channels" subgraph in production. E.g. "Feg6Jero3aQzesVYuqk253NNLyNAZZppbDPKFYEGJ1Hj". +- "subgraphStudioDevChannelsSubgraphName": Name of the channels subgraph in Graph Studio. E.g. "hopr-channels". +- "subgraphStudioDevChannelsVersion": Latest development version of the channels subgraph. E.g. "latest". +- "subgraphHostedChannelsSubgraphName": Name of the channels subgraph in Graph Hosted service. This servie does not exist, so the value should be `null`. +- "subgraphStudioProdHoprOnGnosisQueryId": ID of the HOPR token balances on Gnosis subgraph in production. Value should be `"njToE7kpetd3P9sJdYQPSq6yQjBs7w9DahQpBj6WAoD"`. - "subgraphStudioDevHoprOnGnosisSubgraphName": Name of the HOPR token balances on Gnosis subgraph in Graph Studio. Value should be "hopr-on-gnosis"` - "subgraphStudioDevHoprOnGnosisVersion": Latest development version of the HOPR token balances on Gnosis subgraph. E.g. "v0.0.2"` - "subgraphHostedHoprOnGnosisSubgraphName": Name of the HOPR token balances on Gnosis in Graph Hosted service. Value should be `"hopr-on-xdai"` +- "exponent": Quadratic Voting-like exponent value. E.g., for quadratic-voting, the exponent is 0.5. This value can be set by the community to any value between 0 and 1, inclusive. Currently it is set at `"0.75"`. diff --git a/src/strategies/hopr-stake-and-balance-qv/examples.json b/src/strategies/hopr-stake-and-balance-qv/examples.json index d56e72a4e..c04b7e2c7 100644 --- a/src/strategies/hopr-stake-and-balance-qv/examples.json +++ b/src/strategies/hopr-stake-and-balance-qv/examples.json @@ -6,22 +6,27 @@ "params": { "tokenAddress": "0xf5581dfefd8fb0e4aec526be659cfab1f8c781da", "symbol": "HOPR", - "season": 7, "fallbackGnosisBlock": 27852687, "subgraphStudioProdQueryApiKey": null, "subgraphStudioDevAccountId": null, "subgraphHostedAccountName": "hoprnet", - "useStake": true, + "useSafeStake": true, + "useChannelStake": true, "useHoprOnGnosis": true, "useHoprOnMainnet": true, - "subgraphStudioProdAllSeasonQueryId": "DrkbaCvNGVcNH1RghepLRy6NSHFi8Dmwp4T2LN3LqcjY", - "subgraphStudioDevAllSeasonVersion": "v0.0.9", - "subgraphStudioDevAllSeasonSubgraphName": "hopr-stake-all-seasons", - "subgraphHostedAllSeasonSubgraphName": "hopr-stake-all-seasons", + "subgraphStudioProdSafeStakeQueryId": "DrkbaCvNGVcNH1RghepLRy6NSHFi8Dmwp4T2LN3LqcjY", + "subgraphStudioDevSafeStakeSubgraphName": "hopr-nodes-dufour", + "subgraphStudioDevSafeStakeVersion": "latest", + "subgraphHostedSafeStakeSubgraphName": null, + "subgraphStudioProdChannelsQueryId": "Feg6Jero3aQzesVYuqk253NNLyNAZZppbDPKFYEGJ1Hj", + "subgraphStudioDevChannelsSubgraphName": "hopr-channels", + "subgraphStudioDevChannelsVersion": "latest", + "subgraphHostedChannelsSubgraphName": null, "subgraphStudioProdHoprOnGnosisQueryId": "njToE7kpetd3P9sJdYQPSq6yQjBs7w9DahQpBj6WAoD", "subgraphStudioDevHoprOnGnosisSubgraphName": "hopr-on-gnosis", "subgraphStudioDevHoprOnGnosisVersion": "v0.0.2", - "subgraphHostedHoprOnGnosisSubgraphName": "hopr-on-xdai" + "subgraphHostedHoprOnGnosisSubgraphName": "hopr-on-xdai", + "exponent": "0.75" } }, "network": "1", diff --git a/src/strategies/hopr-stake-and-balance-qv/index.ts b/src/strategies/hopr-stake-and-balance-qv/index.ts index d5e46b602..c8753b13f 100644 --- a/src/strategies/hopr-stake-and-balance-qv/index.ts +++ b/src/strategies/hopr-stake-and-balance-qv/index.ts @@ -1,250 +1,71 @@ import { formatUnits, parseUnits } from '@ethersproject/units'; import { BigNumber } from '@ethersproject/bignumber'; -import { multicall, subgraphRequest } from '../../utils'; +import { multicall } from '../../utils'; +import { + getGnosisBlockNumber, + getHostedSubgraphUrl, + getStudioDevSubgraphUrl, + getStudioProdSubgraphUrl, + hoprNodeStakeOnChannelsSubgraphQuery, + hoprTotalOnGnosisSubgraphQuery, + safeStakeSubgraphQuery, + trimArray +} from './utils'; /** - * @dev Calculate score based on Quadratic Voting system.Token balance comes from - * - Mainnet HOPR token balance, read from multicall - * - Gnosis chain, HOPR token balance, read from subgraph (xHOPR balance and wxHOPR balance) and multicall (mainnet HOPR balance) - * - Gnosis chain. HOPR token staked into the most recent stake season, read from subgraph. + * @dev Calculate score based on Quadratic Voting-like system. + * Votes should be casted by the admin (owner) account of SafeStake. + * Token balance comes from + * - the voter account: + * - Mainnet HOPR token balance, read from multicall + * - Gnosis chain, HOPR token balance, read from subgraph (xHOPR balance and wxHOPR balance) and multicall (mainnet HOPR balance) + * - safes created by the "HoprSafeStakeFactory" contract, where the voter account is an owner. Voting account's share of the safe: + * - Gnosis chain. Safe's HOPR token balance, read from subgraph (xHOPR balance and wxHOPR balance) and multicall (mainnet HOPR balance) + * - Gnosis chain. Safe's HOPR token staked into the production HoprChannels, read from subgraph. */ export const author = 'QYuQianchen'; -export const version = '0.1.0'; +export const version = '0.2.0'; +/* + ****************************************** + *************** PARAMETERS *************** + ****************************************** + */ const XDAI_BLOCK_HOSTED_SUBGRAPH_URL = - 'https://api.thegraph.com/subgraphs/name/1hive/xdai-blocks'; -const QUERY_LIMIT = 1000; // 1000 addresses per query in Subgraph + 'https://api.thegraph.com/subgraphs/name/1hive/xdai-blocks'; // convert mainnet block to its corresponding block on Gnosis chain const tokenAbi = ['function balanceOf(address) view returns (uint256)']; // get mainnet HOPR token balance -// const DEFAULT_HOPR_STAKING_ALL_SEASONS_PROD_SUBGRAPH_ID = 'DrkbaCvNGVcNH1RghepLRy6NSHFi8Dmwp4T2LN3LqcjY'; -// const DEFAULT_HOPR_ON_GNOSIS_PROD_SUBGRAPH_ID = 'njToE7kpetd3P9sJdYQPSq6yQjBs7w9DahQpBj6WAoD'; const DEFAULT_HOPR_HOSTED_ACCOUNT_NAME = 'hoprnet'; -const DEFAULT_HOPR_STAKING_ALL_SEASONS_HOSTED_SUBGRAPH_NAME = - 'hopr-stake-all-seasons'; -const DEFAULT_HOPR_BALANCE_ON_GNOSIS_HOSTED_SUBGRAPH_NAME = 'hopr-on-xdai'; - -function getStudioProdSubgraphUrl( - apiKey: string | null | undefined, - subgraphId: string -): string | null { - return !apiKey - ? null - : `https://gateway.thegraph.com/api/${apiKey}/subgraphs/id/${subgraphId}`; -} -function getStudioDevSubgraphUrl( - accountStudioId: string | null | undefined, - subgraphName: string, - version: string -): string | null { - return !accountStudioId - ? null - : `https://api.studio.thegraph.com/query/${accountStudioId}/${subgraphName}/${version}`; -} -function getHostedSubgraphUrl( - accountName: string, - subgraphName: string -): string { - return `https://api.thegraph.com/subgraphs/name/${accountName}/${subgraphName}`; -} +const DEFAULT_FACTOR = 0.75; // Quadratic-voting-like factor -/** - * Try to query subgraphs from three differnt endpoints (hosted service, studio for development, studio in production), if applicable - * @param hostedSubgraphUrl hosted subgrpah url - * @param stuidoDevSubgraphUrl development url foro studio subgraph - * @param studioProdSubgraphUrl production url foro studio subgraph - * @param builtQuery query object - * @returns null or an object of summed token balance per address +/* + ******************************************** + **************** CALCULATION *************** + ******************************************** */ -async function subgraphRequestsToVariousServices( - hostedSubgraphUrl: string, - stuidoDevSubgraphUrl: string | null, - studioProdSubgraphUrl: string | null, - builtQuery: any -): Promise { - try { - // first try with hosted service - return subgraphRequest(hostedSubgraphUrl, builtQuery); - } catch (error) { - // console.log('Failed to get data from hostedSubgraphUrl'); - } - - // then try with studio dev service - if (stuidoDevSubgraphUrl) { - try { - return subgraphRequest(stuidoDevSubgraphUrl, builtQuery); - } catch (error) { - // console.log('Failed to get data from stuidoDevSubgraphUrl'); - } - } - - // then try with studio prod service - if (studioProdSubgraphUrl) { - try { - return subgraphRequest(studioProdSubgraphUrl, builtQuery); - } catch (error) { - // console.log('Failed to get data from studioProdSubgraphUrl'); - } - } - return null; -} - -/** - * Get block number from Gnosis chain at a given timestamp. - * The timestamp of the returned block should be no-bigger than the desired timestamp - * @param timestamp number of timestamp - * @param fallbackBlockNumber fallback block number on Gnosis chain, in case no result gets returned. - * @returns a number - */ -async function getGnosisBlockNumber( - timestamp: number, - fallbackBlockNumber: number -): Promise { - const query = { - blocks: { - __args: { - first: 1, - orderBy: 'number', - orderDirection: 'desc', - where: { - timestamp_lte: timestamp - } - }, - number: true, - timestamp: true - } - }; - - // query from subgraph - const data = await subgraphRequestsToVariousServices( - XDAI_BLOCK_HOSTED_SUBGRAPH_URL, - null, - null, - query - ); - return !data ? fallbackBlockNumber : Number(data.blocks[0].number); -} - -async function stakingSubgraphQuery( - hostedSubgraphUrl: string, - stuidoDevSubgraphUrl: string | null, - studioProdSubgraphUrl: string | null, - seasonNumber: string, - addresses: string[], - blockNumber: number, - snapshot: number | string -): Promise<{ [propName: string]: BigNumber }> { - const query = { - stakingParticipations: { - __args: { - first: QUERY_LIMIT, - where: { - account_: { - id_in: addresses.map((adr) => adr.toLowerCase()) - }, - stakingSeason_: { - seasonNumber - } - } - }, - account: { - id: true - }, - actualLockedTokenAmount: true, - airdropLockedTokenAmount: true, - unclaimedRewards: true, - virtualLockedTokenAmount: true - } - }; - - if (snapshot !== 'latest') { - // @ts-ignore - query.stakingParticipations.__args.block = { number: blockNumber }; - } - - // query from subgraph - const data = await subgraphRequestsToVariousServices( - hostedSubgraphUrl, - stuidoDevSubgraphUrl, - studioProdSubgraphUrl, - query - ); - - // map result (data.accounts) to addresses - const entries = !data - ? addresses.map((addr) => [addr, BigNumber.from('0')]) - : data.stakingParticipations.map((d) => [ - d.account.id, - BigNumber.from(d.actualLockedTokenAmount) - .add(BigNumber.from(d.airdropLockedTokenAmount)) - .add(BigNumber.from(d.virtualLockedTokenAmount)) - .add(BigNumber.from(d.unclaimedRewards)) - ]); - return Object.fromEntries(entries); -} - -async function hoprTotalOnGnosisSubgraphQuery( - hostedSubgraphUrl: string, - stuidoDevSubgraphUrl: string | null, - studioProdSubgraphUrl: string | null, - addresses: string[], - blockNumber: number, - snapshot: number | string -): Promise<{ [propName: string]: BigNumber }> { - const query = { - accounts: { - __args: { - first: QUERY_LIMIT, - where: { - id_in: addresses.map((adr) => adr.toLowerCase()) - } - }, - id: true, - totalBalance: true - } - }; - - if (snapshot !== 'latest') { - // @ts-ignore - query.accounts.__args.block = { number: blockNumber }; - } - - // query from subgraph - const data = await subgraphRequestsToVariousServices( - hostedSubgraphUrl, - stuidoDevSubgraphUrl, - studioProdSubgraphUrl, - query - ); - - // map result (data.accounts) to addresses - const entries = !data - ? addresses.map((addr) => [addr, BigNumber.from('0')]) - : data.accounts.map((d) => [ - d.id, - parseUnits(d.totalBalance.toString(), 18) - ]); - return Object.fromEntries(entries); -} - /** * Calculate the final score - * @param shouldIncludeMainnetValue if the mainnet token balance should be taken into account - * @param subgraphScore Sum of score from two subgraphs - * @param mainnetTokenResults Multicall returned result, this should contain token balances in an array - * @param index index of the current address - * @returns squared root of the sum of subgraph scores and token amounts if the sum is above 1, if not, returns 0. + * Note that if the (mainnetBalance + gnosisBalance + safeStakingBalance) <= 1, the score is zero + * @param mainnetBalance HOPR token balance of the voting account, if the mainnet token balance should be taken into account. Otherwise, zero + * @param gnosisBalance xHOPR and wxHOPR token balance of the voting account, if the gnosis token balance should be taken into account. Otherwise, zero + * @param safeStakingBalance Voting account's summed share of all its owned safes, on the xHOPR/wxHOPR token balance and all the stakes in channels by their managed nodes. + * @param exponent QV-like exponent value. E.g., for quadratic-voting, the exponent is 0.5. This value can be set by the community to any value between 0 and 1, inclusive. Currently it is set at 0.75. + * @returns calculated score */ function calculateScore( - shouldIncludeMainnetValue: boolean, - subgraphScore: BigNumber, - mainnetTokenResults: BigNumber[], - index: number + mainnetBalance: BigNumber, + gnosisBalance: BigNumber, + safeStakingBalance: number, + exponent: number ) { - const summedAmount = shouldIncludeMainnetValue - ? subgraphScore.add(BigNumber.from(mainnetTokenResults[index].toString())) - : subgraphScore; - const summedAmountInEth = parseFloat(formatUnits(summedAmount, 18)); - if (summedAmountInEth > 1) { - return Math.sqrt(summedAmountInEth); + const total = + parseFloat( + formatUnits( + gnosisBalance.add(BigNumber.from(mainnetBalance.toString())), + 18 + ) + ) + safeStakingBalance; + if (total > 1) { + return Math.pow(total, exponent); } else { return 0; } @@ -265,7 +86,7 @@ export async function strategy( // Get the block on mainnet and find the corresponding time on Gnosis chain) const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - // get token balance (if applicable) and block + // get token balance (if applicable) of voters and block const [resHoprOnMainnet, block] = await Promise.all([ options.useHoprOnMainnet ? multicall( @@ -285,59 +106,143 @@ export async function strategy( // get the block number for subgraph query const subgraphBlock = await getGnosisBlockNumber( + XDAI_BLOCK_HOSTED_SUBGRAPH_URL, block.timestamp, options.fallbackGnosisBlock ); - // console.log( - // `Block on mainnet: ${block.number} and on Gnosis ${subgraphBlock}` - // ); // trim addresses to sub of "QUERY_LIMIT" addresses. - const addressSubsets = Array.apply( - null, - Array(Math.ceil(addresses.length / QUERY_LIMIT)) - ).map((_e, i) => addresses.slice(i * QUERY_LIMIT, (i + 1) * QUERY_LIMIT)); + const addressSubsets: string[][] = trimArray(addresses); + + // share of a safe per owner (voter) + const safeFactor = new Map(); + // total stake in Channels of all the nodes managed by the safe + const safeStakeInChannel = new Map(); + // mapping of owner address to an array of their owned safes + const ownerSafes = new Map(); - let returnedFromSubgraphStake; - if (options.useStake) { - // construct URLs for stake season - const hostedAllSeasonSubgraphUrl: string = getHostedSubgraphUrl( + if (options.useSafeStake) { + // array of nodes per safe + const safeNodes = new Map(); + // array of all the nodes managed by the safes where its owner is a voter + const nodes: string[] = []; + // Find the list of Safes (created by HoprStakeFactory contract) where the voting account is an owner, + // as well as the total number of owners of each safe + // construct URLs for safe stake subgraph + const hostedSafeStakeSubgraphUrl = getHostedSubgraphUrl( options.subgraphHostedAccountName ?? DEFAULT_HOPR_HOSTED_ACCOUNT_NAME, - options.subgraphHostedAllSeasonSubgraphName ?? - DEFAULT_HOPR_STAKING_ALL_SEASONS_HOSTED_SUBGRAPH_NAME + options.subgraphHostedSafeStakeSubgraphName ); - const stuidoDevAllSeasonSubgraphUrl = getStudioDevSubgraphUrl( + const stuidoDevSafeStakeSubgraphUrl = getStudioDevSubgraphUrl( options.subgraphStudioDevAccountId, - options.subgraphStudioDevAllSeasonSubgraphName, - options.subgraphStudioDevAllSeasonVersion + options.subgraphStudioDevSafeStakeSubgraphName, + options.subgraphStudioDevSafeStakeVersion ); - const studioProdAllSeasonSubgraphUrl = getStudioProdSubgraphUrl( + const studioProdSafeStakeSubgraphUrl = getStudioProdSubgraphUrl( options.subgraphStudioProdQueryApiKey, - options.subgraphStudioProdAllSeasonQueryId + options.subgraphStudioProdSafeStakeQueryId ); // get subgraph result for stake season - returnedFromSubgraphStake = await Promise.all( + const returnedFromSubgraphStake = await Promise.all( addressSubsets.map((subset) => - stakingSubgraphQuery( - hostedAllSeasonSubgraphUrl, - stuidoDevAllSeasonSubgraphUrl, - studioProdAllSeasonSubgraphUrl, - options.season.toString(), + safeStakeSubgraphQuery( + hostedSafeStakeSubgraphUrl, + stuidoDevSafeStakeSubgraphUrl, + studioProdSafeStakeSubgraphUrl, subset, subgraphBlock, snapshot ) ) ); + // parse the returned value + returnedFromSubgraphStake.forEach((resultSubset) => { + resultSubset.forEach((safe) => { + // 1. safe -> nodes + safeNodes.set(safe.safeAddress, safe.nodes); + nodes.concat(safe.nodes); + if (safe.owners.length == 0) { + // 2. safe -> factor + safeFactor.set(safe.safeAddress, 0); + } else { + // 2. safe -> factor + safeFactor.set(safe.safeAddress, 1 / safe.owners.length); + safe.owners.forEach((owner) => { + const registeredSafes = ownerSafes.get(owner) ?? []; + // 3. owner -> safes + ownerSafes.set(owner, [...registeredSafes, safe.safeAddress]); + }); + } + }); + }); + // trim addresses to sub of "QUERY_LIMIT" addresses. + const nodesSubsets: string[][] = trimArray(nodes); + + // when safe stake is used, check if channel stake is used + if (options.useChannelStake) { + // construct URLs for HOPR channels + const hostedChannelsSubgraphUrl = getHostedSubgraphUrl( + options.subgraphHostedAccountName ?? DEFAULT_HOPR_HOSTED_ACCOUNT_NAME, + options.subgraphHostedChannelsSubgraphName + ); + const stuidoDevChannelsSubgraphUrl = getStudioDevSubgraphUrl( + options.subgraphStudioDevAccountId, + options.subgraphStudioDevChannelsSubgraphName, + options.subgraphStudioDevChannelsVersion + ); + const studioProdChannelsSubgraphUrl = getStudioProdSubgraphUrl( + options.subgraphStudioProdQueryApiKey, + options.subgraphStudioProdChannelsQueryId + ); + // get subgraph result for hopr on gnosis + const returnedFromSubgraphChannels = await Promise.all( + nodesSubsets.map((subset) => + hoprNodeStakeOnChannelsSubgraphQuery( + hostedChannelsSubgraphUrl, + stuidoDevChannelsSubgraphUrl, + studioProdChannelsSubgraphUrl, + subset, + subgraphBlock, + snapshot + ) + ) + ); + // node-wxHOPR balance staked in Channels + const subgraphNodeStakeInChannels = Object.assign( + {}, + ...returnedFromSubgraphChannels + ); + // parse the returned value from channels + for (const key of safeNodes.keys()) { + const nodesManagedBySafe = safeNodes.get(key); + // populate safeStakeInChannel with safeAddress as key and the sum of all the stakes in nodes + if (!nodesManagedBySafe || nodesManagedBySafe.length == 0) { + safeStakeInChannel.set(key, BigNumber.from('0')); + } else { + const stakesInNodes = nodesManagedBySafe.reduce( + (acc, cur) => + (acc = acc.add( + parseUnits(subgraphNodeStakeInChannels[cur] ?? '0', 18) + )), + BigNumber.from('0') + ); + safeStakeInChannel.set(key, stakesInNodes); + } + } + } } + // trim addresses to sub of "QUERY_LIMIT" addresses. + const addressWithSafesSubsets: string[][] = trimArray( + addresses.concat(Array.from(safeFactor.keys())) + ); + let returnedFromSubgraphOnGnosis; if (options.useHoprOnGnosis) { // construct URLs for HOPR on Gnosis - const hostedHoprOnGnosisSubgraphUrl: string = getHostedSubgraphUrl( + const hostedHoprOnGnosisSubgraphUrl = getHostedSubgraphUrl( options.subgraphHostedAccountName ?? DEFAULT_HOPR_HOSTED_ACCOUNT_NAME, - options.subgraphHostedTokenOnGnosisSubgraphName ?? - DEFAULT_HOPR_BALANCE_ON_GNOSIS_HOSTED_SUBGRAPH_NAME + options.subgraphHostedHoprOnGnosisSubgraphName ); const stuidoDevHoprOnGnosisSubgraphUrl = getStudioDevSubgraphUrl( options.subgraphStudioDevAccountId, @@ -350,7 +255,7 @@ export async function strategy( ); // get subgraph result for hopr on gnosis returnedFromSubgraphOnGnosis = await Promise.all( - addressSubsets.map((subset) => + addressWithSafesSubsets.map((subset) => hoprTotalOnGnosisSubgraphQuery( hostedHoprOnGnosisSubgraphUrl, stuidoDevHoprOnGnosisSubgraphUrl, @@ -364,32 +269,51 @@ export async function strategy( } // get and parse balance from subgraph - const subgraphStakeBalanceStake = Object.assign( - {}, - ...returnedFromSubgraphStake - ); - const subgraphStakeBalanceOnGnosis = Object.assign( - {}, - ...returnedFromSubgraphOnGnosis - ); + const subgraphTokenBalanceOnGnosis: { [propName: string]: BigNumber } = + Object.assign({}, ...returnedFromSubgraphOnGnosis); - const subgraphScore: BigNumber[] = addresses.map((address) => - ( - subgraphStakeBalanceStake[address.toLowerCase()] ?? BigNumber.from('0') - ).add( - subgraphStakeBalanceOnGnosis[address.toLowerCase()] ?? BigNumber.from('0') - ) - ); + // sum of all the safes owned by the voting account + // = sum{ factor * (safe's x/wxHOPR balance + wxHOPR tokens staked in the Channels) } + const summedStakePerSafe = addresses.map((address) => { + // from the voting address, get all the safe addresses + const safes = ownerSafes.get(address) ?? []; + if (safes.length == 0) { + return BigNumber.from('0'); + } else { + return safes.reduce((acc, curSafe) => { + // factor * (x/wxHOPR token balance + safe stake in channels) + const curSafeFactor = safeFactor.get(curSafe) ?? 0; + if (curSafeFactor == 0) { + return acc; + } + const curSafeTokenBalance = + subgraphTokenBalanceOnGnosis[curSafe.toLowerCase()] ?? + BigNumber.from('0'); + const curSafeStakeInChannels = + safeStakeInChannel.get(curSafe.toLowerCase()) ?? BigNumber.from('0'); + return ( + acc + + curSafeFactor * + parseFloat( + formatUnits(curSafeTokenBalance.add(curSafeStakeInChannels), 18) + ) + ); + }); + } + }); // return sqrt(subgraph score + hopr on mainet score) return Object.fromEntries( addresses.map((adr, i) => [ adr, calculateScore( - options.useHoprOnMainnet, - subgraphScore[i], - resHoprOnMainnet, - i + options.useHoprOnMainnet ? resHoprOnMainnet[i] : BigNumber.from('0'), + options.useHoprOnGnosis + ? subgraphTokenBalanceOnGnosis[adr.toLowerCase()] ?? + BigNumber.from('0') + : BigNumber.from('0'), + summedStakePerSafe[i], + parseFloat(options.exponent ?? DEFAULT_FACTOR) ) ]) ); diff --git a/src/strategies/hopr-stake-and-balance-qv/utils.ts b/src/strategies/hopr-stake-and-balance-qv/utils.ts new file mode 100644 index 000000000..c8a72cff6 --- /dev/null +++ b/src/strategies/hopr-stake-and-balance-qv/utils.ts @@ -0,0 +1,334 @@ +import { BigNumber } from '@ethersproject/bignumber'; +import { subgraphRequest } from '../../utils'; +import { parseUnits } from '@ethersproject/units'; + +/* + ****************************************** + ****************** TYPES ***************** + ****************************************** + */ +// details of a safe created by the NodeSafeFactory contract +export type Safe = { + safeAddress: string; + owners: string[]; + nodes: string[]; +}; + +/* + ****************************************** + *************** PARAMETERS *************** + ****************************************** + */ +const QUERY_LIMIT = 1000; // 1000 addresses per query in Subgraph + +/* + *********************************************** + **************** SUBGRAPH SETUP *************** + *********************************************** + */ +export function getStudioProdSubgraphUrl( + apiKey: string | null | undefined, + subgraphId: string +): string | null { + return !apiKey + ? null + : `https://gateway.thegraph.com/api/${apiKey}/subgraphs/id/${subgraphId}`; +} + +export function getStudioDevSubgraphUrl( + accountStudioId: string | null | undefined, + subgraphName: string, + version: string +): string | null { + return !accountStudioId + ? null + : `https://api.studio.thegraph.com/query/${accountStudioId}/${subgraphName}/${version}`; +} + +export function getHostedSubgraphUrl( + accountName: string, + subgraphName: string | null +): string | null { + return !subgraphName + ? null + : `https://api.thegraph.com/subgraphs/name/${accountName}/${subgraphName}`; +} + +/** + * Try to query subgraphs from three differnt endpoints (hosted service, studio for development, studio in production), if applicable + * @param hostedSubgraphUrl hosted subgrpah url + * @param stuidoDevSubgraphUrl development url foro studio subgraph + * @param studioProdSubgraphUrl production url foro studio subgraph + * @param builtQuery query object + * @returns null or an object of summed token balance per address + */ +export async function subgraphRequestsToVariousServices( + hostedSubgraphUrl: string | null, + stuidoDevSubgraphUrl: string | null, + studioProdSubgraphUrl: string | null, + builtQuery: any +): Promise { + if (hostedSubgraphUrl) { + try { + // first try with hosted service + return subgraphRequest(hostedSubgraphUrl, builtQuery); + } catch (error) { + // console.log('Failed to get data from hostedSubgraphUrl'); + } + } + + // then try with studio dev service + if (stuidoDevSubgraphUrl) { + try { + return subgraphRequest(stuidoDevSubgraphUrl, builtQuery); + } catch (error) { + // console.log('Failed to get data from stuidoDevSubgraphUrl'); + } + } + + // then try with studio prod service + if (studioProdSubgraphUrl) { + try { + return subgraphRequest(studioProdSubgraphUrl, builtQuery); + } catch (error) { + // console.log('Failed to get data from studioProdSubgraphUrl'); + } + } + return null; +} + +/* + ************************************************* + **************** SUBGRAPH QUERIES *************** + ************************************************* + */ +/** + * Get block number from Gnosis chain at a given timestamp. + * The timestamp of the returned block should be no-bigger than the desired timestamp + * @param queryUrl URL to the subgraph query URL + * @param timestamp number of timestamp + * @param fallbackBlockNumber fallback block number on Gnosis chain, in case no result gets returned. + * @returns a number + */ +export async function getGnosisBlockNumber( + queryUrl: string, + timestamp: number, + fallbackBlockNumber: number +): Promise { + const query = { + blocks: { + __args: { + first: 1, + orderBy: 'number', + orderDirection: 'desc', + where: { + timestamp_lte: timestamp + } + }, + number: true, + timestamp: true + } + }; + + // query from subgraph + const data = await subgraphRequestsToVariousServices( + queryUrl, + null, + null, + query + ); + return !data ? fallbackBlockNumber : Number(data.blocks[0].number); +} + +/** + * Get the list of safe address created by the HoprStakeFactory contract + * where the voting account is an owner. + * It also returns the share per owner (1 / total number of owners) of each safe. + * @param hostedSubgraphUrl url to the hosted subgraph + * @param stuidoDevSubgraphUrl url to the dev subgraph in the studio + * @param studioProdSubgraphUrl url to the production subgraph in the studio + * @param addresses address of voting accounts, which is an owner of the safe + * @param blockNumber block number of the snapshot + * @param snapshot snapshot + * @returns a key-value object where the key is safe address the value is the total number of owners. + */ +export async function safeStakeSubgraphQuery( + hostedSubgraphUrl: string | null, + stuidoDevSubgraphUrl: string | null, + studioProdSubgraphUrl: string | null, + addresses: string[], + blockNumber: number, + snapshot: number | string +): Promise { + const query = { + safes: { + __args: { + first: QUERY_LIMIT, + where: { + owners_: { + owner_in: addresses.map((adr) => adr.toLowerCase()) + } + } + }, + id: true, + owners: { + owner: { + id: true + } + }, + registeredNodesInNetworkRegistry: { + node: { + id: true + } + } + } + }; + + if (snapshot !== 'latest') { + // @ts-ignore + query.safes.__args.block = { number: blockNumber }; + } + + // query from subgraph + const data = await subgraphRequestsToVariousServices( + hostedSubgraphUrl, + stuidoDevSubgraphUrl, + studioProdSubgraphUrl, + query + ); + + // return parsed entries + if (!data || !data.safes || data.safe.length == 0) { + return []; + } else { + return data.safes.map((s) => { + return { + safeAddress: s.id, + owners: s.owners.map((o) => o.owner.id), + nodes: s.registeredNodesInNetworkRegistry.map((n) => n.node.id) + } as Safe; + }); + } +} + +/** + * Get the list of wxHOPR + xHOPR balance of addresses on Gnosis chain + * @param hostedSubgraphUrl url to the hosted subgraph + * @param stuidoDevSubgraphUrl url to the dev subgraph in the studio + * @param studioProdSubgraphUrl url to the production subgraph in the studio + * @param addresses address of wallets + * @param blockNumber block number of the snapshot + * @param snapshot snapshot + * @returns a key-value object where the key is the address and the value is the total HOPR token balance on Gnosis chain. + */ +export async function hoprTotalOnGnosisSubgraphQuery( + hostedSubgraphUrl: string | null, + stuidoDevSubgraphUrl: string | null, + studioProdSubgraphUrl: string | null, + addresses: string[], + blockNumber: number, + snapshot: number | string +): Promise<{ [propName: string]: BigNumber }> { + const query = { + accounts: { + __args: { + first: QUERY_LIMIT, + where: { + id_in: addresses.map((adr) => adr.toLowerCase()) + } + }, + id: true, + totalBalance: true + } + }; + + if (snapshot !== 'latest') { + // @ts-ignore + query.accounts.__args.block = { number: blockNumber }; + } + + // query from subgraph + const data = await subgraphRequestsToVariousServices( + hostedSubgraphUrl, + stuidoDevSubgraphUrl, + studioProdSubgraphUrl, + query + ); + + // map result (data.accounts) to addresses + const entries = !data + ? addresses.map((addr) => [addr, BigNumber.from('0')]) + : data.accounts.map((d) => [ + d.id, + parseUnits(d.totalBalance.toString(), 18) + ]); + return Object.fromEntries(entries); +} + +/** + * Get the total stake in all the outgoing channels per node + * @param hostedSubgraphUrl url to the hosted subgraph + * @param stuidoDevSubgraphUrl url to the dev subgraph in the studio + * @param studioProdSubgraphUrl url to the production subgraph in the studio + * @param addresses node addresses + * @param blockNumber block number of the snapshot + * @param snapshot snapshot + * @returns a key-value object where the key is the address and the value is the total HOPR token balance on Gnosis chain. + */ +export async function hoprNodeStakeOnChannelsSubgraphQuery( + hostedSubgraphUrl: string | null, + stuidoDevSubgraphUrl: string | null, + studioProdSubgraphUrl: string | null, + addresses: string[], + blockNumber: number, + snapshot: number | string +): Promise<{ [propName: string]: BigNumber }> { + const query = { + accounts: { + __args: { + first: QUERY_LIMIT, + where: { + id_in: addresses.map((adr) => adr.toLowerCase()) + } + }, + id: true, + balance: true + } + }; + + if (snapshot !== 'latest') { + // @ts-ignore + query.accounts.__args.block = { number: blockNumber }; + } + + // query from subgraph + const data = await subgraphRequestsToVariousServices( + hostedSubgraphUrl, + stuidoDevSubgraphUrl, + studioProdSubgraphUrl, + query + ); + + // map result (data.accounts) to addresses + const entries = !data + ? addresses.map((addr) => [addr, BigNumber.from('0')]) + : data.accounts.map((d) => [ + d.id, + parseUnits(d.totalBalance.toString(), 18) + ]); + return Object.fromEntries(entries); +} + +/* + *********************************************** + ******************** OTHERS ******************* + *********************************************** + */ +export function trimArray( + originalArray: Array, + size: number = QUERY_LIMIT +): Array> { + return Array.apply(null, Array(Math.ceil(originalArray.length / size))).map( + (_e, i) => originalArray.slice(i * size, (i + 1) * size) + ); +} diff --git a/src/strategies/hopr-staking/examples.json b/src/strategies/hopr-staking/examples.json deleted file mode 100644 index 2a88e0253..000000000 --- a/src/strategies/hopr-staking/examples.json +++ /dev/null @@ -1,61 +0,0 @@ -[ - { - "name": "Stakes and unclaimed rewards from HOPR Stake program", - "strategy": { - "name": "hopr-staking", - "params": { - "tokenAddress": "0xf5581dfefd8fb0e4aec526be659cfab1f8c781da", - "symbol": "HOPR" - } - }, - "network": "1", - "addresses": [ - "0x04BBB7eA18EA570aE47d4489991645E4E49bBf37", - "0x2aF80738aC01e7883d11c912dFe8322C129ae5C5", - "0x0bb43EFc1a613658177D8f67CcF9CFFD8B25b906", - "0x53e85186ebF5A7d4BD06324F7b9D8B3623EF0307", - "0x2DCDB99930E279f1e9Ad11F491163051432542A0", - "0x4326990033eCd87A5444383Cf8c715E696301910", - "0xEd6a59A7C1D5a88b7cb5eb877A7A6078A7e801C7", - "0xeFC05B0D0C8bE8D4Cb3a220ef582E9f7E6FBCd00", - "0xC7B169b108c5e75991C520AEA97140534291C81D", - "0x04Be52434EB64aDdF373137310551ac42013677c", - "0xBE8C93a8C18AF63aAB449994AFAc13E71240ccC4", - "0xf813773eBDD4759c1B780d745081f046A5B776fB", - "0x7F26C34Ed10bF66602009231bBFF24f2f84e9270", - "0x4abd7276C53279b3aBFFF2B5D8A47c0AFc84833B", - "0x3e1A12a6019ee26418F22B656926fE38F5e58C5f", - "0x7A27A4D91231aCB3282b410Cc784517B417FA0DA" - ], - "snapshot": 13269966 - }, - { - "name": "Stakes and unclaimed rewards from HOPR Stake program", - "strategy": { - "name": "hopr-staking", - "params": { - "tokenAddress": "0xf5581dfefd8fb0e4aec526be659cfab1f8c781da" - } - }, - "network": "100", - "addresses": [ - "0x04BBB7eA18EA570aE47d4489991645E4E49bBf37", - "0x2aF80738aC01e7883d11c912dFe8322C129ae5C5", - "0x0bb43EFc1a613658177D8f67CcF9CFFD8B25b906", - "0x53e85186ebF5A7d4BD06324F7b9D8B3623EF0307", - "0x2DCDB99930E279f1e9Ad11F491163051432542A0", - "0x4326990033eCd87A5444383Cf8c715E696301910", - "0xEd6a59A7C1D5a88b7cb5eb877A7A6078A7e801C7", - "0xeFC05B0D0C8bE8D4Cb3a220ef582E9f7E6FBCd00", - "0xC7B169b108c5e75991C520AEA97140534291C81D", - "0x04Be52434EB64aDdF373137310551ac42013677c", - "0xBE8C93a8C18AF63aAB449994AFAc13E71240ccC4", - "0xf813773eBDD4759c1B780d745081f046A5B776fB", - "0x7F26C34Ed10bF66602009231bBFF24f2f84e9270", - "0x4abd7276C53279b3aBFFF2B5D8A47c0AFc84833B", - "0x3e1A12a6019ee26418F22B656926fE38F5e58C5f", - "0x7A27A4D91231aCB3282b410Cc784517B417FA0DA" - ], - "snapshot": 18200908 - } -] diff --git a/src/strategies/hopr-staking/index.ts b/src/strategies/hopr-staking/index.ts deleted file mode 100644 index 33f630559..000000000 --- a/src/strategies/hopr-staking/index.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { BigNumber } from '@ethersproject/bignumber'; -import { subgraphRequest } from '../../utils'; - -export const author = 'QYuQianchen'; -export const version = '0.1.0'; - -const XDAI_BLOCK_SUBGRAPH_URL = - 'https://api.thegraph.com/subgraphs/name/1hive/xdai-blocks'; -const HOPR_STAKING_SUBGRAPH_URL = - 'https://api.thegraph.com/subgraphs/name/hoprnet/hopr-staking-program'; -const LIMIT = 1000; // 1000 addresses per query in Subgraph - -async function getXdaiBlockNumber(timestamp: number): Promise { - const query = { - blocks: { - __args: { - first: 1, - orderBy: 'number', - orderDirection: 'desc', - where: { - timestamp_lte: timestamp - } - }, - number: true, - timestamp: true - } - }; - const data = await subgraphRequest(XDAI_BLOCK_SUBGRAPH_URL, query); - return Number(data.blocks[0].number); -} - -async function stakingSubgraphQuery( - addresses: string[], - blockNumber: number -): Promise<{ [propName: string]: BigNumber }> { - const query = { - accounts: { - __args: { - first: LIMIT, - block: { - number: blockNumber - }, - where: { - id_in: addresses.map((adr) => adr.toLowerCase()) - } - }, - id: true, - actualStake: true, - virtualStake: true, - unclaimedRewards: true - } - }; - const data = await subgraphRequest(HOPR_STAKING_SUBGRAPH_URL, query); - // map result (data.accounts) to addresses - const entries = data.accounts.map((d) => [ - d.id, - BigNumber.from(d.actualStake).add( - BigNumber.from(d.virtualStake).add(BigNumber.from(d.unclaimedRewards)) - ) - ]); - return Object.fromEntries(entries); -} - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const isXdai = network === '100'; // either xDAI or ETH mainnet - const [block] = await Promise.all([provider.getBlock(blockTag)]); - - // get the block number for subgraph query - const subgraphBlock = isXdai - ? block.number - : await getXdaiBlockNumber(block.timestamp); - - // trim addresses to sub of "LIMIT" addresses. - const addressSubsets = Array.apply( - null, - Array(Math.ceil(addresses.length / LIMIT)) - ).map((_e, i) => addresses.slice(i * LIMIT, (i + 1) * LIMIT)); - - const returnedFromSubgraph = await Promise.all( - addressSubsets.map((subset) => stakingSubgraphQuery(subset, subgraphBlock)) - ); - - // get and parse balance from subgraph - const subgraphBalance = Object.assign({}, ...returnedFromSubgraph); - const subgraphScore = addresses.map( - (address) => subgraphBalance[address.toLowerCase()] ?? 0 - ); - - return Object.fromEntries( - addresses.map((adr, i) => [ - adr, - parseFloat(formatUnits(subgraphScore[i], 18)) // subgraph balance - ]) - ); -} diff --git a/src/strategies/index.ts b/src/strategies/index.ts index 8242f8518..6759234d2 100644 --- a/src/strategies/index.ts +++ b/src/strategies/index.ts @@ -9,11 +9,8 @@ import * as erc20Votes from './erc20-votes'; import * as erc20VotesWithOverride from './erc20-votes-with-override'; import * as antiWhale from './anti-whale'; import * as balancer from './balancer'; -import * as balancerErc20InternalBalanceOf from './balancer-erc20-internal-balance-of'; -import * as sunder from './sunder'; import * as balancerSmartPool from './balancer-smart-pool'; import * as contractCall from './contract-call'; -import * as dextfVaults from './dextf-staked-in-vaults'; import * as dfynFarms from './dfyn-staked-in-farms'; import * as dfynVaults from './dfyn-staked-in-vaults'; import * as vDfynVault from './balance-in-vdfyn-vault'; @@ -35,7 +32,6 @@ import * as erc20BalanceOfTopHolders from './erc20-balance-of-top-holders'; import * as erc20BalanceOfWeighted from './erc20-balance-of-weighted'; import * as ethalendBalanceOf from './ethalend-balance-of'; import * as prepoVesting from './prepo-vesting'; -import * as mintoBalanceAll from './minto-balance-of-all'; import * as erc20BalanceOfIndexed from './erc20-balance-of-indexed'; import * as revest from './revest'; import * as erc20Price from './erc20-price'; @@ -61,22 +57,9 @@ import * as pancake from './pancake'; import * as pancakeProfile from './pancake-profile'; import * as synthetix from './synthetix'; import * as aelinCouncil from './aelin-council'; -import * as synthetixQuadratic from './synthetix-quadratic'; -import * as synthetixQuadraticOne from './synthetix-quadratic_1'; -import * as synthetixQuadraticTwo from './synthetix-quadratic_2'; -import * as synthetixOne from './synthetix_1'; -import * as synthetixNonQuadratic from './synthetix-non-quadratic'; -import * as synthetixNonQuadraticOne from './synthetix-non-quadratic_1'; -import * as synthetixNonQuadraticTwo from './synthetix-non-quadratic_2'; import * as ctoken from './ctoken'; -import * as cream from './cream'; -import * as esd from './esd'; -import * as esdDelegation from './esd-delegation'; import * as stakedUniswap from './staked-uniswap'; -import * as piedao from './piedao'; -import * as ethReceived from './eth-received'; import * as erc20Received from './erc20-received'; -import * as ethPhilanthropy from './eth-philanthropy'; import * as xDaiEasyStaking from './xdai-easy-staking'; import * as xDaiPOSDAOStaking from './xdai-posdao-staking'; import * as xDaiStakeHolders from './xdai-stake-holders'; @@ -84,7 +67,6 @@ import * as xDaiStakeDelegation from './xdai-stake-delegation'; import * as defidollar from './defidollar'; import * as aavegotchi from './aavegotchi'; import * as aavegotchiAgip from './aavegotchi-agip'; -import * as aavegotchiAgip17 from './aavegotchi-agip-17'; import * as mithcash from './mithcash'; import * as dittomoney from './dittomoney'; import * as balancerUnipool from './balancer-unipool'; @@ -95,6 +77,7 @@ import * as stakedKeep from './staked-keep'; import * as stakedDaomaker from './staked-daomaker'; import * as typhoon from './typhoon'; import * as delegation from './delegation'; +import * as delegationWithCap from './delegation-with-cap'; import * as delegationWithOverrides from './delegation-with-overrides'; import * as withDelegation from './with-delegation'; import * as ticket from './ticket'; @@ -103,23 +86,19 @@ import * as ticketValidity from './ticket-validity'; import * as validation from './validation'; import * as opium from './opium'; import * as ocean from './ocean-marketplace'; -import * as ocean_v4 from './ocean-marketplace-v4'; import * as theGraphBalance from './the-graph-balance'; import * as theGraphDelegation from './the-graph-delegation'; import * as theGraphIndexing from './the-graph-indexing'; import * as whitelist from './whitelist'; import * as whitelistWeighted from './whitelist-weighted'; import * as tokenlon from './tokenlon'; -import * as rebased from './rebased'; import * as pobHash from './pob-hash'; -import * as totalAxionShares from './total-axion-shares'; import * as erc1155BalanceOf from './erc1155-balance-of'; import * as erc1155BalanceOfCv from './erc1155-balance-of-cv'; import * as erc1155WithMultiplier from './erc1155-with-multiplier'; import * as compLikeVotes from './comp-like-votes'; import * as governorAlpha from './governor-alpha'; import * as pagination from './pagination'; -import * as rulerStakedToken from './ruler-staked-token'; import * as rulerStakedLP from './ruler-staked-lp'; import * as xcover from './xcover'; import * as niuStaked from './niu-staked'; @@ -136,8 +115,6 @@ import * as erc1155AllBalancesOf from './erc1155-all-balances-of'; import * as trancheStakingLP from './tranche-staking-lp'; import * as masterchefPoolBalance from './masterchef-pool-balance'; import * as masterchefPoolBalancePrice from './masterchef-pool-balance-price'; -import * as avnBalanceOfStaked from './avn-balance-of-staked'; -import * as badgeth from './badgeth'; import * as api from './api'; import * as apiPost from './api-post'; import * as apiV2 from './api-v2'; @@ -146,7 +123,6 @@ import * as molochAll from './moloch-all'; import * as molochLoot from './moloch-loot'; import * as erc721Enumerable from './erc721-enumerable'; import * as erc721WithMultiplier from './erc721-with-multiplier'; -import * as protofiErc721TierWeighted from './protofi-erc721-tier-weighted'; import * as erc721WithTokenId from './erc721-with-tokenid'; import * as erc721WithTokenIdRangeWeights from './erc721-with-tokenid-range-weights'; import * as erc721WithTokenIdRangeWeightsSimple from './erc721-with-tokenid-range-weights-simple'; @@ -158,32 +134,22 @@ import * as erc721 from './erc721'; import * as erc721MultiRegistry from './erc721-multi-registry'; import * as apescape from './apescape'; import * as liftkitchen from './liftkitchen'; -import * as coordinape from './coordinape'; import * as decentralandEstateSize from './decentraland-estate-size'; import * as decentralandWearableRariry from './decentraland-wearable-rarity'; import * as decentralandRentalLessors from './decentraland-rental-lessors'; -import * as iotexBalance from './iotex-balance'; import * as iotexStakedBalance from './iotex-staked-balance'; import * as xrc20BalanceOf from './xrc20-balance-of'; import * as brightid from './brightid'; import * as inverseXINV from './inverse-xinv'; import * as modefi from './modefi'; -import * as modefiStaking from './modefi-staking'; import * as spookyswap from './spookyswap'; -import * as squadzPower from './squadz-power'; import * as glide from './glide'; -import * as goldfinchVotingPower from './goldfinch-voting-power'; -import * as goldfinchMembership from './goldfinch-membership'; import * as rnbwBalance from './rnbw-balance'; import * as celerSgnDelegation from './celer-sgn-delegation'; -import * as balancerDelegation from './balancer-delegation'; import * as infinityProtocolPools from './infinityprotocol-liquidity-pools'; import * as aaveGovernancePower from './aave-governance-power'; import * as cake from './cake'; import * as aks from './aks'; -import * as tomyumswap from './tomyumswap'; -import * as planetFinance from './planet-finance'; -import * as planetFinancev2 from './planet-finance-v2'; import * as impossibleFinance from './impossible-finance'; import * as immutableX from './immutable-x'; import * as ogn from './ogn'; @@ -191,7 +157,6 @@ import * as oolongswap from './oolongswap'; import * as zrxVotingPower from './zrx-voting-power'; import * as tombFinance from './tomb-finance'; import * as trancheStakingSLICE from './tranche-staking-slice'; -import * as unipoolSameToken from './unipool-same-token'; import * as unipoolUniv2Lp from './unipool-univ2-lp'; import * as unipoolXSushi from './unipool-xsushi'; import * as taraxaDelegation from './taraxa-delegation'; @@ -203,11 +168,9 @@ import * as uniswapV3Staking from './uniswap-v3-staking'; import * as l2Deversifi from './l2-deversifi'; import * as vestedDeversifi from './vested-deversifi'; import * as biswap from './biswap'; -import * as cronaswap from './cronaswap'; import * as honeyswap from './honeyswap'; import * as eglVote from './egl-vote'; import * as mcnFarm from './mcn-farm'; -import * as snowswap from './snowswap'; import * as meebitsdao from './meebitsdao'; import * as membership from './membership'; import * as holdsTokens from './holds-tokens'; @@ -219,39 +182,24 @@ import * as hasrock from './has-rock'; import * as flexaCapacityStaking from './flexa-capacity-staking'; import * as sunriseGamingUniv2Lp from './sunrisegaming-univ2-lp'; import * as sunriseGamingStaking from './sunrisegaming-staking'; -import * as sUmamiHolders from './sumami-holders'; import * as singleStakingAutoCompoundBalanceOf from './single-staking-autocompound-balanceof'; import * as singleStakingPoolsBalanceOf from './single-staking-pools-balanceof'; import * as occStakeOf from './occ-stake-of'; -import * as hoprStaking from './hopr-staking'; -import * as hoprStakingS2 from './hopr-staking-s2'; -import * as hoprStakingBySeason from './hopr-staking-by-season'; import * as hoprBridgedBalance from './hopr-bridged-balance'; import * as hoprStakeAndBalanceQV from './hopr-stake-and-balance-qv'; import * as lootCharacterGuilds from './loot-character-guilds'; -import * as swapr from './swapr'; -import * as cyberkongz from './cyberkongz'; -import * as cyberkongzV2 from './cyberkongz-v2'; -import * as cyberkongzV3 from './cyberkongz-v3'; import * as compLikeVotesInclusive from './comp-like-votes-inclusive'; import * as mstable from './mstable'; import * as hashesVoting from './hashes-voting'; -import * as hashflowGovernancePower from './hashflow-governance-power'; import * as hashflowVeHft from './hashflow-vehft'; -import * as podLeader from './pod-leader'; import * as aavegotchiWagmiGuild from './aavegotchi-wagmi-guild'; import * as polisBalance from './polis-balance'; import * as techQuadraticRankedChoice from './tech-quadratic-ranked-choice'; import * as mutantCatsStakersAndHolders from './mutant-cats-stakers-and-holders'; -import * as vaultTokenLpBalance from './vault-token-lp-balance'; -import * as singleStakingVaultBalanceOf from './single-staking-vault-balanceof'; import * as razorVoting from './razor-network-voting'; -import * as svsStaking from './svs-staking'; import * as mcbBalanceFromGraph from './mcb-balance-from-graph'; import * as colonyReputation from './colony-reputation'; -import * as radicleCommunityTokens from './radicle-community-tokens'; import * as digitalaxMonaQuickswap from './digitalax-mona-quickswap'; -import * as digitalaxDecoToMona from './digitalax-deco-to-mona'; import * as digitalaxGenesisContribution from './digitalax-genesis-contribution'; import * as digitalaxLPStakers from './digitalax-lp-stakers'; import * as digitalaxMonaStakersMatic from './digitalax-mona-stakers-matic'; @@ -265,24 +213,17 @@ import * as bscMvb from './bsc-mvb'; import * as coinswap from './coinswap'; import * as dgenesis from './dgenesis'; import * as votePowerAndShare from './vote-power-and-share'; -import * as blockzerolabsCryptonauts from './blockzerolabs-cryptonauts'; import * as math from './math'; import * as pushVotingPower from './push-voting-power'; import * as stakedPSPBalance from './staked-psp-balance'; import * as erc20BalanceOfContractMultiplier from './erc20-balance-of-contract-multiplier'; -import * as agave from './agave'; import * as juicebox from './juicebox'; import * as snetFarmers from './snet-farmers'; import * as snetStakers from './snet-stakers'; import * as snetLiquidityProviders from './snet-liquidity-providers'; -import * as minMaxMcnFarm from './minmax-mcn-farm'; import * as unstackedToadzAndStackedToadzStakers from './unstackedtoadz-and-stackedtoadz-stakers'; -import * as jadeSmrt from './jade-smrt'; import * as oceanDAOBrightID from './ocean-dao-brightid'; -import * as saddleFinance from './saddle-finance'; -import * as saddleFinanceV2 from './saddle-finance-v2'; import * as lydiaGovVault from './lydia-gov-vault'; -import * as xkawaFarm from './xkawa-farm'; import * as darkforestScore from './darkforest-score'; import * as orangeReputationBasedVoting from './orange-reputation-based-voting'; import * as orangeReputationNftBasedVoting from './orange-reputation-nft-based-voting'; @@ -291,13 +232,9 @@ import * as pathBalanceStakedAndLocked from './path-balance-staked-and-locked'; import * as bottoDao from './botto-dao'; import * as genart from './genart'; import * as erc721MultiRegistryWeighted from './erc721-multi-registry-weighted'; -import * as genomesdao from './genomesdao'; -import * as zorro from './zorro'; -import * as voltVotingPower from './volt-voting-power'; import * as balancerPoolid from './balancer-poolid'; import * as stakedBalancer from './staked-balancer'; import * as stakedUniswapModifiable from './staked-uniswap-modifiable'; -import * as givethXdaiBalance from './giveth-xdai-balance'; import * as givethGnosisBalanceV2 from './giveth-gnosis-balance-v2'; import * as givethBalancerBalance from './giveth-balancer-balance'; import * as erc1155BalanceOfIds from './erc1155-balance-of-ids'; @@ -306,8 +243,8 @@ import * as erc1155weighted from './erc1155-weighted-by-id'; import * as stakersAndHolders from './stakers-and-holders'; import * as banksyDao from './banksy-dao'; import * as spacey2025 from './spacey2025'; +import * as spacefiBlp from './spacefi-blp'; import * as sandmanDao from './sandman-dao'; -import * as ethercatsFounderSeries from './ethercats-founder-series'; import * as veBalanceOfAt from './ve-balance-of-at'; import * as veRibbon from './ve-ribbon'; import * as veRibbonVotingPower from './ve-ribbon-voting-power'; @@ -316,7 +253,6 @@ import * as landDaoTiers from './landdao-token-tiers'; import * as defiplaza from './defiplaza'; import * as stakingClaimedUnclaimed from './staking-claimed-unclaimed'; import * as gysrStakingBalance from './gysr-staking-balance'; -import * as gysrPendingRewards from './gysr-pending-rewards'; import * as gysrLPStakingBalance from './gysr-lp-staking-balance'; import * as wanakafarmStaking from './wanakafarm-staking'; import * as starsharks from './starsharks'; @@ -324,21 +260,15 @@ import * as printerFinancial from './printer-financial'; import * as ethercatsFoundersSeries from './ethercats-founders-series'; import * as potion from './potion'; import * as MinotaurMoney from './minotaur-money'; -import * as safetyModuleBptPower from './safety-module-bpt-power'; import * as convFinance from './conv-finance'; import * as sdBoost from './sd-boost'; -import * as capitalDaoStaking from './capitaldao-staking'; -import * as erc20RebaseWrapper from './erc20-rebase-wrapper'; import * as wanakafarmLandIngame from './wanakafarm-land-ingame'; -import * as meebitsDaoDelegation from './meebitsdao-delegation'; import * as starcatchersTopWindow from './starcatchers-top-window'; import * as gno from './gno'; -import * as umaVoting from './uma-voting'; import * as masterchefPoolBalanceNoRewarddebt from './masterchef-pool-balance-no-rewarddebt'; import * as proofOfHumanity from './proof-of-humanity'; import * as samuraiLegendsGeneralsBalance from './samurailegends-generals-balance'; import * as dogsUnchained from './dogs-unchained'; -import * as stakeDAOGovernanceUpdate from './stakedao-governance-update'; import * as umamiVoting from './umami-voting'; import * as liquidityTokenProvide from './liquidity-token-provide'; import * as gamiumVoting from './gamium-voting'; @@ -348,19 +278,13 @@ import * as rowdyRoos from './rowdy-roos'; import * as ethermon721 from './ethermon-erc721'; import * as etherorcsComboBalanceOf from './etherorcs-combo-balanceof'; import * as hedgey from './hedgey'; -import * as hedgeyMulti from './hedgey-multi'; import * as hedgeyDelegate from './hedgey-delegate'; import * as sybilProtection from './sybil-protection'; import * as veBalanceOfAtNFT from './ve-balance-of-at-nft'; import * as genzeesFromSubgraph from './genzees-from-subgraph'; -import * as ginFinance from './gin-finance'; import * as positionGovernancePower from './position-governance-power'; import * as creditLp from './credit-lp'; import * as helix from './helix'; -import * as arrakisFinance from './arrakis-finance'; -import * as auraFinance from './aura-vlaura-vebal'; -import * as auraFinanceWithOverrides from './aura-vlaura-vebal-with-overrides'; -import * as auraBalanceOfVlauraVebal from './aura-balance-of-vlaura-vebal'; import * as auraBalanceOfSingleAsset from './aura-vault-balance-of-single-asset'; import * as rocketpoolNodeOperator from './rocketpool-node-operator'; import * as rocketpoolNodeOperatorv2 from './rocketpool-node-operator-v2'; @@ -368,72 +292,49 @@ import * as rocketpoolNodeOperatorv3 from './rocketpool-node-operator-v3'; import * as earthfundChildDaoStakingBalance from './earthfund-child-dao-staking-balance'; import * as unipilotVaultPilotBalance from './unipilot-vault-pilot-balance'; import * as sdBoostTWAVP from './sd-boost-twavp'; -import * as apeswap from './apeswap'; import * as fortaShares from './forta-shares'; -import * as solvVoucherClaimable from './solv-voucher-claimable'; -import * as h2o from './h2o'; -import * as dopamine from './dopamine'; import * as lrcL2SubgraphBalanceOf from './lrc-l2-subgraph-balance-of'; import * as lrcL2NftBalanceOf from './lrc-l2-nft-balance-of'; import * as lrcLPSubgraphBalanceOf from './lrc-lp-subgraph-balance-of'; -import * as lrcNFTDAOSearch from './lrc-nft-dao-search'; import * as lrcNFTmult from './lrc-nft-search-mult'; -import * as erc3525VestingVoucher from './erc3525-vesting-voucher'; -import * as rariFuse from './rari-fuse'; -import * as selfswap from './selfswap'; -import * as xrookBalanceOfUnderlyingWeighted from './xrook-balance-of-underlying-weighted'; import * as bancorPoolTokenUnderlyingBalance from './bancor-pool-token-underlying-balance'; -import * as orbsNetworkDelegation from './orbs-network-delegation'; import * as balanceOfSubgraph from './balance-of-subgraph'; import * as wagdieSubgraph from './wagdie-subgraph'; import * as erc3525FlexibleVoucher from './erc3525-flexible-voucher'; import * as erc721PairWeights from './erc721-pair-weights'; import * as harmonyStaking from './harmony-staking'; -import * as echelonCachedErc1155Decay from './echelon-cached-erc1155-decay'; import * as orcaPod from './orca-pod'; import * as metropolisPod from './metropolis-pod'; -import * as proxyProtocolErc20BalanceOf from './proxyprotocol-erc20-balance-of'; import * as proxyProtocolErc721BalanceOf from './proxyprotocol-erc721-balance-of'; -import * as proxyProtocolErc1155BalanceOf from './proxyprotocol-erc1155-balance-of'; import * as arrowVesting from './arrow-vesting'; import * as tutellusProtocol from './tutellus-protocol'; import * as fightClub from './fight-club'; import * as tproStaking from './tpro-staking'; import * as safeVested from './safe-vested'; -import * as riskharborUnderwriter from './riskharbor-underwriter'; import * as otterspaceBadges from './otterspace-badges'; import * as syntheticNounsClaimerOwner from './synthetic-nouns-with-claimer'; import * as echelonWalletPrimeAndCachedKey from './echelon-wallet-prime-and-cached-key'; import * as nation3VotesWIthDelegations from './nation3-votes-with-delegations'; import * as nation3CoopPassportWithDelegations from './nation3-passport-coop-with-delegations'; -import * as aavegotchiAgip37WapGhst from './aavegotchi-agip-37-wap-ghst'; -import * as aavegotchiAgip37GltrStakedLp from './aavegotchi-agip-37-gltr-staked-lp'; import * as posichainStaking from './posichain-staking'; import * as posichainTotalBalance from './posichain-total-balance'; import * as erc20TokensPerUni from './erc20-tokens-per-uni'; import * as bancorStandardRewardsUnderlyingBalance from './bancor-standard-rewards-underlying-balance'; import * as sdVoteBoost from './sd-vote-boost'; import * as sdVoteBoostTWAVP from './sd-vote-boost-twavp'; -import * as clqdrBalanceWithLp from './clqdr-balance-with-lp'; import * as ninechroniclesStakedAndDcc from './ninechronicles-staked-and-dcc'; import * as spreadsheet from './spreadsheet'; import * as offchainDelegation from './offchain-delegation'; -import * as dslaParametricStakingServiceCredits from './dsla-parametric-staking-service-credits'; import * as rep3Badges from './rep3-badges'; import * as marsecosystem from './marsecosystem'; import * as ari10StakingLocked from './ari10-staking-locked'; -import * as multichainSerie from './multichain-serie'; -import * as ctsiStaking from './ctsi-staking'; -import * as ctsiStakingPool from './ctsi-staking-pool'; import * as skaleDelegationWeighted from './skale-delegation-weighted'; import * as reliquary from './reliquary'; import * as acrossStakedAcx from './across-staked-acx'; -import * as vstaPoolStaking from './vsta-pool-staking'; import * as lodestarVesting from './lodestar-vesting'; import * as lodestarStakedLp from './lodestar-staked-lp'; import * as jpegdLockedJpegOf from './jpegd-locked-jpeg-of'; import * as litDaoGovernance from './lit-dao-governance'; -import * as babywealthyclub from './babywealthyclub'; import * as battleflyVGFLYAndStakedGFLY from './battlefly-vgfly-and-staked-gfly'; import * as nexonArmyNFT from './nexon-army-nft'; import * as moonbeamFreeBalance from './moonbeam-free-balance'; @@ -442,7 +343,6 @@ import * as pspInSePSP2Balance from './psp-in-sepsp2-balance'; import * as pdnBalancesAndVests from './pdn-balances-and-vests'; import * as izumiVeiZi from './izumi-veizi'; import * as lqtyProxyStakers from './lqty-proxy-stakers'; -import * as echelonWalletPrimeAndCachedKeyGated from './echelon-wallet-prime-and-cached-key-gated'; import * as rdntCapitalVoting from './rdnt-capital-voting'; import * as stakedDefiBalance from './staked-defi-balance'; import * as degenzooErc721AnimalsWeighted from './degenzoo-erc721-animals-weighted'; @@ -463,11 +363,53 @@ 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 sdVoteBoostTWAVPBalanceof from './sd-vote-boost-twavp-balanceof'; import * as friendTech from './friend-tech'; import * as moonbase from './moonbase'; import * as dssVestUnpaid from './dss-vest-unpaid'; import * as dssVestBalanceAndUnpaid from './dss-vest-balance-and-unpaid'; import * as eoaBalanceAndStakingPools from './eoa-balance-and-staking-pools'; +import * as stationScoreIfBadge from './station-score-if-badge'; +import * as stationConstantIfBadge from './station-constant-if-badge'; +import * as mangroveStationQVScaledToMGV from './mangrove-station-qv-scaled-to-mgv'; +import * as floki from './floki'; +import * as hatsProtocolHatId from './hats-protocol-hat-id'; +import * as hatsProtocolHatIds from './hats-protocol-hat-ids'; +import * as bubblegumKids from './bubblegum-kids'; +import * as clipperStakedSail from './clipper-staked-sail'; +import * as plearn from './plearn'; +import * as snote from './snote'; +import * as streamr from './streamr'; +import * as aavegotchiAgip17 from './aavegotchi-agip-17'; +import * as aavegotchiAgip37GltrStakedLp from './aavegotchi-agip-37-gltr-staked-lp'; +import * as aavegotchiAgip37WapGhst from './aavegotchi-agip-37-wap-ghst'; +import * as agave from './agave'; +import * as arrakisFinance from './arrakis-finance'; +import * as ctsiStakingPool from './ctsi-staking-pool'; +import * as cyberkongzV2 from './cyberkongz-v2'; +import * as dextfStakedInVaults from './dextf-staked-in-vaults'; +import * as genomesdao from './genomesdao'; +import * as goldfinchMembership from './goldfinch-membership'; +import * as goldfinchVotingPower from './goldfinch-voting-power'; +import * as h2o from './h2o'; +import * as hoprStakingBySeason from './hopr-staking-by-season'; +import * as hoprStakingS2 from './hopr-staking-s2'; +import * as ilv from './ilv'; +import * as meebitsdaoDelegation from './meebitsdao-delegation'; +import * as modefiStaking from './modefi-staking'; +import * as orbsNetworkDelegation from './orbs-network-delegation'; +import * as planetFinanceV2 from './planet-finance-v2'; +import * as rariFuse from './rari-fuse'; +import * as synthetixNonQuadratic_1 from './synthetix-non-quadratic_1'; +import * as synthetixQuadratic from './synthetix-quadratic'; +import * as synthetixQuadratic_1 from './synthetix-quadratic_1'; +import * as synthetix_1 from './synthetix_1'; +import * as totalAxionShares from './total-axion-shares'; +import * as unipoolSameToken from './unipool-same-token'; +import * as vendorV2BorrowerCollateralBalanceOf from './vendor-v2-borrower-collateral-balance-of'; +import * as voltVotingPower from './volt-voting-power'; +import * as xdaiStakersAndHolders from './xdai-stakers-and-holders'; const strategies = { 'cap-voting-power': capVotingPower, @@ -480,27 +422,20 @@ const strategies = { 'recusal-list': recusalList, 'landdao-token-tiers': landDaoTiers, 'giveth-balancer-balance': givethBalancerBalance, - 'giveth-xdai-balance': givethXdaiBalance, 'giveth-gnosis-balance-v2': givethGnosisBalanceV2, 'nouns-rfp-power': nounsPower, - coordinape, 'anti-whale': antiWhale, balancer, - sunder, 'balancer-smart-pool': balancerSmartPool, 'lit-dao-governance': litDaoGovernance, - 'balancer-erc20-internal-balance-of': balancerErc20InternalBalanceOf, 'balance-in-vdfyn-vault': vDfynVault, 'erc20-received': erc20Received, 'contract-call': contractCall, defiplaza: defiplaza, - 'dextf-staked-in-vaults': dextfVaults, 'dfyn-staked-in-farms': dfynFarms, 'dfyn-staked-in-vaults': dfynVaults, 'dps-nft-strategy': dpsNFTStrategy, 'dps-nft-strategy-nova': dpsNFTStrategyNova, - 'eth-received': ethReceived, - 'eth-philanthropy': ethPhilanthropy, 'ens-domains-owned': ensDomainsOwned, 'ens-reverse-record': ensReverseRecord, 'ens-10k-club': ens10kClub, @@ -520,7 +455,6 @@ const strategies = { 'erc20-balance-of-quadratic-delegation': erc20BalanceOfQuadraticDelegation, 'erc20-balance-of-top-holders': erc20BalanceOfTopHolders, 'erc20-balance-of-weighted': erc20BalanceOfWeighted, - 'minto-balance-of-all': mintoBalanceAll, 'erc20-balance-of-indexed': erc20BalanceOfIndexed, 'erc20-price': erc20Price, 'ethalend-balance-of': ethalendBalanceOf, @@ -534,7 +468,6 @@ const strategies = { erc721, 'erc721-enumerable': erc721Enumerable, 'erc721-with-multiplier': erc721WithMultiplier, - 'protofi-erc721-tier-weighted': protofiErc721TierWeighted, 'erc721-with-tokenid': erc721WithTokenId, 'erc721-with-tokenid-range-weights': erc721WithTokenIdRangeWeights, 'erc721-with-tokenid-range-weights-simple': @@ -561,19 +494,8 @@ const strategies = { 'pancake-profile': pancakeProfile, synthetix, 'aelin-council': aelinCouncil, - 'synthetix-quadratic': synthetixQuadratic, - 'synthetix-quadratic_1': synthetixQuadraticOne, - 'synthetix-quadratic_2': synthetixQuadraticTwo, - synthetix_1: synthetixOne, - 'synthetix-non-quadratic': synthetixNonQuadratic, - 'synthetix-non-quadratic_1': synthetixNonQuadraticOne, - 'synthetix-non-quadratic_2': synthetixNonQuadraticTwo, ctoken, - cream, 'staked-uniswap': stakedUniswap, - esd, - 'esd-delegation': esdDelegation, - piedao, 'xdai-easy-staking': xDaiEasyStaking, 'xdai-posdao-staking': xDaiPOSDAOStaking, 'xdai-stake-holders': xDaiStakeHolders, @@ -581,7 +503,6 @@ const strategies = { defidollar, aavegotchi, 'aavegotchi-agip': aavegotchiAgip, - 'aavegotchi-agip-17': aavegotchiAgip17, mithcash, stablexswap, dittomoney, @@ -590,6 +511,7 @@ const strategies = { 'balancer-unipool': balancerUnipool, typhoon, delegation, + 'delegation-with-cap': delegationWithCap, 'delegation-with-overrides': delegationWithOverrides, 'with-delegation': withDelegation, ticket, @@ -598,20 +520,16 @@ const strategies = { validation, opium, 'ocean-marketplace': ocean, - 'ocean-marketplace-v4': ocean_v4, 'the-graph-balance': theGraphBalance, 'the-graph-delegation': theGraphDelegation, 'the-graph-indexing': theGraphIndexing, whitelist, 'whitelist-weighted': whitelistWeighted, tokenlon, - rebased, 'pob-hash': pobHash, - 'total-axion-shares': totalAxionShares, 'comp-like-votes': compLikeVotes, 'governor-alpha': governorAlpha, pagination, - 'ruler-staked-token': rulerStakedToken, 'ruler-staked-lp': rulerStakedLP, xcover, 'niu-staked': niuStaked, @@ -629,7 +547,6 @@ const strategies = { 'tranche-staking-lp': trancheStakingLP, 'masterchef-pool-balance': masterchefPoolBalance, 'masterchef-pool-balance-price': masterchefPoolBalancePrice, - 'avn-balance-of-staked': avnBalanceOfStaked, api, 'api-post': apiPost, 'api-v2': apiV2, @@ -646,34 +563,23 @@ const strategies = { brightid, 'inverse-xinv': inverseXINV, modefi, - 'modefi-staking': modefiStaking, - 'iotex-balance': iotexBalance, 'iotex-staked-balance': iotexStakedBalance, 'xrc20-balance-of': xrc20BalanceOf, spookyswap, - 'squadz-power': squadzPower, glide, - 'goldfinch-voting-power': goldfinchVotingPower, - 'goldfinch-membership': goldfinchMembership, 'rnbw-balance': rnbwBalance, 'celer-sgn-delegation': celerSgnDelegation, - 'balancer-delegation': balancerDelegation, 'infinityprotocol-liquidity-pools': infinityProtocolPools, 'aave-governance-power': aaveGovernancePower, cake, aks, - tomyumswap, - 'planet-finance': planetFinance, - 'planet-finance-v2': planetFinancev2, ogn, oolongswap, 'impossible-finance': impossibleFinance, 'immutable-x': immutableX, - badgeth, 'zrx-voting-power': zrxVotingPower, 'tomb-finance': tombFinance, 'tranche-staking-slice': trancheStakingSLICE, - 'unipool-same-token': unipoolSameToken, 'unipool-univ2-lp': unipoolUniv2Lp, 'unipool-xsushi': unipoolXSushi, 'taraxa-delegation': taraxaDelegation, @@ -685,11 +591,9 @@ const strategies = { 'l2-deversifi': l2Deversifi, 'vested-deversifi': vestedDeversifi, biswap, - cronaswap, honeyswap, 'egl-vote': eglVote, 'mcn-farm': mcnFarm, - snowswap, meebitsdao, 'crucible-erc20-balance-of': crucibleERC20BalanceOf, 'erc20-token-and-lp-weighted': erc20TokenAndLpWeighted, @@ -701,40 +605,26 @@ const strategies = { 'sunrisegaming-staking': sunriseGamingStaking, 'single-staking-autocompound-balanceof': singleStakingAutoCompoundBalanceOf, 'single-staking-pools-balanceof': singleStakingPoolsBalanceOf, - 'hopr-staking': hoprStaking, - 'hopr-staking-s2': hoprStakingS2, - 'hopr-staking-by-season': hoprStakingBySeason, 'hopr-stake-and-balance-qv': hoprStakeAndBalanceQV, 'hopr-bridged-balance': hoprBridgedBalance, 'occ-stake-of': occStakeOf, - swapr, 'holds-tokens': holdsTokens, 'loot-character-guilds': lootCharacterGuilds, - cyberkongz: cyberkongz, - 'cyberkongz-v2': cyberkongzV2, - 'cyberkongz-v3': cyberkongzV3, 'comp-like-votes-inclusive': compLikeVotesInclusive, mstable, 'hashes-voting': hashesVoting, - 'hashflow-governance-power': hashflowGovernancePower, 'hashflow-vehft': hashflowVeHft, - 'pod-leader': podLeader, 'aavegotchi-wagmi-guild': aavegotchiWagmiGuild, 'polis-balance': polisBalance, - 'vault-token-lp-balance': vaultTokenLpBalance, - 'single-staking-vault-balanceof': singleStakingVaultBalanceOf, 'mutant-cats-stakers-and-holders': mutantCatsStakersAndHolders, 'razor-network-voting': razorVoting, - 'svs-staking': svsStaking, 'mcb-balance-from-graph': mcbBalanceFromGraph, - 'radicle-community-tokens': radicleCommunityTokens, - 'digitalax-mona-quickswap': digitalaxMonaQuickswap, - 'digitalax-deco-to-mona': digitalaxDecoToMona, 'digitalax-genesis-contribution': digitalaxGenesisContribution, 'digitalax-lp-stakers': digitalaxLPStakers, 'digitalax-mona-stakers-matic': digitalaxMonaStakersMatic, 'digitalax-lp-stakers-matic': digitalaxLPStakersMatic, 'colony-reputation': colonyReputation, + 'digitalax-mona-quickswap': digitalaxMonaQuickswap, 'galaxy-nft-with-score': galaxyNftWithScore, 'galxe-loyalty-points': galxeLoyaltyPoints, 'gatenet-total-staked': gatenetTotalStaked, @@ -745,37 +635,26 @@ const strategies = { coinswap, dgenesis, 'vote-power-and-share': votePowerAndShare, - 'blockzerolabs-cryptonauts': blockzerolabsCryptonauts, math, 'push-voting-power': pushVotingPower, 'staked-psp-balance': stakedPSPBalance, 'erc20-balance-of-contract-multiplier': erc20BalanceOfContractMultiplier, - agave, juicebox, 'snet-farmers': snetFarmers, 'snet-stakers': snetStakers, 'snet-liquidity-providers': snetLiquidityProviders, - 'minmax-mcn-farm': minMaxMcnFarm, 'unstackedtoadz-and-stackedtoadz-stakers': unstackedToadzAndStackedToadzStakers, - 'jade-smrt': jadeSmrt, 'ocean-dao-brightid': oceanDAOBrightID, - 'saddle-finance': saddleFinance, - 'saddle-finance-v2': saddleFinanceV2, membership: membership, 'lydia-gov-vault': lydiaGovVault, - 'xkawa-farm': xkawaFarm, 'darkforest-score': darkforestScore, 'orange-reputation-based-voting': orangeReputationBasedVoting, 'orange-reputation-nft-based-voting': orangeReputationNftBasedVoting, 'squid-dao': squidDao, 'botto-dao': bottoDao, genart, - genomesdao, 'path-balance-staked-and-locked': pathBalanceStakedAndLocked, - 'sumami-holders': sUmamiHolders, - zorro, - 'volt-voting-power': voltVotingPower, 'balancer-poolid': balancerPoolid, 'staked-balancer': stakedBalancer, 'staked-uniswap-modifiable': stakedUniswapModifiable, @@ -785,8 +664,8 @@ const strategies = { 'stakers-and-holders': stakersAndHolders, 'banksy-dao': banksyDao, spacey2025: spacey2025, + 'spacefi-blp': spacefiBlp, 'sandman-dao': sandmanDao, - 'ethercats-founder-series': ethercatsFounderSeries, 've-balance-of-at': veBalanceOfAt, 've-ribbon': veRibbon, 've-ribbon-voting-power': veRibbonVotingPower, @@ -794,49 +673,36 @@ const strategies = { revest: revest, 'staking-claimed-unclaimed': stakingClaimedUnclaimed, 'gysr-staking-balance': gysrStakingBalance, - 'gysr-pending-rewards': gysrPendingRewards, 'gysr-lp-staking-balance': gysrLPStakingBalance, 'wanakafarm-staking': wanakafarmStaking, starsharks, 'printer-financial': printerFinancial, 'ethercats-founders-series': ethercatsFoundersSeries, potion, - 'safety-module-bpt-power': safetyModuleBptPower, 'minotaur-money': MinotaurMoney, 'conv-finance': convFinance, 'sd-boost': sdBoost, - 'capitaldao-staking': capitalDaoStaking, - 'erc20-rebase-wrapper': erc20RebaseWrapper, 'wanakafarm-land-ingame': wanakafarmLandIngame, - 'meebitsdao-delegation': meebitsDaoDelegation, 'starcatchers-top-window': starcatchersTopWindow, gno: gno, 'gno-vote-weight': gno, - 'uma-voting': umaVoting, 'masterchef-pool-balance-no-rewarddebt': masterchefPoolBalanceNoRewarddebt, 'proof-of-humanity': proofOfHumanity, 'sybil-protection': sybilProtection, 'samurailegends-generals-balance': samuraiLegendsGeneralsBalance, 'dogs-unchained': dogsUnchained, - 'stakedao-governance-update': stakeDAOGovernanceUpdate, 'umami-voting': umamiVoting, 'liquidity-token-provide': liquidityTokenProvide, 'gamium-voting': gamiumVoting, 'citydao-square-root': citydaoSquareRoot, 'rowdy-roos': rowdyRoos, hedgey, - 'hedgey-multi': hedgeyMulti, 'hedgey-delegate': hedgeyDelegate, 've-balance-of-at-nft': veBalanceOfAtNFT, 'genzees-from-subgraph': genzeesFromSubgraph, - 'gin-finance': ginFinance, 'position-governance-power': positionGovernancePower, 'credit-lp': creditLp, helix, - 'arrakis-finance': arrakisFinance, - 'aura-vlaura-vebal': auraFinance, - 'aura-vlaura-vebal-with-overrides': auraFinanceWithOverrides, - 'aura-balance-of-vlaura-vebal': auraBalanceOfVlauraVebal, 'aura-vault-balance-of-single-asset': auraBalanceOfSingleAsset, 'rocketpool-node-operator': rocketpoolNodeOperator, 'rocketpool-node-operator-v2': rocketpoolNodeOperatorv2, @@ -844,34 +710,21 @@ const strategies = { 'earthfund-child-dao-staking-balance': earthfundChildDaoStakingBalance, 'sd-boost-twavp': sdBoostTWAVP, 'unipilot-vault-pilot-balance': unipilotVaultPilotBalance, - 'solv-voucher-claimable': solvVoucherClaimable, 'balance-of-with-linear-vesting-power': balanceOfWithLinearVestingPower, 'linear-vesting-power': linearVestingPower, - apeswap, - h2o, - dopamine, 'lrc-l2-subgraph-balance-of': lrcL2SubgraphBalanceOf, 'lrc-l2-nft-balance-of': lrcL2NftBalanceOf, 'lrc-lp-subgraph-balance-of': lrcLPSubgraphBalanceOf, - 'lrc-nft-dao-search': lrcNFTDAOSearch, 'lrc-nft-search-mult': lrcNFTmult, - 'rari-fuse': rariFuse, 'bancor-pool-token-underlying-balance': bancorPoolTokenUnderlyingBalance, - selfswap, - 'erc3525-vesting-voucher': erc3525VestingVoucher, - 'xrook-balance-of-underlying-weighted': xrookBalanceOfUnderlyingWeighted, - 'orbs-network-delegation': orbsNetworkDelegation, 'balance-of-subgraph': balanceOfSubgraph, 'wagdie-subgraph': wagdieSubgraph, 'erc721-pair-weights': erc721PairWeights, 'harmony-staking': harmonyStaking, - 'echelon-cached-erc1155-decay': echelonCachedErc1155Decay, 'erc3525-flexible-voucher': erc3525FlexibleVoucher, 'orca-pod': orcaPod, 'metropolis-pod': metropolisPod, - 'proxyprotocol-erc20-balance-of': proxyProtocolErc20BalanceOf, 'proxyprotocol-erc721-balance-of': proxyProtocolErc721BalanceOf, - 'proxyprotocol-erc1155-balance-of': proxyProtocolErc1155BalanceOf, 'posichain-staking': posichainStaking, 'posichain-total-balance': posichainTotalBalance, 'arrow-vesting': arrowVesting, @@ -879,38 +732,27 @@ const strategies = { 'fight-club': fightClub, 'tpro-staking': tproStaking, 'safe-vested': safeVested, - 'riskharbor-underwriter': riskharborUnderwriter, 'otterspace-badges': otterspaceBadges, 'synthetic-nouns-with-claimer': syntheticNounsClaimerOwner, 'echelon-wallet-prime-and-cached-key': echelonWalletPrimeAndCachedKey, 'nation3-votes-with-delegations': nation3VotesWIthDelegations, 'nation3-passport-coop-with-delegations': nation3CoopPassportWithDelegations, - 'aavegotchi-agip-37-wap-ghst': aavegotchiAgip37WapGhst, - 'aavegotchi-agip-37-gltr-staked-lp': aavegotchiAgip37GltrStakedLp, 'erc20-tokens-per-uni': erc20TokensPerUni, 'bancor-standard-rewards-underlying-balance': bancorStandardRewardsUnderlyingBalance, 'sd-vote-boost': sdVoteBoost, 'sd-vote-boost-twavp': sdVoteBoostTWAVP, - 'clqdr-balance-with-lp': clqdrBalanceWithLp, spreadsheet, 'offchain-delegation': offchainDelegation, 'ninechronicles-staked-and-dcc': ninechroniclesStakedAndDcc, - 'dsla-parametric-staking-service-credits': - dslaParametricStakingServiceCredits, 'rep3-badges': rep3Badges, marsecosystem, 'ari10-staking-locked': ari10StakingLocked, - 'multichain-serie': multichainSerie, - 'ctsi-staking': ctsiStaking, - 'ctsi-staking-pool': ctsiStakingPool, 'skale-delegation-weighted': skaleDelegationWeighted, reliquary, - 'vsta-pool-staking': vstaPoolStaking, 'jpegd-locked-jpeg-of': jpegdLockedJpegOf, 'lodestar-vesting': lodestarVesting, 'lodestar-staked-lp': lodestarStakedLp, - babywealthyclub, 'battlefly-vgfly-and-staked-gfly': battleflyVGFLYAndStakedGFLY, 'nexon-army-nft': nexonArmyNFT, 'moonbeam-free-balance': moonbeamFreeBalance, @@ -918,8 +760,6 @@ const strategies = { 'psp-in-sepsp2-balance': pspInSePSP2Balance, 'pdn-balances-and-vests': pdnBalancesAndVests, 'lqty-proxy-stakers': lqtyProxyStakers, - 'echelon-wallet-prime-and-cached-key-gated': - echelonWalletPrimeAndCachedKeyGated, 'rdnt-capital-voting': rdntCapitalVoting, 'staked-defi-balance': stakedDefiBalance, 'degenzoo-erc721-animals-weighted': degenzooErc721AnimalsWeighted, @@ -941,10 +781,53 @@ const strategies = { 'friend-tech': friendTech, 'sd-vote-boost-twavp-v2': sdVoteBoostTWAVPV2, 'sd-vote-boost-twavp-v3': sdVoteBoostTWAVPV3, + 'sd-vote-boost-twavp-vsdtoken': sdVoteBoostTWAVPVsdToken, + 'sd-vote-boost-twavp-balanceof': sdVoteBoostTWAVPBalanceof, moonbase: moonbase, 'dss-vest-unpaid': dssVestUnpaid, 'dss-vest-balance-and-unpaid': dssVestBalanceAndUnpaid, - 'eoa-balance-and-staking-pools': eoaBalanceAndStakingPools + 'eoa-balance-and-staking-pools': eoaBalanceAndStakingPools, + 'station-score-if-badge': stationScoreIfBadge, + 'station-constant-if-badge': stationConstantIfBadge, + 'mangrove-station-qv-scaled-to-mgv': mangroveStationQVScaledToMGV, + floki, + 'hats-protocol-hat-id': hatsProtocolHatId, + 'hats-protocol-hat-ids': hatsProtocolHatIds, + 'bubblegum-kids': bubblegumKids, + 'clipper-staked-sail': clipperStakedSail, + plearn, + snote, + streamr, + 'aavegotchi-agip-17': aavegotchiAgip17, + 'aavegotchi-agip-37-gltr-staked-lp': aavegotchiAgip37GltrStakedLp, + 'aavegotchi-agip-37-wap-ghst': aavegotchiAgip37WapGhst, + agave, + 'arrakis-finance': arrakisFinance, + 'ctsi-staking-pool': ctsiStakingPool, + 'cyberkongz-v2': cyberkongzV2, + 'dextf-staked-in-vaults': dextfStakedInVaults, + genomesdao, + 'goldfinch-membership': goldfinchMembership, + 'goldfinch-voting-power': goldfinchVotingPower, + h2o, + 'hopr-staking-by-season': hoprStakingBySeason, + 'hopr-staking-s2': hoprStakingS2, + ilv, + 'meebitsdao-delegation': meebitsdaoDelegation, + 'modefi-staking': modefiStaking, + 'orbs-network-delegation': orbsNetworkDelegation, + 'planet-finance-v2': planetFinanceV2, + 'rari-fuse': rariFuse, + 'synthetix-non-quadratic_1': synthetixNonQuadratic_1, + 'synthetix-quadratic': synthetixQuadratic, + 'synthetix-quadratic_1': synthetixQuadratic_1, + synthetix_1, + 'total-axion-shares': totalAxionShares, + 'unipool-same-token': unipoolSameToken, + 'vendor-v2-borrower-collateral-balance-of': + vendorV2BorrowerCollateralBalanceOf, + 'volt-voting-power': voltVotingPower, + 'xdai-stakers-and-holders': xdaiStakersAndHolders }; Object.keys(strategies).forEach(function (strategyName) { diff --git a/src/strategies/iotex-balance/examples.json b/src/strategies/iotex-balance/examples.json deleted file mode 100644 index 7fa6bbe80..000000000 --- a/src/strategies/iotex-balance/examples.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "iotex balance query", - "strategy": { - "name": "iotex-balance", - "params": { - "address": "0x1904BFcb93EdC9BF961Eead2e5c0de81dCc1D37D", - "symbol": "IOTX", - "decimals": 18 - } - }, - "network": "4689", - "addresses": [ - "0x515c2c35c3ec82c30affc5ec06da9a30ef008741", - "0xa00744882684c3e4747faefd68d283ea44099d03" - ], - "snapshot": 13000000 - } -] diff --git a/src/strategies/iotex-balance/index.ts b/src/strategies/iotex-balance/index.ts deleted file mode 100644 index 0ecf21b25..000000000 --- a/src/strategies/iotex-balance/index.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { strategy as EthBalanceStrategy } from '../eth-balance'; -import fetch from 'cross-fetch'; -interface ApiReturn { - balance: string[]; -} - -export const author = 'iotexproject'; -export const version = '0.0.2'; - -const testNetUrl = 'https://analyser-api.testnet.iotex.io'; -const mainNetUrl = 'https://analyser-api.iotex.io'; - -function getUrl(network) { - return network == 4689 ? mainNetUrl : testNetUrl; -} - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - if (blockTag == 'latest') - return EthBalanceStrategy( - space, - network, - provider, - addresses, - options, - snapshot - ); - - const apiUrl = getUrl(network); - const response = await fetch( - `${apiUrl}/api.AccountService.IotexBalanceByHeight`, - { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - address: addresses, - height: snapshot - }) - } - ); - - const ret: ApiReturn = await response.json(); - return Object.fromEntries( - ret.balance.map((v, i) => [addresses[i], parseFloat(v)]) - ); -} diff --git a/src/strategies/jade-smrt/README.md b/src/strategies/jade-smrt/README.md deleted file mode 100644 index 0126da0f3..000000000 --- a/src/strategies/jade-smrt/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# jade-smrt - -This is a strategy for counting: -- JADE and sJADE token held on BSC -- SMRT and SMRTR token held on Avalanche c-chain (including the ones in -the SMRTR/AVAX pool), which are then weighted by the price_SMRT/price_JADE -and price_SMRTR/price_JADE \ No newline at end of file diff --git a/src/strategies/jade-smrt/examples.json b/src/strategies/jade-smrt/examples.json deleted file mode 100644 index 87af5aa7a..000000000 --- a/src/strategies/jade-smrt/examples.json +++ /dev/null @@ -1,73 +0,0 @@ -[ - { - "name": "Jade-Smrt multichain voting", - "strategy": { - "name": "jade-smrt", - "params": { - "symbol": "jade-smrt", - - "JADE": { - "network": "56", - "address": "0x7ad7242A99F21aa543F9650A56D141C57e4F6081", - "decimals": 9 - }, - - "SJADE": { - "network": "56", - "address": "0x94CEA04C51E7d3EC0a4A97Ac0C3B3c9254c2aD41", - "decimals": 9 - }, - - "JADELP": { - "network": "56", - "address": "0x46503d91D7a41FCbDC250E84ceE9D457d082D7b4", - "decimals": 18 - }, - - "SMRTR": { - "network": "43114", - "address": "0x6D923f688C7FF287dc3A5943CAeefc994F97b290", - "decimals": 18 - }, - - "SMRT": { - "network": "43114", - "address": "0xCC2f1d827b18321254223dF4e84dE399D9Ff116c", - "decimals": 18 - }, - - "SMRTLP": { - "network": "43114", - "address": "0xf070843Ba9ed0ab85B0d15f9E8D67A5A8E073254", - "decimals": 18 - }, - - "SMRTRLP": { - "network": "43114", - "address": "0x7b7617c7b2236d7871741783cae8bcc222c2e05d", - "decimals": 18 - }, - - "avaxGraph": "https://api.thegraph.com/subgraphs/name/elkfinance/avax-blocks", - - "startBlocks": { - "56": 12858840, - "43114": 7639389 - } - } - }, - - "network": "56", - "addresses": [ - "0x422Cb45F4DCB3f798B53835F8671288D424d881F", - "0x0E916175eFa7633b5703cb9c50A99602e6c65530", - "0xbDe951E26aae4F39e20628724f58293A4E6457D4", - "0xA932857fA2Dbf18A8EeB50e359E99EDD40E27F7f", - "0xe0d7069685AF2F940D60685d93673Ee1141473C4", - "0xB0354be8EDD26d154dCf10BE3c47C88Ee6150DDB", - "0x20c204adad9C991ED35ce2778e705439227EA406", - "0x7AaCE7fb8CD4CEC3588F4a0b0A0DBdd470b20FbD" - ], - "snapshot": 13350104 - } -] diff --git a/src/strategies/jade-smrt/index.ts b/src/strategies/jade-smrt/index.ts deleted file mode 100644 index 8596b4c53..000000000 --- a/src/strategies/jade-smrt/index.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { subgraphRequest } from '../../utils'; -import { Multicaller, getProvider } from '../../utils'; -import { formatUnits } from '@ethersproject/units'; - -export const author = 'drgorillamd'; -export const version = '1.0.0'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function totalSupply() external view returns (uint256)' -]; - -const BUSD = '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56'; -const WAVAX = '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7'; -const USDC = '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664'; -const WAVAXUSDC = '0xA389f9430876455C36478DeEa9769B7Ca4E3DDB1'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const block = await provider.getBlock(blockTag); - const timestamp = block.timestamp; - const avaxBlockTag = await getAvaxBlockTag(timestamp, options); - - // BSC balances: - const multiBsc = new Multicaller(network, provider, abi, { blockTag }); - addresses.forEach((address: string) => { - multiBsc.call(address + '-jade', options.JADE.address, 'balanceOf', [ - address - ]); - multiBsc.call(address + '-sjade', options.SJADE.address, 'balanceOf', [ - address - ]); - }); - - // BUSD per JADE spot - pick CAREFULLY the block height, it is NOT a twap - // as a twap would require 2 additionnal multicalls (and therefore be above the Snapshot 5 calls limit) - multiBsc.call('jadeLPBalance', options.JADE.address, 'balanceOf', [ - options.JADELP.address - ]); // jade balance in jade-busd pool - multiBsc.call('busdLPBalance', BUSD, 'balanceOf', [options.JADELP.address]); // BUSD balance in jade-busd pool - - // Avax balances: - const multiAvax = new Multicaller('43114', getProvider('43114'), abi, { - blockTag: avaxBlockTag - }); - addresses.forEach((address: string) => { - multiAvax.call(address + '-smrt', options.SMRT.address, 'balanceOf', [ - address - ]); - multiAvax.call(address + '-smrtR', options.SMRTR.address, 'balanceOf', [ - address - ]); - multiAvax.call(address + '-smrtRLp', options.SMRTRLP.address, 'balanceOf', [ - address - ]); - }); - - // WAVAX per SMRT spot - multiAvax.call('smrtLPBalance', options.SMRT.address, 'balanceOf', [ - options.SMRTLP.address - ]); // SMRT in SMRT/WAVAX pool balance - multiAvax.call('wavaxSmrtLPBalance', WAVAX, 'balanceOf', [ - options.SMRTLP.address - ]); // wavax in SMRT/WAVAX pool balance - - // WAVAX per SMRTR spot - multiAvax.call('smrtRLPBalance', options.SMRTR.address, 'balanceOf', [ - options.SMRTRLP.address - ]); - multiAvax.call('wavaxSmrtRLPBalance', WAVAX, 'balanceOf', [ - options.SMRTRLP.address - ]); // SMRT SMRT/WAVAX pool balance - - // USD per WAVAX spot - multiAvax.call('UsdLPBalance', USDC, 'balanceOf', [WAVAXUSDC]); // SMRT SMRT/WAVAX pool balance - multiAvax.call('wavaxUsdLPBalance', WAVAX, 'balanceOf', [WAVAXUSDC]); // SMRT SMRT/WAVAX pool balance - - // Avax SMRTR/WAVAX pool: LP token total supply - multiAvax.call('smrtRLPSupply', options.SMRTRLP.address, 'totalSupply', []); - - let resBsc: Record | number = { 0: 0 }, - resAvax: - | [number, number, number, Record, Record] - | number = [0, 0, 0, { 0: 0 }, { 0: 0 }]; - - [resBsc, resAvax] = await Promise.all([ - multiBsc.execute(), - multiAvax.execute() - ]); - - // All prices in USDish (BUSD or USDC.e) - const jadePrice: number = - parseFloat(formatUnits(resBsc['busdLPBalance'], 18)) / - parseFloat(formatUnits(resBsc['jadeLPBalance'], 9)); - const wavaxPrice: number = - parseFloat(formatUnits(resAvax['UsdLPBalance'], 6)) / - parseFloat(formatUnits(resAvax['wavaxUsdLPBalance'], 18)); - const smrtPrice: number = - wavaxPrice / - (parseFloat(formatUnits(resAvax['smrtLPBalance'], 18)) / - parseFloat(formatUnits(resAvax['wavaxSmrtLPBalance'], 18))); - const smrtRPrice: number = - wavaxPrice / - (parseFloat(formatUnits(resAvax['smrtRLPBalance'], 18)) / - parseFloat(formatUnits(resAvax['wavaxSmrtRLPBalance'], 18))); - const smrtRLPBalance: number = parseFloat( - formatUnits(resAvax['smrtRLPBalance'], 18) - ); - const smrtRLPSupply: number = parseFloat( - formatUnits(resAvax['smrtRLPSupply'], 18) - ); - - return Object.fromEntries( - addresses.map((adr: string) => { - let bal = parseFloat( - formatUnits(resBsc[adr + '-jade'], options.JADE.decimals) - ); - bal += parseFloat( - formatUnits(resBsc[adr + '-sjade'], options.SJADE.decimals) - ); - - // SMRT balance * SMRT price/JADE price: - const parsedSmrt = parseFloat( - formatUnits(resAvax[adr + '-smrt'], options.SMRT.decimals) - ); - bal += (parsedSmrt * smrtPrice) / jadePrice; - - // SMRTR balance * SMRTR price/JADE price: - const parsedSrmtr = parseFloat( - formatUnits(resAvax[adr + '-smrtR'], options.SMRTR.decimals) - ); - bal += (parsedSrmtr * smrtRPrice) / jadePrice; - - // LP token held * smrtr pool balance / LP token total supply: - const LPHeld = - (parseFloat(formatUnits(resAvax[adr + '-smrtRLp'], 18)) * - smrtRLPBalance) / - smrtRLPSupply; - bal += (LPHeld * smrtRPrice) / jadePrice; - - return [adr, bal]; - }) - ); -} - -async function getAvaxBlockTag(timestamp: number, options): Promise { - const query = { - blocks: { - __args: { - first: 1, - orderBy: 'number', - orderDirection: 'desc', - where: { - timestamp_lte: timestamp - } - }, - number: true, - timestamp: true - } - }; - const data = await subgraphRequest(options.avaxGraph, query); - return Number(data.blocks[0].number); -} diff --git a/src/strategies/lrc-nft-dao-search/README.md b/src/strategies/lrc-nft-dao-search/README.md deleted file mode 100644 index f7112f189..000000000 --- a/src/strategies/lrc-nft-dao-search/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# lrc-nft-dao-search - -This is an improvement of karamorf's lrc-l2-nft-balance of Snapshot voting strategy by raecaug(system32). -The extended functionality allows space owners to(alongside all of lrc-l2-nft-balance-of's functionality) specify individual nft ids within a token contract with the nft_ids option. -This option is not needed, and if excluded the query will search for all nfts minted under that token contract address. -No other behavior has been changed. - - -Strategy to read account balances for NFTs (72 or 1155) from LoopringV2 subgraph. Assumes we only want tokens minted by a specific account id. - -Here is an example of parameters: - -```json -{ - "graph": "https://api.thegraph.com/subgraphs/name/juanmardefago/loopring36", - "minter_account_id": "74447", - "tokens": ["token (Collection contract address) to include"], - "nft_ids": ["nftIDs, unique to every nft, even those under the same token contract"], - "blacklisted_account_ids": ["38482"], - "blacklisted_nft_ids": ["... nft id's to exclude ..."] -} -``` - -Use explorer.loopring.io to look up addresses and find account id's. - -Account id `38482` maps to `0x000000000000000000000000000000000000dead` and is used for burning tokens. - -to note: either the `minter_account_id` or the `tokens` parameter must be provided for this query to work. You do not need to specify both, just one of them. diff --git a/src/strategies/lrc-nft-dao-search/examples.json b/src/strategies/lrc-nft-dao-search/examples.json deleted file mode 100644 index a944db039..000000000 --- a/src/strategies/lrc-nft-dao-search/examples.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "name": "LoopringDAOSearch", - "strategy": { - "name": "lrc-nft-dao-search", - "params": { - "graph": "https://api.thegraph.com/subgraphs/name/juanmardefago/loopring36", - "minter_account_id": "157510", - "tokens": ["0xb6d91e38e4ac53c9f8952c6c6b1c7aee66c8b6f0"], - "nft_ids": [ - "0x1e31297dd163ca44a5fad74de4ffbebf1ba11d46e1b448b0e105449d827fb264" - ], - "blacklisted_account_ids": [""], - "blacklisted_nft_ids": [""] - } - }, - "network": "1", - "addresses": ["0xeE253D3fCC30787a1E58570E355010d0b9C33B60"], - "snapshot": 15677787 - } -] diff --git a/src/strategies/lrc-nft-dao-search/index.ts b/src/strategies/lrc-nft-dao-search/index.ts deleted file mode 100644 index a18cba427..000000000 --- a/src/strategies/lrc-nft-dao-search/index.ts +++ /dev/null @@ -1,126 +0,0 @@ -// Original code by Karamorf, upgraded by Raecaug(system32) -// Allows querys of Loopring L2 accounts & balances by specifying a nft minter, token contract address(and optionally specifying individual ids to white/blacklist) - -import { subgraphRequest } from '../../utils'; -export const author = 'raecaug'; -export const version = '0.1.2'; - -const LIMIT = 1000; - -function makeQuery( - snapshot, // This is an Ethereum block # or defaults to 'latest' - minter, // This is referred to as account # or account id on the Loopring L2 block explorer - tokens, // NFT collection contract addresses, also referred to as 'token address' - skip, // Used to skip response lines in requests - blacklisted_account_ids, // Ditto properties of 'minter' - blacklisted_nft_ids, // This is the nft id, which is unique for every nft ever minted, allows distinction between nfts in a collection at the chain level - nft_ids // Ditto properties of blacklisted version -) { - const query: any = { - // Query constructor, builds request with params from snapshot space settings - accountNFTSlots: { - __args: { - where: { - nft_: { - id_not_in: blacklisted_nft_ids, // Excluding blacklisted nft ids - nftID_in: nft_ids // Including uniquely specified nft ids - }, - account_not_in: blacklisted_account_ids // Excluding blacklisted account ids - }, - first: LIMIT, - skip: skip - }, - account: { address: true }, - balance: true - } - }; - - if (minter && minter !== '') { - //Check to ensure minter id is specified and not blank - query.accountNFTSlots.__args.where.nft_.minter = minter; - } - - if (tokens && tokens.length > 0) { - //Check to ensure at least 1 token to search for is specified - query.accountNFTSlots.__args.where.nft_.token_in = tokens; - } - - if (snapshot !== 'latest') { - // If the snapshot date is manually specified, overwrite the 'latest' block, strict inequality check operand used - query.accountNFTSlots.__args = { - ...query.accountNFTSlots.__args, - block: { - number: snapshot - } - }; - } - - return query; -} - -export async function strategy( // *****Logical execution begins here; args passed in by Snapshot settings***** - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - let blacklisted_account_ids = options.blacklisted_account_ids; - let blacklisted_nft_ids = options.blacklisted_nft_ids; - - let nft_ids = options.nft_ids; // Unique NFT ids, distinguishable from 1155 token contracts - - const balances = {}; // Initialization - let skip = 0; - let response_size = 0; - - if (!blacklisted_account_ids || blacklisted_account_ids.length === 0) { - // If no blacklisted accts specified, set to empty - blacklisted_account_ids = ['']; - } - - if (!blacklisted_nft_ids || blacklisted_nft_ids.length === 0) { - // If no unique nft_ids specified, set to empty - blacklisted_nft_ids = ['']; - } - - if (!nft_ids || nft_ids.length === 0) { - // If no unique nft_ids specified, set to empty - nft_ids = ['']; - } - - do { - // Transmit query and await results - const response = await subgraphRequest( - // Constructs response variable from subgraph query function - options.graph, // Parameter 1, options specified - makeQuery( - // Query constructor(defined above) called, results are the second parameter - snapshot, - options.minter_account_id, - options.tokens, - skip, - blacklisted_account_ids, - blacklisted_nft_ids, - nft_ids - ) - ); - - response.accountNFTSlots.forEach((slot) => { - // Checking against each accountNFTSlot element - if (!balances.hasOwnProperty(slot.account.address)) { - balances[slot.account.address] = 0; // If nothing returned, set this accounts balance to 0 - } - balances[slot.account.address] += parseInt(slot.balance); // Otherwise, a bigint is returned, parse it and store in balances array - }); - response_size = response.accountNFTSlots.length; // Value is set to 0 on loop entry, updated here, will break loop for anything other than 1000 - skip += response_size; - } while (response_size == LIMIT); - - const scores = Object.fromEntries( - addresses.map((address) => [address, balances[address.toLowerCase()]]) // Map returned addresses and balances as scores array - ); - - return scores; // Returns addresses and balances to Snapshot -} diff --git a/src/strategies/mangrove-station-qv-scaled-to-mgv/README.md b/src/strategies/mangrove-station-qv-scaled-to-mgv/README.md new file mode 100644 index 000000000..1dbdf5130 --- /dev/null +++ b/src/strategies/mangrove-station-qv-scaled-to-mgv/README.md @@ -0,0 +1,10 @@ +# mangrove-station-qv-scaled-to-mgv + +This strategy allows active members of one of the Mangrove DAO governance groups (Builders Group and Pods Group) to vote in the Mangrove multistakeholder governance. + +Quadratic voting is applied to each member's score and then the voting weight is scaled such that the total voting weight of the group is equal to the circulating supply of MGV tokens. This ensures that the three stakeholder groups have equal voting weights. + +## Notes +Mangrove DAO governance groups are [Station](https://www.station.express/) [GroupOS](https://groupos.xyz/) groups on-chain, and active members have a ERC-1155 badge. + +The circulating supply of MGV tokens is the total number of allocated, claimable tokens. diff --git a/src/strategies/mangrove-station-qv-scaled-to-mgv/examples.json b/src/strategies/mangrove-station-qv-scaled-to-mgv/examples.json new file mode 100644 index 000000000..dbe07b91b --- /dev/null +++ b/src/strategies/mangrove-station-qv-scaled-to-mgv/examples.json @@ -0,0 +1,28 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "mangrove-station-qv-scaled-to-mgv", + "params": { + "membershipERC721": "0x33dbde2e093b7cf8446d9ac0de79220d42423501", + "badgesERC1155": "0xd1502a7659eaad60278ae3ef27edea849504f4da", + "activeBadgeId": 1, + "activityScoreERC20": "0x5f120453dfd0c55f55370d1f718089ae0fcf6387", + "activityScoreDecimals": 18, + "erc6551Registry": "0x000000006551c19487814612e58FE06813775758", + "erc6551Implementation": "0x509b531c8e979c85375370c0ba92ac44173c2d12", + "erc6551Salt": "0x0000000000000000000000000000000000000000000000000000000000000000", + "dssVestAddress": "0x370F850180FDDCdc521Ed11900a7a27D08B2d402", + "dssVestDecimals": 18 + } + }, + "network": "1", + "addresses": [ + "0x5A8CA059a7e185ad01695136dbe5A721D4e053D6", + "0x1ba671ce4718d76D42609affbB567Bf1E71E0852", + "0x008B1FFe244bF31896C833bFf4Ad9009E7A0f4Eb", + "0x1b515D521dd0CBA174Bee666ED8f42449C8cA8bb" + ], + "snapshot": 18779365 + } +] diff --git a/src/strategies/mangrove-station-qv-scaled-to-mgv/index.ts b/src/strategies/mangrove-station-qv-scaled-to-mgv/index.ts new file mode 100644 index 000000000..059666c00 --- /dev/null +++ b/src/strategies/mangrove-station-qv-scaled-to-mgv/index.ts @@ -0,0 +1,95 @@ +import { + getAllMembers, + fetchBadgeBalances, + fetchScores +} from '../station-score-if-badge'; +import { getAllVestings } from '../dss-vest-unpaid'; + +export const author = 'espendk'; +export const version = '1.0.1'; + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +): Promise> { + const members = await getAllMembers( + network, + provider, + snapshot, + options.membershipERC721, + options.erc6551Registry, + options.erc6551Implementation, + options.erc6551Salt + ); + + await fetchBadgeBalances( + network, + provider, + snapshot, + members, + options.badgesERC1155, + options.activeBadgeId + ); + + // Keep only TBAs and members with a badge + for (const [, member] of members) { + member.TBAs = member.TBAs.filter((tba) => (tba.badgeBalance ?? 0) > 0); + if (member.TBAs.length === 0) { + members.delete(member.address); + } + } + + await fetchScores( + network, + provider, + snapshot, + members, + options.activityScoreERC20, + options.activityScoreDecimals + ); + + // Aggregate membership scores and apply quadratic voting + const activeMemberScores = new Map(); + let totalScore = 0; + for (const [, member] of members) { + let score = 0; + for (const tba of member.TBAs) { + score += tba.score ?? 0; + } + score = Math.sqrt(score); + activeMemberScores.set(member.address, score); + totalScore += score; + } + + // Get the circulating supply of the vesting token + const allVestings = await getAllVestings( + network, + provider, + snapshot, + options.dssVestAddress, + options.dssVestDecimals + ); + const circulatingSupply = allVestings.reduce( + (acc, vesting) => acc + vesting.accrued, + 0 + ); + + // Scale scores to the circulating supply of the vesting token + const scale = circulatingSupply / totalScore; + for (const [address, score] of activeMemberScores) { + activeMemberScores.set(address, score * scale); + } + + // Build address -> scaled score for the queried addresses + const result = {}; + for (const [address, score] of activeMemberScores) { + if (addresses.includes(address)) { + result[address] = score; + } + } + return result; +} diff --git a/src/strategies/mangrove-station-qv-scaled-to-mgv/schema.json b/src/strategies/mangrove-station-qv-scaled-to-mgv/schema.json new file mode 100644 index 000000000..fa70a4561 --- /dev/null +++ b/src/strategies/mangrove-station-qv-scaled-to-mgv/schema.json @@ -0,0 +1,89 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/Strategy", + "definitions": { + "Strategy": { + "title": "Strategy", + "type": "object", + "properties": { + "membershipERC721": { + "type": "string", + "title": "Membership ERC721 contract address", + "examples": ["e.g. 0x651bf9C1A1dEC27b49061F2356482F7c6F3D18fb"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "badgesERC1155": { + "type": "string", + "title": "Badges ERC1155 contract address", + "examples": ["e.g. 0xd775e55e314164cce7f71f9f70fc905c907fc65e"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "activeBadgeId": { + "type": "number", + "title": "ERC115 token ID for the Active Badge that indicate current members", + "examples": ["e.g. 1"], + "minimum": 0 + }, + "activityScoreERC20": { + "type": "string", + "title": "Member score ERC20 contract address", + "examples": ["e.g. 0x30D602cBfe96FC2C83fF31Bdf79d48De65f80733"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "activityScoreDecimals": { + "type": "number", + "title": "Member score decimals", + "examples": ["e.g. 18"], + "minimum": 0 + }, + "erc6551Registry": { + "type": "string", + "title": "ERC6551 registry address", + "examples": ["e.g. 0x02101dfB77FDE026414827Fdc604ddAF224F0921"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "erc6551Implementation": { + "type": "string", + "title": "ERC6551 implementation address", + "examples": ["e.g. 0x2d25602551487c3f3354dd80d76d54383a243358"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "erc6551Salt": { + "type": "string", + "title": "ERC6551 salt", + "examples": [ + "e.g. 0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "pattern": "^0x[a-fA-F0-9]{64}$", + "minLength": 66, + "maxLength": 66 + }, + "dssVestAddress": { + "type": "string", + "title": "DssVest contract address", + "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "dssVestDecimals": { + "type": "number", + "title": "Decimals", + "examples": ["e.g. 18"] + } + }, + "required": [], + "additionalProperties": false + } + } +} diff --git a/src/strategies/minmax-mcn-farm/README.md b/src/strategies/minmax-mcn-farm/README.md deleted file mode 100644 index f48af959d..000000000 --- a/src/strategies/minmax-mcn-farm/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# minmax-mcn-farm - -This strategy returns the total amount of user-owned MAX tokens rewards, including the token in wallet, staked token and pending rewards held by the farm contract. - -## Examples - -Rhe space config will look like this: - -```JSON -{ - "tokenAddress": "0xe45d95a66cfF6aB5E9b796CF5A36F0669AF3Ec98", - "lpAddress": "0x88137f2a610693e975b17d7cf940bf014cf0f325", - "stakingAddress": "0xf3a640eeb661cdf78f1817314123e8bbd12e191f" -} -``` diff --git a/src/strategies/minmax-mcn-farm/examples.json b/src/strategies/minmax-mcn-farm/examples.json deleted file mode 100644 index 6c46be2f7..000000000 --- a/src/strategies/minmax-mcn-farm/examples.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "minmax-mcn-farm", - "params": { - "tokenAddress": "0xe45d95a66cfF6aB5E9b796CF5A36F0669AF3Ec98", - "symbol": "MAX", - "decimals": "18", - "lpAddress": "0x88137f2a610693e975b17d7cf940bf014cf0f325", - "stakingAddress": "0xf3a640eeb661cdf78f1817314123e8bbd12e191f" - } - }, - "network": "4689", - "addresses": [ - "0x207dCfFf0bF605e892d7b036617FfF1A9B7a3038", - "0x1c4922142585881d24a0374a26368224974ba730", - "0x9125b2457479964540a0557e3b010681317b635e", - "0x226f272a2635523b9d50a8d2b481ced204b4d9c2", - "0x2a4aa0c965e7903b230036169803d00c886c0d11" - ], - "snapshot": 14629883 - } -] diff --git a/src/strategies/minmax-mcn-farm/index.ts b/src/strategies/minmax-mcn-farm/index.ts deleted file mode 100644 index 420da0604..000000000 --- a/src/strategies/minmax-mcn-farm/index.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { multicall } from '../../utils'; - -export const author = 'LeifuChen'; -export const version = '0.1.0'; - -const FARM_ADDRESS = '0xf3a640eeb661cdf78f1817314123e8bbd12e191f'; -const MAX_ADDRESS = '0xe45d95a66cff6ab5e9b796cf5a36f0669af3ec98'; -const MAX_LP_ADDRESS = '0x88137f2a610693e975b17d7cf940bf014cf0f325'; - -const abi = [ - 'function totalSupply() view returns (uint256)', - 'function balanceOf(address account) view returns (uint256)', - 'function getUser(address _lpToken, address _account) view returns (tuple(uint256 amount, uint256[] rewardsWriteoffs) user, uint256[] rewards)' -]; - -export async function strategy( - space, - network, - provider, - addresses: string[], - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const lpAddress = options.lpAddress || MAX_LP_ADDRESS; - const tokenAddress = options.tokenAddress || MAX_ADDRESS; - const farmAddress = options.stakingAddress || FARM_ADDRESS; - const pools = [ - '0x88137F2a610693E975b17D7Cf940BF014CF0f325', // BUSD_MAX => MAX token in LP - '0xC35257624b01932e521bc5D9dc07e4F9ed21ED28', // minmaxB3 - '0x14D66e676b978255C719B2771c657ACc418Bb9Fa', // minmaxE4 - '0xdFf5DC9d8dAC189324452D54e2df19d2Bdba78CE', // minmaxM3 - '0x425C2c686f12d61ECD4dFD1170214E3BEFEbBe33' // minmaxUSDT - ]; - - const flatten = (arr) => [].concat.apply([], arr); - const product = (...sets) => { - return sets.reduce( - (acc, set) => flatten(acc.map((x) => set.map((y) => [...x, y]))), - [[]] - ); - }; - const params = product(pools, addresses); - const res = await multicall( - network, - provider, - abi, - [ - [lpAddress, 'totalSupply', []], - [tokenAddress, 'balanceOf', [lpAddress]] - ] - .concat(addresses.map((p) => [lpAddress, 'balanceOf', [p]])) - .concat(addresses.map((p) => [tokenAddress, 'balanceOf', [p]])) - .concat(params.map((p) => [farmAddress, 'getUser', p])), - { blockTag } - ); - - const values = {}; - Object.values(addresses).forEach((address: string) => (values[address] = 0)); - - // MAX token in user's wallet - const walletInfo = res.slice(2 + addresses.length, 2 + addresses.length * 2); - for (let i = 0; i < addresses.length; i++) { - values[addresses[i]] += walletInfo[i] / 10 ** 18; - } - - // MAX token in pendingRewards - const poolInfo = res.slice(2 + addresses.length * 2); - poolInfo.forEach(({ 1: reward }, i) => { - values[addresses[i % addresses.length]] += reward / 10 ** 18; - }); - - // MAX token in MAX_BUSD LP (MCN Farm) - const [totalSupply] = res[0]; - const [tokenBalanceInLP] = res[1]; - const tokensPerLP = tokenBalanceInLP / totalSupply; - const lpInfo = res.slice(2 + addresses.length * 2, 2 + addresses.length * 3); - lpInfo.forEach(({ 0: userInfo }, i) => { - values[addresses[i % addresses.length]] += - (userInfo[0] / 10 ** 18) * tokensPerLP; - }); - - // MAX token in MAX_BUSD LP (User Wallet) - const lpInWallet = res.slice(2, 2 + addresses.length); - for (let i = 0; i < addresses.length; i++) { - values[addresses[i]] += (lpInWallet[i] / 10 ** 18) * tokensPerLP; - } - - return values; -} diff --git a/src/strategies/minto-balance-of-all/README.md b/src/strategies/minto-balance-of-all/README.md deleted file mode 100644 index be2fd8de1..000000000 --- a/src/strategies/minto-balance-of-all/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# minto-balance-of-all - -This is the minto strategy, it returns sum the balances of the voters from minto erc20, minto staking, minto staking. - -Here is an example of parameters: - -```json -{ - "address": "0x410a56541bD912F9B60943fcB344f1E3D6F09567", - "symbol": "BTCMT", - "decimals": 18, - "stakingAddress": "0x78ae303182FCA96A4629A78Ee13235e6525EbcFb", - "autoStakingAddress": "0xE751ffdC2a684EEbcaB9Dc95fEe05c083F963Bf1" -} -``` diff --git a/src/strategies/minto-balance-of-all/examples.json b/src/strategies/minto-balance-of-all/examples.json deleted file mode 100644 index 2d63f741f..000000000 --- a/src/strategies/minto-balance-of-all/examples.json +++ /dev/null @@ -1,36 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "minto-balance-of-all", - "params": { - "address": "0x410a56541bD912F9B60943fcB344f1E3D6F09567", - "symbol": "BTCMT", - "decimals": 18, - "stakingAddress": "0x78ae303182FCA96A4629A78Ee13235e6525EbcFb", - "autoStakingAddress": "0xE751ffdC2a684EEbcaB9Dc95fEe05c083F963Bf1" - } - }, - "network": "128", - "addresses": [ - "0x05903a958710ec752dd4a25dd3157ccee9d6c92e", - "0x03bb74c32acb025effab4d31e579d3751363c216", - "0x0369a66d5d25734a9aef4689546a809888fe5bfe", - "0x0743a7eb79cf4b478ba0bba22bdf2f11bc8ad5de", - "0x00ce049d0624a087473a4bd5078ac90ad25175e8", - "0x01f3a3962668df7799429ae827df59bc32a5d761", - "0x020ce48bb83cd6cb1483149ed7a6d2bca4526c84", - "0x0235ac7285d4ad1a5261904539e4862c13a830af", - "0x0369a66d5d25734a9aef4689546a809888fe5bfe", - "0x03bb74c32acb025effab4d31e579d3751363c216", - "0x05903a958710ec752dd4a25dd3157ccee9d6c92e", - "0x0636b47164ea3099c6fc39c52a3f4b7b7ecaa03b", - "0x0686b62cd088715dcb690e6514aaeca229bbac07", - "0x34db5451844371226c7737137f9cdf30638733f2", - "0xe2096f4a5d1ea4501e27ffafc1e910e35a2c200a", - "0xf4ad32a268d49f72e7bd1b89e2a63d89ac5e8b62", - "0x0dC14192333A736a3caDfBD3f9f883106584614A" - ], - "snapshot": 12883023 - } -] diff --git a/src/strategies/minto-balance-of-all/index.ts b/src/strategies/minto-balance-of-all/index.ts deleted file mode 100644 index 009f367b5..000000000 --- a/src/strategies/minto-balance-of-all/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = 'btcmt-minto'; -export const version = '0.1.0'; - -const abi = [ - 'function balanceOfSum(address account) external view returns (uint256)' -]; - -const stakingAbi = [ - 'function userStakes(address account) external view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256)' -]; - -const autoStakingAbi = [ - 'function userStake(address account) external view returns (uint256, uint256, uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - - const multiStaking = new Multicaller(network, provider, stakingAbi, { - blockTag - }); - const multiAutoStaking = new Multicaller(network, provider, autoStakingAbi, { - blockTag - }); - - addresses.forEach((address) => { - multi.call(address, options.address, 'balanceOfSum', [address]); - multiStaking.call(address, options.stakingAddress, 'userStakes', [address]); - multiAutoStaking.call(address, options.autoStakingAddress, 'userStake', [ - address - ]); - }); - - const [result, resultStaking, resultAutoStaking] = await Promise.all([ - multi.execute(), - multiStaking.execute(), - multiAutoStaking.execute() - ]); - - return Object.fromEntries( - addresses.map((address) => { - const sum = - parseFloat(formatUnits(result[address], options.decimals)) + - parseFloat(formatUnits(resultStaking[address][1], options.decimals)) + - parseFloat( - formatUnits(resultAutoStaking[address][0], options.decimals) - ); - return [address, sum]; - }) - ); -} diff --git a/src/strategies/multichain-serie/examples.json b/src/strategies/multichain-serie/examples.json deleted file mode 100644 index 70aa3639e..000000000 --- a/src/strategies/multichain-serie/examples.json +++ /dev/null @@ -1,36 +0,0 @@ -[ - { - "name": "Multichain serie", - "strategy": { - "name": "multichain-serie", - "params": { - "symbol": "MULTI", - "strategies": [ - { - "name": "erc20-balance-of", - "network": "137", - "params": { - "address": "0xB9638272aD6998708de56BBC0A290a1dE534a578", - "decimals": 18 - } - }, - { - "name": "erc20-balance-of", - "network": "56", - "params": { - "address": "0x0e37d70b51ffa2b98b4d34a5712c5291115464e3", - "decimals": 18 - } - } - ] - } - }, - "network": "1", - "addresses": [ - "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5", - "0x9feab70f3c4a944b97b7565bac4991df5b7a69ff", - "0xaca39b187352d9805deced6e73a3d72abf86e7a0" - ], - "snapshot": 13035566 - } -] diff --git a/src/strategies/multichain-serie/index.ts b/src/strategies/multichain-serie/index.ts deleted file mode 100644 index 98b3d9982..000000000 --- a/src/strategies/multichain-serie/index.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { getProvider, getSnapshots } from '../../utils'; -import strategies from '..'; - -export const author = 'kesar'; -export const version = '1.1.0'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const results: any = []; - const blocks = await getSnapshots( - network, - snapshot, - provider, - options.strategies.map((s) => s.network || network) - ); - - for (const strategy of options.strategies) { - // If snapshot is taken before a network is activated then ignore its strategies - if ( - options.startBlocks && - blocks[strategy.network] < options.startBlocks[strategy.network] - ) { - continue; - } - - results.push( - await strategies[strategy.name].strategy( - space, - strategy.network, - getProvider(strategy.network), - addresses, - strategy.params, - blocks[strategy.network] - ) - ); - } - - return results.reduce((finalResults: any, strategyResult: any) => { - for (const [address, value] of Object.entries(strategyResult)) { - if (!finalResults[address]) { - finalResults[address] = 0; - } - finalResults[address] += value; - } - return finalResults; - }, {}); -} diff --git a/src/strategies/nation3-passport-coop-with-delegations/README.md b/src/strategies/nation3-passport-coop-with-delegations/README.md index f6b726178..34703bf40 100644 --- a/src/strategies/nation3-passport-coop-with-delegations/README.md +++ b/src/strategies/nation3-passport-coop-with-delegations/README.md @@ -2,7 +2,11 @@ Calculates voting power using a cooperative model (one person one vote) based on a user's ownership of nation3 passport (_erc721 NFT_). It also takes into account whether the NFT owner delegated his voting power to another account (using the `setSigner` function) -## Requires 1 input parameters: +## Requires 2 input parameters: + +**erc20** + +The address of veNation tokens contract **erc721** diff --git a/src/strategies/nation3-passport-coop-with-delegations/examples.json b/src/strategies/nation3-passport-coop-with-delegations/examples.json index 91f7dd490..b7689e4c2 100644 --- a/src/strategies/nation3-passport-coop-with-delegations/examples.json +++ b/src/strategies/nation3-passport-coop-with-delegations/examples.json @@ -4,11 +4,13 @@ "strategy": { "name": "nation3-passport-coop-with-delegations", "params": { - "erc721": "0x3337dac9f251d4e403d6030e18e3cfb6a2cb1333" + "erc721": "0x3337dac9f251d4e403d6030e18e3cfb6a2cb1333", + "erc20": "0xf7def1d2fbda6b74bee7452fdf7894da9201065d" } }, "network": "1", "addresses": [ + "0x61c872A66d79d932F9aEE874b397F2D50Ed78326", "0xEdd000B7Db3cb8931d4E0cb1D0DBe6B947Ceb09A", "0x47d80912400ef8f8224531EBEB1ce8f2ACf4b75a", "0x636d65212C815b93B8E5b069f7082169cec851b7", @@ -16,6 +18,6 @@ "0xfafda3727fe0406e50230bf6092be5ded68cd9e9", "0x79438224Bc21b0E6B45ECF9F8caADfBdB874DedD" ], - "snapshot": 17683972 + "snapshot": 18611783 } ] diff --git a/src/strategies/nation3-passport-coop-with-delegations/index.ts b/src/strategies/nation3-passport-coop-with-delegations/index.ts index 833a030cb..8975d3967 100644 --- a/src/strategies/nation3-passport-coop-with-delegations/index.ts +++ b/src/strategies/nation3-passport-coop-with-delegations/index.ts @@ -1,8 +1,20 @@ -import { BigNumber } from '@ethersproject/bignumber'; +import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; import { Multicaller } from '../../utils'; +import { formatUnits } from '@ethersproject/units'; +import { subgraphRequest } from '../../utils'; export const author = 'nation3'; -export const version = '0.2.0'; +export const version = '0.3.0'; + +type Query = { [key: string]: any }; + +const DECIMALS = 18; + +const balanceAbi = [ + 'function balanceOf(address account) external view returns (uint256)' +]; + +const ownerAbi = ['function ownerOf(uint256 id) public view returns (address)']; const signerAbi = [ 'function signerOf(uint256 id) external view returns (address)' @@ -20,9 +32,10 @@ export async function strategy( ): Promise> { const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const erc721SignerCaller = new Multicaller(network, provider, signerAbi, { + const erc20BalanceCaller = new Multicaller(network, provider, balanceAbi, { blockTag }); + const erc721LastTokenIdCaller = new Multicaller( network, provider, @@ -30,23 +43,82 @@ export async function strategy( { blockTag } ); + const erc721SignerCaller = new Multicaller(network, provider, signerAbi, { + blockTag + }); + const erc721OwnerCaller = new Multicaller(network, provider, ownerAbi, { + blockTag + }); + + const passportIssuanceSubgrgraph = + 'https://api.thegraph.com/subgraphs/name/nation3/passportissuance'; + + const revokedQuery: Query = { + revokes: { + id: true, + _to: true, + _tokenId: true + } + }; + + const revokedUsersResponse = await subgraphRequest( + passportIssuanceSubgrgraph, + revokedQuery + ); + + const revokedPassports: number[] = revokedUsersResponse.revokes.map( + (revokeObject) => { + return BigNumber.from(revokeObject._tokenId).toNumber(); + } + ); + erc721LastTokenIdCaller.call('lastTokenId', options.erc721, 'getNextId'); const lastIndex = await erc721LastTokenIdCaller.execute(); const lastTokenId = BigNumber.from(lastIndex.lastTokenId).toNumber(); - for (let i = 0; i < lastTokenId; i++) { + for (let i = 1; i < lastTokenId; i++) { + if (revokedPassports.includes(i)) continue; + erc721SignerCaller.call(i, options.erc721, 'signerOf', [i]); + erc721OwnerCaller.call(i, options.erc721, 'ownerOf', [i]); } - const erc721Signers: Record = - await erc721SignerCaller.execute(); + const [erc721Signers, erc721Owners]: [ + Record, + Record + ] = await Promise.all([ + erc721SignerCaller.execute(), + erc721OwnerCaller.execute() + ]); const erc721SignersArr = Object.entries(erc721Signers); + const erc721OwnersArr = Object.entries(erc721Owners); + + const eligibleAddresses = erc721SignersArr.filter(([, address]) => + addresses.includes(address) + ); + + //create a combined tuple + const eligibleSignerOwner: [string, string, string][] = eligibleAddresses.map( + ([id, signerAddress]) => { + const owner = erc721OwnersArr.find(([ownerId]) => id === ownerId); + return [id, signerAddress, owner ? owner[1] : '0x0']; + } + ); - const eligibleAddresses = erc721SignersArr - .map(([, address]) => address) - .filter((address) => addresses.includes(address)); + eligibleSignerOwner.forEach(([, , owner]) => + erc20BalanceCaller.call(owner, options.erc20, 'balanceOf', [owner]) + ); + + const erc20Balances: Record = + await erc20BalanceCaller.execute(); + + //now we have balances, need to check for > 1.5 on all IDs that have voted + const withPower = eligibleSignerOwner.filter(([, , owner]) => { + const balance = erc20Balances[owner] || 0; + return parseFloat(formatUnits(balance, DECIMALS)) > 1.5; + }); - return Object.fromEntries(eligibleAddresses.map((value) => [value, 1])); + return Object.fromEntries(withPower.map(([, signer]) => [signer, 1])) || []; } diff --git a/src/strategies/nation3-passport-coop-with-delegations/schema.json b/src/strategies/nation3-passport-coop-with-delegations/schema.json index 6fc8cdccd..8318dcad9 100644 --- a/src/strategies/nation3-passport-coop-with-delegations/schema.json +++ b/src/strategies/nation3-passport-coop-with-delegations/schema.json @@ -6,6 +6,14 @@ "title": "Strategy", "type": "object", "properties": { + "erc20": { + "type": "string", + "title": "veNation", + "examples": ["e.g. 0xf7def1d2fbda6b74bee7452fdf7894da9201065d"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, "erc721": { "type": "string", "title": "nation3 passport", @@ -15,7 +23,7 @@ "maxLength": 42 } }, - "required": ["erc721"], + "required": ["erc20", "erc721"], "additionalProperties": false } } diff --git a/src/strategies/nation3-votes-with-delegations/index.ts b/src/strategies/nation3-votes-with-delegations/index.ts index 4843fa03c..190afe909 100644 --- a/src/strategies/nation3-votes-with-delegations/index.ts +++ b/src/strategies/nation3-votes-with-delegations/index.ts @@ -53,7 +53,7 @@ export async function strategy( const lastIndex = await erc721LastTokenIdCaller.execute(); const lastTokenId = BigNumber.from(lastIndex.lastTokenId).toNumber(); - for (let i = 0; i < lastTokenId; i++) { + for (let i = 1; i < lastTokenId; i++) { erc721SignerCaller.call(i, options.erc721, 'signerOf', [i]); erc721OwnerCaller.call(i, options.erc721, 'ownerOf', [i]); } diff --git a/src/strategies/ocean-marketplace-v4/README.md b/src/strategies/ocean-marketplace-v4/README.md deleted file mode 100644 index 71121343b..000000000 --- a/src/strategies/ocean-marketplace-v4/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Ocean marketplace v4 - -```version 0.1``` - -This strategy gives score aka votes to the liquidity providers on the [Ocean marketplace v4](https://market.oceanprotocol.com). This means that LP participants can vote for OceanDAO grants via Snapshot without removing their liquidity. - -## Solution description - -The solution pulls the needed data from all Ocean Protocol subgraphs using the following path: -```https://subgraph.{network}.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph``` - -This strategy is designed to give voting scores only to marketplace liquidity providers by calculating their individual pool shares. - -The remaining vote count comes from other strategies configured in [OceanDAO Snapshot Page](https://vote.oceanprotocol.com/#/). - -For v4, we only look at pools w/ OCEAN as the basetoken, and pools that have been proprely initialized. We then attributes votes to LP'ers like this: -``` -user_votes = user_pool_shares * (total_Ocean_in_the_pool / total_number_of_pool_shares) -``` -This is done for all pools and the votes for the users are added up. - -To extend or run this strategy please use the setup described [here](https://docs.snapshot.page/strategies). - -## v3 vs. v4 differences -- In v4 we have to check if the pool basetoken is Ocean. Only Ocean tokens will obtain voting power. -- In v4, pools.datatoken.holderCount is always 0 as datatokens are consumed as soon as they are purchased -- In v4, pools.isFinalized checks if the pool has been properly setup. In v3 the equivalent was pools.active - -## Ocean ERC20 Addresses -You need to submit the ERC20 Ocean tokenAddress via the stratgy parameters. These [can be found here](https://github.com/oceanprotocol/contracts/blob/v4main/addresses/address.json) - -Addresses by network: -``` -'1': '0x967da4048cD07aB37855c090aAF366e4ce1b9F48', //mainnet -'3': '0x5e8DCB2AfA23844bcc311B00Ad1A0C30025aADE9', //ropsten -'42': '0x8967bcf84170c91b0d24d4302c2376283b0b3a07', //rinkeby -'56': '0xDCe07662CA8EbC241316a15B611c89711414Dd1a', //bsc -'137': '0x282d8efCe846A88B159800bd4130ad77443Fa1A1', //poly -'246': '0x593122AAE80A6Fc3183b2AC0c4ab3336dEbeE528', //ewt -'1285': '0x99C409E5f62E4bd2AC142f17caFb6810B8F0BAAE', //movr -'1287': '0xF6410bf5d773C7a41ebFf972f38e7463FA242477', //glmr -'80001': '0xd8992Ed72C445c35Cb4A2be468568Ed1079357c8' //mumbai -``` - -## How to Test -(1) Remove comments around debug logs. -(2) Enable code block inside `index.ts` that verifies expected results. -``` -if (options.expectedResults) { -``` -(3) Use the regular testing functionality `yarn test -strategy=ocean-marktplace-v4` diff --git a/src/strategies/ocean-marketplace-v4/examples.json b/src/strategies/ocean-marketplace-v4/examples.json deleted file mode 100644 index d4986c397..000000000 --- a/src/strategies/ocean-marketplace-v4/examples.json +++ /dev/null @@ -1,94 +0,0 @@ -[ - { - "name": "ERC-20 OCEAN staked in mainnet v4 marketplace via subgraph", - "strategy": { - "name": "ocean-marketplace-v4", - "params": { - "address": "0x967da4048cD07aB37855c090aAF366e4ce1b9F48", - "expectedResults": { - "scores": { - "0x7842Fa3B2d87Ff1cd52C4152382f7C4B3406E5A6": 250, - "0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0": 200, - "0x903322C7E45A60d7c8C3EA236c5beA9Af86310c7": 50 - } - } - } - }, - "network": "1", - "addresses": [ - "0x7842Fa3B2d87Ff1cd52C4152382f7C4B3406E5A6", - "0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0", - "0x903322C7E45A60d7c8C3EA236c5beA9Af86310c7" - ], - "snapshot": 15006609 - }, - { - "name": "ERC-20 OCEAN staked in polygon v4 marketplace via subgraph", - "strategy": { - "name": "ocean-marketplace-v4", - "params": { - "address": "0x282d8efCe846A88B159800bd4130ad77443Fa1A1", - "expectedResults": { - "scores": { - "0x3EFDD8f728c8e774aB81D14d0B2F07a8238960f4": 10762.326857765422, - "0xbbd33AFa85539fa65cc08A2e61a001876D2f13FE": 5752.362504190221, - "0x0363F3C31076a64b85Ceb69a28f958A7c1181CEe": 1750, - "0xC5320Dc3956484662cF3FE3B9355AEA93729783D": 778.6503526737412 - } - } - } - }, - "network": "137", - "addresses": [ - "0x3EFDD8f728c8e774aB81D14d0B2F07a8238960f4", - "0xbbd33AFa85539fa65cc08A2e61a001876D2f13FE", - "0x0363F3C31076a64b85Ceb69a28f958A7c1181CEe", - "0xC5320Dc3956484662cF3FE3B9355AEA93729783D" - ], - "snapshot": 29358635 - }, - { - "name": "ERC-20 OCEAN staked in EWT v4 marketplace via subgraph", - "strategy": { - "name": "ocean-marketplace-v4", - "params": { - "address": "0x593122AAE80A6Fc3183b2AC0c4ab3336dEbeE528", - "expectedResults": { - "scores": { - "0x159924ca0F47D6F704B97E29099b89e518A17B5E": 3489.1235163210085, - "0x4F20e69E7bA5aB2Fb2ae25A1d17C93fE5307faA9": 500.45061032028195 - } - } - } - }, - "network": "246", - "addresses": [ - "0x159924ca0F47D6F704B97E29099b89e518A17B5E", - "0x4F20e69E7bA5aB2Fb2ae25A1d17C93fE5307faA9" - ], - "snapshot": 18788160 - }, - { - "name": "ERC-20 OCEAN staked in MOVR v4 marketplace via subgraph", - "strategy": { - "name": "ocean-marketplace-v4", - "params": { - "address": "0x99C409E5f62E4bd2AC142f17caFb6810B8F0BAAE", - "expectedResults": { - "scores": { - "0xc97fa83746aDe91b0eeB16cb51326a0A980Af7c3": 200, - "0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0": 200, - "0xFcd3DfCc1E793D4D03fB4ffFf2B7Eb5A20FCfe4E": 0 - } - } - } - }, - "network": "1285", - "addresses": [ - "0xc97fa83746aDe91b0eeB16cb51326a0A980Af7c3", - "0x99840Df5Cb42faBE0Feb8811Aaa4BC99cA6C84e0", - "0xFcd3DfCc1E793D4D03fB4ffFf2B7Eb5A20FCfe4E" - ], - "snapshot": 2150228 - } -] diff --git a/src/strategies/ocean-marketplace-v4/index.ts b/src/strategies/ocean-marketplace-v4/index.ts deleted file mode 100644 index b86da3220..000000000 --- a/src/strategies/ocean-marketplace-v4/index.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { getAddress } from '@ethersproject/address'; -import { subgraphRequest } from '../../utils'; -import { formatUnits, parseUnits } from '@ethersproject/units'; -import { BigNumber } from '@ethersproject/bignumber'; -// import { verifyResultsLength, verifyResults } from './oceanUtils'; - -export const author = 'oceanprotocol'; -export const version = '0.1.0'; - -const OCEAN_ERC20_DECIMALS = 18; -const OCEAN_SUBGRAPH_URL = { - '1': 'https://v4.subgraph.mainnet.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', - '3': 'https://v4.subgraph.ropsten.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', - '42': 'https://v4.subgraph.rinkeby.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', - '56': 'https://v4.subgraph.bsc.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', - '137': - 'https://v4.subgraph.polygon.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', - '246': - 'https://v4.subgraph.energyweb.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', - '1285': - 'https://v4.subgraph.moonriver.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', - '1287': - 'https://v4.subgraph.moonbase.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph', - '80001': - 'https://v4.subgraph.mumbai.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph' -}; - -// Returns a BigDecimal as a BigNumber with 10^decimals extra zeros -export function bdToBn(bd, decimals) { - let bn; - const splitDecimal = bd.split('.'); - - if (splitDecimal.length > 1) { - bn = `${splitDecimal[0]}.${splitDecimal[1].slice( - 0, - decimals - splitDecimal[0].length - 1 - )}`; - } else { - bn = `${splitDecimal[0]}`; - } - - const bn2 = parseUnits(bn, decimals); - return bn2; -} - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const oceanToken = options.address.toLowerCase(); - const params = { - pools: { - __args: { - where: { - baseToken_in: [oceanToken] - }, - first: 1000, - orderBy: 'baseTokenLiquidity', - orderDirection: 'desc' - }, - isFinalized: true, - totalShares: true, - baseTokenLiquidity: true, - shares: { - __args: { - where: { - user_in: addresses.map((address) => address.toLowerCase()) - }, - orderBy: 'shares', - orderDirection: 'desc' - }, - user: { - id: true - }, - shares: true - }, - datatoken: { - holderCount: true - } - } - }; - - if (snapshot !== 'latest') { - // @ts-ignore - params.pools.__args.block = { number: +snapshot }; - } - - // Retrieve the top 1000 pools - const graphResults = await subgraphRequest( - OCEAN_SUBGRAPH_URL[network], - params - ); - - // Get total votes, for ALL addresses, inside top 1000 pools, with a minimum of 0.0001 shares - const score = {}; - const userAddresses: string[] = []; - const return_score = {}; - - // console.log( - // `graph results for network: ${network} at snapshot: ${snapshot}` - // ); - // console.log('results: ', graphResults); - - if (graphResults && graphResults.pools) { - graphResults.pools.forEach((pool) => { - if (pool.isFinalized) { - pool.shares.map((share) => { - const userAddress = getAddress(share.user.id); - // const shares = share.shares; - // console.log( - // `High Level - User address: ${userAddress} user poolShares: ${shares} baseTokenLiquidity: ${pool.baseTokenLiquidity} poolTotalShares: ${pool.totalShares}` - // ); - if (!userAddresses.includes(userAddress)) - userAddresses.push(userAddress); - if (!score[userAddress]) score[userAddress] = BigNumber.from(0); - const userShare = - share.shares * (pool.baseTokenLiquidity / pool.totalShares); - if (userShare > 0.0001) { - score[userAddress] = score[userAddress].add( - bdToBn(userShare.toString(), OCEAN_ERC20_DECIMALS) - ); - } - }); - } - }); - - // We then sum total votes, per user address - userAddresses.forEach((address) => { - const parsedSum = parseFloat( - formatUnits(score[address], OCEAN_ERC20_DECIMALS) - ); - return_score[address] = parsedSum * 2; //this resolves the 50% discrepancy due to 50/50 OCEAN/DT LP - - // console.log(`Score for address: ${address} is: ${return_score[address]}`); - }); - } - - // We then filter only the addresses expected - const results = Object.fromEntries( - Object.entries(return_score).filter(([k]) => addresses.indexOf(k) >= 0) - ); - - // Test validation: Update examples.json w/ expectedResults to reflect LPs @ blockHeight - // Success criteria: Address scores and length, must match expectedResults. Order not validated. - // From GRT's graphUtils.ts => verifyResults => Scores need to match expectedResults. - // npm run test --strategy=ocean-marketplace | grep -E 'SUCCESS|ERROR' - // if (options.expectedResults) { - // const expectedResults = {}; - // Object.keys(options.expectedResults.scores).forEach(function (key) { - // expectedResults[key] = results[key]; - // }); - // - // verifyResults( - // JSON.stringify(expectedResults), - // JSON.stringify(options.expectedResults.scores), - // 'Scores' - // ); - // - // verifyResultsLength( - // Object.keys(expectedResults).length, - // Object.keys(options.expectedResults.scores).length, - // 'Scores' - // ); - // } - - return results || {}; -} diff --git a/src/strategies/ocean-marketplace-v4/oceanUtils.ts b/src/strategies/ocean-marketplace-v4/oceanUtils.ts deleted file mode 100644 index be3b22095..000000000 --- a/src/strategies/ocean-marketplace-v4/oceanUtils.ts +++ /dev/null @@ -1,31 +0,0 @@ -export function verifyResultsLength( - result: number, - expectedResults: number, - type: string -): void { - if (result === expectedResults) { - console.log( - `>>> SUCCESS: ${type} result:[${result}] match expected results:[${expectedResults}] - length` - ); - } else { - console.error( - `>>> ERROR: ${type} result:[${result}] do not match expected results:[${expectedResults}] - length` - ); - } -} - -export function verifyResults( - result: string, - expectedResults: string, - type: string -): void { - if (result === expectedResults) { - console.log( - `>>> SUCCESS: ${type} result:[${result}] match expected results:[${expectedResults}]` - ); - } else { - console.error( - `>>> ERROR: ${type} result:[${result}] do not match expected results:[${expectedResults}]` - ); - } -} diff --git a/src/strategies/ocean-marketplace/index.ts b/src/strategies/ocean-marketplace/index.ts index de9b9bfdc..4261c0f5c 100644 --- a/src/strategies/ocean-marketplace/index.ts +++ b/src/strategies/ocean-marketplace/index.ts @@ -17,7 +17,7 @@ const OCEAN_SUBGRAPH_URL = { }; // Returns a BigDecimal as a BigNumber with 10^decimals extra zeros -export function bdToBn(bd, decimals) { +export function bdToBn(bd: string, decimals: number): BigNumber { let bn; const splitDecimal = bd.split('.'); diff --git a/src/strategies/piedao/examples.json b/src/strategies/piedao/examples.json deleted file mode 100644 index 40b626a10..000000000 --- a/src/strategies/piedao/examples.json +++ /dev/null @@ -1,23 +0,0 @@ -[ - { - "name": "piedao", - "strategy": { - "name": "piedao", - "params": { - "symbol": "PIE", - "BPT": "0xFAE2809935233d4BfE8a56c2355c4A2e7d1fFf1A", - "doughv1": "0x5f5e9ed11344dadc3a08688e5f17e23f6a99bf81", - "doughv2": "0xad32A8e6220741182940c5aBF610bDE99E737b2D", - "stakedDough": "0xB9a4Bca06F14A982fcD14907D31DFACaDC8ff88E", - "eDOUGH": "0x63cbd1858bd79de1a06c3c26462db360b834912d", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x66827bcd635f2bb1779d68c46aeb16541bca6ba8", - "0x635b230c3fdf6a466bb6dc3b9b51a8ceb0659b67" - ], - "snapshot": 11350000 - } -] diff --git a/src/strategies/piedao/index.ts b/src/strategies/piedao/index.ts deleted file mode 100644 index 8c4bdc1b9..000000000 --- a/src/strategies/piedao/index.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; - -export const author = 'alexintosh'; -export const version = '0.0.1'; - -const abi = [ - { - constant: true, - inputs: [ - { - internalType: 'address', - name: 'account', - type: 'address' - } - ], - name: 'balanceOf', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256' - } - ], - payable: false, - stateMutability: 'view', - type: 'function' - }, - { - constant: true, - inputs: [], - name: 'totalSupply', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function' - } -]; - -const chunk = (arr, size) => - Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => - arr.slice(i * size, i * size + size) - ); - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const doughv1Query = addresses.map((address: any) => [ - options.doughv1, - 'balanceOf', - [address] - ]); - - const doughv2Query = addresses.map((address: any) => [ - options.doughv2, - 'balanceOf', - [address] - ]); - - const eDOUGHQuery = addresses.map((address: any) => [ - options.eDOUGH, - 'balanceOf', - [address] - ]); - - const stakedDoughQuery = addresses.map((address: any) => [ - options.stakedDough, - 'balanceOf', - [address] - ]); - - const lpDoughQuery = addresses.map((address: any) => [ - options.BPT, - 'balanceOf', - [address] - ]); - - const response = await multicall( - network, - provider, - abi, - [ - [options.doughv2, 'balanceOf', [options.BPT]], - [options.BPT, 'totalSupply'], - ...doughv1Query, - ...doughv2Query, - ...eDOUGHQuery, - ...stakedDoughQuery, - ...lpDoughQuery - ], - { blockTag } - ); - - const doughv2BPT = response[0]; - const doughv2BptTotalSupply = response[1]; - const responseClean = response.slice(2, response.length); - - const chunks = chunk(responseClean, addresses.length); - const doughv1Balances = chunks[0]; - const doughv2Balances = chunks[1]; - const eDOUGHBalances = chunks[2]; - const stakedDoughBalances = chunks[3]; - const lpDoughBalances = chunks[4]; - - return Object.fromEntries( - Array(addresses.length) - .fill('x') - .map((_, i) => [ - addresses[i], - parseFloat( - formatUnits( - doughv2BPT[0] - .mul(stakedDoughBalances[i][0]) - .div(doughv2BptTotalSupply[0]) - .add( - doughv2BPT[0] - .mul(lpDoughBalances[i][0]) - .div(doughv2BptTotalSupply[0]) - ) - .add(doughv1Balances[i][0]) - .add(doughv2Balances[i][0]) - .add(eDOUGHBalances[i][0]) - .toString(), - options.decimals - ) - ) - ]) - ); -} diff --git a/src/strategies/planet-finance/examples.json b/src/strategies/planet-finance/examples.json deleted file mode 100644 index a1d317dc5..000000000 --- a/src/strategies/planet-finance/examples.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "planet-finance", - "params": { - "address": "0x72B7D61E8fC8cF971960DD9cfA59B8C829D91991", - "symbol": "AQUA", - "decimals": 18 - } - }, - "network": "56", - "addresses": ["0x3BDaDCbF17e24B2157d920E25407FF8cd4f5F54C"], - "snapshot": 13267571 - } -] diff --git a/src/strategies/planet-finance/index.ts b/src/strategies/planet-finance/index.ts deleted file mode 100644 index 6b3e0c189..000000000 --- a/src/strategies/planet-finance/index.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; -import { Multicaller } from '../../utils'; -import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; - -export const author = 'planet-finance'; -export const version = '0.0.1'; - -const planetFinanceFarmAbi = [ - 'function poolInfo(uint256) returns (address want,uint256 allocPoint,uint256 lastRewardBlock,uint256 accAQUAPerShare,address strat)', - 'function stakedWantTokens(uint256 _pid, address _user) returns (uint256)' -]; - -const bep20Abi: any = [ - 'function totalSupply() view returns (uint256)', - 'function balanceOf(address) view returns (uint256)' -]; - -const aquaAutoCompAbi = [ - 'function balanceOf() view returns (uint256)', - 'function totalShares() view returns (uint256)', - 'function userInfo(address) view returns (uint256 shares, uint256 lastDepositedTime , uint256 cakeAtLastUserAction , uint256 lastUserActionTime)' -]; - -const aquaLendingAbi = [ - 'function getAccountSnapshot(address) view returns (uint256,uint256,uint256,uint256)' -]; - -const planetFinanceFarmContractAddress = - '0x0ac58Fd25f334975b1B61732CF79564b6200A933'; - -const gammaFarmAddress = '0xB87F7016585510505478D1d160BDf76c1f41b53d'; - -const aquaAutoCompPoolAddress = '0x8A53dAdF2564d030b41dB1c04fB3c4998dC1326e'; - -const aquaAddress = '0x72B7D61E8fC8cF971960DD9cfA59B8C829D91991'; - -const aquaBnbLpTokenAddress = '0x03028D2F8B275695A1c6AFB69A4765e3666e36d9'; - -const aquaGammaLpTokenAddress = '0xcCaF3fcE9f2D7A7031e049EcC65c0C0Cc331EE0D'; - -const aquaLendingAddress = '0xb7eD4A5AF620B52022fb26035C565277035d4FD7'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const erc20Multi = new Multicaller(network, provider, bep20Abi, { - blockTag - }); - - const autoCompMulti = new Multicaller(network, provider, aquaAutoCompAbi, { - blockTag - }); - - // returns user's aqua balance ofr their address - let score: any = erc20BalanceOfStrategy( - space, - network, - provider, - addresses, - options, - snapshot - ); - - // returns user's aqua balance in aqua only vault - let usersAquaVaultBalances: any = multicall( - network, - provider, - planetFinanceFarmAbi, - addresses.map((address: any) => [ - planetFinanceFarmContractAddress, - 'stakedWantTokens', - ['1', address] - ]), - { blockTag } - ); - - //returns user's shares in aqua auto comp vault - let usersAquaAutoCompVaultBalances: any = multicall( - network, - provider, - aquaAutoCompAbi, - addresses.map((address: any) => [ - aquaAutoCompPoolAddress, - 'userInfo', - [address] - ]), - { blockTag } - ); - - // returns user's aqua balance in aqua-bnb vault - let usersAquaGammaVaultBalances: any = multicall( - network, - provider, - planetFinanceFarmAbi, - addresses.map((address: any) => [ - gammaFarmAddress, - 'stakedWantTokens', - ['0', address] - ]), - { blockTag } - ); - - // returns user's aqua balance in aqua-bnb vault - let usersNewAquaBnbVaultBalances: any = multicall( - network, - provider, - planetFinanceFarmAbi, - addresses.map((address: any) => [ - gammaFarmAddress, - 'stakedWantTokens', - ['1', address] - ]), - { blockTag } - ); - - //AQUA LENDING - let usersAquaInLending: any = multicall( - network, - provider, - aquaLendingAbi, - addresses.map((address: any) => [ - aquaLendingAddress, - 'getAccountSnapshot', - [address] - ]), - { blockTag } - ); - - const result = await Promise.all([ - score, - usersAquaVaultBalances, - usersAquaAutoCompVaultBalances, - usersAquaGammaVaultBalances, - usersNewAquaBnbVaultBalances, - usersAquaInLending - ]); - - score = result[0]; - usersAquaVaultBalances = result[1]; - usersAquaAutoCompVaultBalances = result[2]; - usersAquaGammaVaultBalances = result[3]; - usersNewAquaBnbVaultBalances = result[4]; - usersAquaInLending = result[5]; - - //AQUA-BNB - erc20Multi.call('aquaBnbTotalSupply', aquaBnbLpTokenAddress, 'totalSupply'); - - erc20Multi.call('aquaBnbAquaBal', aquaAddress, 'balanceOf', [ - aquaBnbLpTokenAddress - ]); - - //AQUA-GAMMA - erc20Multi.call( - 'aquaGammaTotalSupply', - aquaGammaLpTokenAddress, - 'totalSupply' - ); - - erc20Multi.call('aquaGammaAquaBal', aquaAddress, 'balanceOf', [ - aquaGammaLpTokenAddress - ]); - - const erc20Result = await erc20Multi.execute(); - - const totalSupply = erc20Result.aquaBnbTotalSupply.toString(); - - const contractAquaBalance = erc20Result.aquaBnbAquaBal.toString(); - - const totalSupplyAquaGamma = erc20Result.aquaGammaTotalSupply.toString(); - - const aquaGammaContractAquaBalance = erc20Result.aquaGammaAquaBal.toString(); - - //AQUA AUTO COMPOUNDING - autoCompMulti.call('aquaBalance', aquaAutoCompPoolAddress, 'balanceOf'); - autoCompMulti.call('totalShares', aquaAutoCompPoolAddress, 'totalShares'); - - const autoCompResult = await autoCompMulti.execute(); - - let aquaBalance = autoCompResult.aquaBalance.toString(); - aquaBalance = parseFloat(formatUnits(aquaBalance, 18)); - - let totalShares = autoCompResult.totalShares.toString(); - totalShares = parseFloat(formatUnits(totalShares, 18)); - - return Object.fromEntries( - Object.entries(score).map((address: any, index) => [ - address[0], - - address[1] + - parseFloat(formatUnits(usersAquaVaultBalances[index].toString(), 18)) + - (parseFloat( - formatUnits(usersNewAquaBnbVaultBalances[index].toString(), 18) - ) / - parseFloat(formatUnits(totalSupply, 18))) * - parseFloat(formatUnits(contractAquaBalance, 18)) + - (parseFloat( - formatUnits(usersAquaGammaVaultBalances[index].toString(), 18) - ) / - parseFloat(formatUnits(totalSupplyAquaGamma, 18))) * - parseFloat(formatUnits(aquaGammaContractAquaBalance, 18)) + - (parseFloat( - formatUnits( - usersAquaAutoCompVaultBalances[index]['shares'].toString(), - 18 - ) - ) / - totalShares) * - aquaBalance + - parseFloat(formatUnits(usersAquaInLending[index]['1'], 18)) * - parseFloat(formatUnits(usersAquaInLending[index]['3'], 18)) - ]) - ); -} diff --git a/src/strategies/plearn/README.md b/src/strategies/plearn/README.md new file mode 100644 index 000000000..45f64f8e2 --- /dev/null +++ b/src/strategies/plearn/README.md @@ -0,0 +1,47 @@ +# Plearn + +This is the most common strategy, it returns the balances of the voters for a balances PLN token +in Plearn project(pools, token). + +Here is an example of parameters: + +```json +[ + { + "name": "Example query", + "strategy": { + "name": "plearn", + "params": { + "lockedPoolAddresses": [ + { + "address": "0xc38d542326545470a12B06Bf8e315DE55B0B6B46" + }, + { + "address": "0x9b45a8eeD3eF6DA3bE222147533Da542aa384006" + } + ], + "foundingInvestorPoolAddresses": [], + "pendingWithdrawalAddresses": [ + { + "address": "0x7E4e06C81B41284198C0693cd98eb357257Fc3d9" + }, + { + "address": "0xC26a3E07D8CCF34195e943C0bb705f206Dd57030" + } + ], + "symbol": "PLN", + "address": "0xBe0D3526fc797583Dada3F30BC390013062A048B", + "decimals": 18 + } + }, + "network": "56", + "addresses": [ + "0xE0d54117600e592E7a78C985996d11b8Fb1B69C3", + "0x3C97c372B45cC96Fe73814721ebbE6db02C9D88E", + "0xB6605F98A5562b1AC821Bc5f2B75934239e8c6D6", + "0x8900cCBdC60fD97E3B7c8529A9987F8c0f8A1125" + ], + "snapshot": 33739024 + } +] +``` diff --git a/src/strategies/plearn/examples.json b/src/strategies/plearn/examples.json new file mode 100644 index 000000000..e64080805 --- /dev/null +++ b/src/strategies/plearn/examples.json @@ -0,0 +1,38 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "plearn", + "params": { + "lockedPoolAddresses": [ + { + "address": "0xc38d542326545470a12B06Bf8e315DE55B0B6B46" + }, + { + "address": "0x9b45a8eeD3eF6DA3bE222147533Da542aa384006" + } + ], + "foundingInvestorPoolAddresses": [], + "pendingWithdrawalAddresses": [ + { + "address": "0x7E4e06C81B41284198C0693cd98eb357257Fc3d9" + }, + { + "address": "0xC26a3E07D8CCF34195e943C0bb705f206Dd57030" + } + ], + "symbol": "PLN", + "address": "0xBe0D3526fc797583Dada3F30BC390013062A048B", + "decimals": 18 + } + }, + "network": "56", + "addresses": [ + "0xE0d54117600e592E7a78C985996d11b8Fb1B69C3", + "0x3C97c372B45cC96Fe73814721ebbE6db02C9D88E", + "0xB6605F98A5562b1AC821Bc5f2B75934239e8c6D6", + "0x8900cCBdC60fD97E3B7c8529A9987F8c0f8A1125" + ], + "snapshot": 33739024 + } +] diff --git a/src/strategies/plearn/index.ts b/src/strategies/plearn/index.ts new file mode 100644 index 000000000..be80146f2 --- /dev/null +++ b/src/strategies/plearn/index.ts @@ -0,0 +1,144 @@ +import { Provider } from '@ethersproject/providers'; +import { formatUnits } from '@ethersproject/units'; +import { BigNumber } from '@ethersproject/bignumber'; +import { multicall } from '../../utils'; +import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; + +export const author = 'plearnclub'; +export const version = '0.0.1'; + +const lockedPoolabi = [ + 'function userInfo(address) view returns (uint256 amount)' +]; + +const foundingInvestorPoolabi = [ + 'function userInfo(address) view returns (uint256 initialAmount, uint256 amount)' +]; + +const pendingWithdrawalabi = [ + 'function lockedBalances(address user) view returns (uint256 total, uint256 unlockable, uint256 locked, tuple(uint256 amount, uint256 unlockTime)[] lockData)' +]; + +function transformResults( + res: any[], + addresses: string[], + balanceTransformer: (result: any) => number +): { [address: string]: number } { + return res.reduce((acc: { [address: string]: number }, result, index) => { + const address = addresses[index % addresses.length]; + if (!acc[address]) { + acc[address] = 0; + } + + const amount = balanceTransformer(result); + acc[address] += amount; + return acc; + }, {}); +} + +export async function strategy( + space: string, + network: string, + provider: Provider, + addresses: string[], + options: { + lockedPoolAddresses: { address: string }[]; + foundingInvestorPoolAddresses: { address: string }[]; + pendingWithdrawalAddresses: { address: string }[]; + symbol: string; + address: string; + decimals: number; + }, + snapshot: number | string +): Promise<{ [address: string]: number }> { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + const score = await erc20BalanceOfStrategy( + space, + network, + provider, + addresses, + options, + snapshot + ); + + const lockedPoolCalls = options.lockedPoolAddresses.flatMap((item) => + addresses.map((address) => [ + item.address, + 'userInfo', + [address], + { blockTag } + ]) + ); + + const foundingInvestorPoolCalls = + options.foundingInvestorPoolAddresses.flatMap((item) => + addresses.map((address) => [ + item.address, + 'userInfo', + [address], + { blockTag } + ]) + ); + + const pendingWithdrawalCalls = options.pendingWithdrawalAddresses.flatMap( + (item) => + addresses.map((address) => [ + item.address, + 'lockedBalances', + [address], + { blockTag } + ]) + ); + + const [ + lockedPoolBalancesRes, + foundingInvestorPoolBalancesRes, + pendingWithdrawalBalancesRes + ] = await Promise.all([ + multicall(network, provider, lockedPoolabi, lockedPoolCalls, { blockTag }), + multicall( + network, + provider, + foundingInvestorPoolabi, + foundingInvestorPoolCalls, + { blockTag } + ), + multicall(network, provider, pendingWithdrawalabi, pendingWithdrawalCalls, { + blockTag + }) + ]); + + const pf = (amount: BigNumber) => + parseFloat(formatUnits(amount, options.decimals)); + + const lockedPoolScore = transformResults( + lockedPoolBalancesRes, + addresses, + (r) => pf(r.amount) + ); + const foundingInvestorPoolScore = transformResults( + foundingInvestorPoolBalancesRes, + addresses, + (r) => pf(r.amount) + ); + const pendingWithdrawalScore = transformResults( + pendingWithdrawalBalancesRes, + addresses, + (r) => pf(r.total) + ); + + const finalScore = Object.keys(score).reduce( + (acc: { [address: string]: number }, address) => { + acc[address] = Math.trunc( + score[address] + + (lockedPoolScore[address] || 0) + + (foundingInvestorPoolScore[address] || 0) + + (pendingWithdrawalScore[address] || 0) + ); + return acc; + }, + {} + ); + + return finalScore; +} diff --git a/src/strategies/pod-leader/README.md b/src/strategies/pod-leader/README.md deleted file mode 100644 index 0ac193924..000000000 --- a/src/strategies/pod-leader/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# PodLeader pool balance - -Calculates the balance of either a uniswap pair in a Yield Yak/Pod leader style farm, or the amount of each individual token in that LP which is deposited into the farm. For example, if an LP pair has 100 token1 and 200 token2, one can isolate token2 for snapshot votes. One can also give weights to each of these tokens when combined with other strategies. - -## Accepted options: - -- chefAddress: Masterchef contract address -- pid: Mastechef pool id (starting with zero) - -- uniPairAddress: Address of a uniswap pair (or a sushi pair or any other with the same interface) - - If the uniPairAddress option is provided, converts staked LP token balance to base token balance - (based on the pair total supply and base token reserve) - - If uniPairAddress is null or undefined, returns staked token balance of the pool - -- tokenAddress: Address of a token for single token Pools. - - if the uniPairAddress is provided the tokenAddress is ignored. - -- weight: Integer multiplier of the result (for combining strategies with different weights, totally optional) -- weightDecimals: Integer value of number of decimal places to apply to the final result - -- token0.address: Address of the uniPair token 0. If defined, the strategy will return the result for the token0. - -- token0.weight: Integer multiplier of the result for token0 -- token0.weightDecimals: Integer value of number of decimal places to apply to the result of token0 - -- token1.address: Address of the uniPair token 1. If defined, the strategy will return the result for the token1. - -- token1.weight: Integer multiplier of the result for token1 -- token1.weightDecimal: Integer value of number of decimal places to apply to the result of token1 - - -- log: Boolean flag to enable or disable logging to the console (used for debugging purposes during development) - - - -Check the examples.json file for how to use the options. diff --git a/src/strategies/pod-leader/examples.json b/src/strategies/pod-leader/examples.json deleted file mode 100644 index 67d0c6578..000000000 --- a/src/strategies/pod-leader/examples.json +++ /dev/null @@ -1,63 +0,0 @@ -[ - { - "name": "Example query - Count of tokens in single token Pool", - "strategy": { - "name": "pod-leader", - "params": { - "symbol": "STAKE", - "chefAddress": "0x111E1E97435b57467E79d4930acc4B7EB3d478ad", - "uniPairAddress": "0x73e6CB72a79dEa7ed75EF5eD6f8cFf86C9128eF5", - "token0": { - "address": "0x8B1d98A91F853218ddbb066F20b8c63E782e2430", - "weight": 1, - "weightDecimals": 0 - }, - "pid": "2", - "weight": 1, - "weightDecimals": 0 - } - }, - "network": "43114", - "addresses": ["0x9F8A5B35f5508071cf2304A670EAB0803F3737aa"], - "snapshot": 5475221 - }, - { - "name": "Example query - Count of tokens in single token Pool", - "strategy": { - "name": "pod-leader", - "params": { - "chefAddress": "0x111E1E97435b57467E79d4930acc4B7EB3d478ad", - "uniPairAddress": "0x1a9bd67c82c0e8e47c3ad2fa772fcb9b7a831a37", - "token1": { - "address": "0x8B1d98A91F853218ddbb066F20b8c63E782e2430", - "weight": 1, - "weightDecimals": 0 - }, - "pid": "0", - "weight": 1, - "weightDecimals": 0 - } - }, - "network": "43114", - "addresses": ["0x9F8A5B35f5508071cf2304A670EAB0803F3737aa"], - "snapshot": 5475221 - }, - - { - "name": "Example query - Tokens in single token Pool", - "strategy": { - "name": "pod-leader", - "params": { - "chefAddress": "0xA3654801Ba6FB21d5A984F9a857441395dDeccFb", - "tokenAddress": "0x8B1d98A91F853218ddbb066F20b8c63E782e2430", - "pid": "0", - "weight": 1, - "weightDecimals": 0, - "decimals": 0 - } - }, - "network": "43114", - "addresses": ["0x9F8A5B35f5508071cf2304A670EAB0803F3737aa"], - "snapshot": 5475221 - } -] diff --git a/src/strategies/pod-leader/index.ts b/src/strategies/pod-leader/index.ts deleted file mode 100644 index 4b448e509..000000000 --- a/src/strategies/pod-leader/index.ts +++ /dev/null @@ -1,343 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; -import { BigNumber } from '@ethersproject/bignumber'; - -export const author = 'ursamaritimus'; -export const version = '0.3.0'; - -/* - * PodLeader pool balance. Accepted options: - * - chefAddress: Masterchef contract address - * - pid: Mastechef pool id (starting with zero) - * - * - uniPairAddress: Address of a uniswap pair (or a sushi pair or any other with the same interface) - * - If the uniPairAddress option is provided, converts staked LP token balance to base token balance - * (based on the pair total supply and base token reserve) - * - If uniPairAddress is null or undefined, returns staked token balance of the pool - * - * - tokenAddress: Address of a token for single token Pools. - * - if the uniPairAddress is provided the tokenAddress is ignored. - * - * - weight: Integer multiplier of the result (for combining strategies with different weights, totally optional) - * - weightDecimals: Integer value of number of decimal places to apply to the final result - * - * - token0.address: Address of the uniPair token 0. If defined, the strategy will return the result for the token0. - * - * - token0.weight: Integer multiplier of the result for token0 - * - token0.weightDecimals: Integer value of number of decimal places to apply to the result of token0 - * - * - token1.address: Address of the uniPair token 1. If defined, the strategy will return the result for the token1. - * - * - token1,weight: Integer multiplier of the result for token1 - * - token1.weightDecimal: Integer value of number of decimal places to apply to the result of token1 - * - * - * - log: Boolean flag to enable or disable logging to the console (used for debugging purposes during development) - * - - * - * Check the examples.json file for how to use the options. - */ - -const abi = [ - 'function userInfo(uint256, address) view returns (uint256 amount, uint256 rewardTokenDebt)', - 'function totalSupply() view returns (uint256)', - 'function getReserves() view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast)', - 'function token0() view returns (address)', - 'function token1() view returns (address)', - 'function decimals() view returns (uint8)' -]; - -let log: string[] = []; -let _options; - -const getUserInfoCalls = (addresses: any[]) => { - const result: any[] = []; - - for (const address of addresses) { - result.push([_options.chefAddress, 'userInfo', [_options.pid, address]]); - } - - return result; -}; - -const getTokenCalls = () => { - const result: any[] = []; - - if (_options.uniPairAddress != null) { - result.push([_options.uniPairAddress, 'totalSupply', []]); - result.push([_options.uniPairAddress, 'getReserves', []]); - result.push([_options.uniPairAddress, 'token0', []]); - result.push([_options.uniPairAddress, 'token1', []]); - result.push([_options.uniPairAddress, 'decimals', []]); - - if (_options.token0?.address != null) { - result.push([_options.token0.address, 'decimals', []]); - } - - if (_options.token1?.address != null) { - result.push([_options.token1.address, 'decimals', []]); - } - } else if (_options.tokenAddress != null) { - result.push([_options.tokenAddress, 'decimals', []]); - } - - return result; -}; - -function arrayChunk(arr: T[], chunkSize: number): T[][] { - const result: T[][] = []; - - for (let i = 0, j = arr.length; i < j; i += chunkSize) { - result.push(arr.slice(i, i + chunkSize)); - } - - return result; -} - -async function processValues( - values: any[], - tokenValues: any[], - network: any, - provider: any, - blockTag: string | number -) { - log.push(`values = ${JSON.stringify(values, undefined, 2)}`); - log.push(`tokenValues = ${JSON.stringify(tokenValues, undefined, 2)}`); - printLog(); - - const poolStaked = values[0][0] as BigNumber; - const weight = BigNumber.from(_options.weight || 1); - const weightDecimals = BigNumber.from(10).pow( - BigNumber.from(_options.weightDecimals || 0) - ); - - let result = 0; - - if (_options.uniPairAddress == null) { - log.push(`poolStaked = ${poolStaked}`); - - if (_options.tokenAddress != null) { - const tokenDecimals = BigNumber.from(10).pow( - BigNumber.from(tokenValues[0][0]) - ); - - log.push(`tokenDecimals = ${tokenDecimals}`); - log.push(`decimals = ${_options.decimals}`); - printLog(); - - result = toFloat(poolStaked.div(tokenDecimals), _options.decimals); - } else { - printLog(); - result = toFloat(poolStaked, _options.decimals); - } - } else { - const uniTotalSupply = tokenValues[0][0]; - const uniReserve0 = tokenValues[1][0]; - const uniReserve1 = tokenValues[1][1]; - const uniPairDecimalsIndex: any = - _options.uniPairAddress != null ? 4 : null; - const uniPairDecimalsCount = tokenValues[uniPairDecimalsIndex][0]; - const uniPairDecimals = - uniPairDecimalsIndex != null - ? BigNumber.from(10).pow(BigNumber.from(uniPairDecimalsCount || 0)) - : BigNumber.from(1); - - const token0Address = tokenValues[2][0]; - const useToken0 = - _options.token0?.address != null && - _options.token0.address.toString().toLowerCase() == - token0Address?.toString().toLowerCase(); - - log.push(`useToken0 = ${useToken0}`); - - if (useToken0) { - const token0DecimalsIndex = 5; - - log.push(`token0DecimalsIndex = ${token0DecimalsIndex}`); - log.push(`tokenValues = ${JSON.stringify(tokenValues, undefined, 2)}`); - printLog(); - - result += await GetTokenValue( - network, - provider, - blockTag, - uniTotalSupply, - uniReserve0, - uniPairDecimals, - poolStaked, - tokenValues, - token0Address, - token0DecimalsIndex, - _options.token0?.weight, - _options.token0?.weightDecimals - ); - } - - const token1Address = tokenValues[3][0]; - const useToken1 = - _options.token1?.address != null && - _options.token1.address.toString().toLowerCase() == - token1Address?.toString().toLowerCase(); - - log.push(`useToken1 = ${useToken1}`); - - if (useToken1) { - const token1DecimalsIndex = _options.token0?.address != null ? 6 : 5; - - log.push(`token1DecimalsIndex = ${token1DecimalsIndex}`); - log.push(`tokenValues = ${JSON.stringify(tokenValues, undefined, 2)}`); - printLog(); - - result += await GetTokenValue( - network, - provider, - blockTag, - uniTotalSupply, - uniReserve1, - uniPairDecimals, - poolStaked, - tokenValues, - token1Address, - token1DecimalsIndex, - _options.token1?.weight, - _options.token1?.WeightDecimals - ); - } - - if (!useToken0 && !useToken1) { - log.push(`poolStaked = ${poolStaked}`); - log.push(`uniPairDecimals = ${uniPairDecimals}`); - printLog(); - - const tokenCount = poolStaked.toNumber() / 10 ** uniPairDecimalsCount; - - log.push(`tokenCount = ${tokenCount}`); - - result = tokenCount / 10 ** (_options.decimals || 0); - } - } - - log.push(`result = ${result}`); - printLog(); - - result *= weight.toNumber() / weightDecimals.toNumber(); - - log.push(`weight = ${weight}`); - log.push(`weightDecimals = ${weightDecimals}`); - log.push(`result = ${result}`); - printLog(); - - return result; -} - -function toFloat(value: BigNumber, decimals: any): number { - const decimalsResult = decimals === 0 ? 0 : decimals || 18; - - log.push(`toFloat value = ${value}`); - log.push(`toFloat decimals = ${decimals}`); - log.push(`toFloat decimalsResult = ${decimalsResult}`); - printLog(); - - return parseFloat(formatUnits(value.toString(), decimalsResult)); -} - -async function GetTokenValue( - network: any, - provider: any, - blockTag: string | number, - uniTotalSupply: any, - uniReserve: any, - uniPairDecimals: BigNumber, - poolStaked: BigNumber, - tokenValues: any[], - tokenAddress: any, - tokenDecimalsIndex: any, - tokenWeight: any, - tokenWeightDecimals: any -) { - const weightDecimals = BigNumber.from(10).pow( - BigNumber.from(tokenWeightDecimals || 0) - ); - const weight = BigNumber.from(tokenWeight || 1); - const tokensPerLp = uniReserve.mul(uniPairDecimals).div(uniTotalSupply); - - const tokenDecimals = - tokenDecimalsIndex != null - ? BigNumber.from(10).pow( - BigNumber.from(tokenValues[tokenDecimalsIndex][0] || 0) - ) - : BigNumber.from(1); - log.push(`tokenAddress = ${tokenAddress}`); - log.push(`tokenDecimals = ${tokenDecimals}`); - log.push(`poolStaked = ${poolStaked}`); - log.push(`uniReserve = ${uniReserve}`); - log.push(`uniPairDecimals = ${uniPairDecimals}`); - log.push(`uniTotalSupply = ${uniTotalSupply}`); - log.push(`tokensPerLp = ${tokensPerLp}`); - log.push(`tokenWeight = ${weight}`); - log.push(`tokenWeightDecimals = ${weightDecimals}`); - - printLog(); - - const tokenCount = poolStaked - .mul(tokensPerLp) - .div(tokenDecimals) - .mul(weight) - .div(weightDecimals); - - log.push(`tokenCount = ${tokenCount}`); - - return toFloat(tokenCount, _options.decimals); -} - -function printLog() { - if (_options.log || false) { - console.debug(log); - log = []; - } -} - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - _options = options; - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const userInfoCalls = getUserInfoCalls(addresses); - const tokenCalls = getTokenCalls(); - const entries = new Map(); - - const userInfoResponse = await multicall( - network, - provider, - abi, - userInfoCalls, - { blockTag } - ); - - const userInfoChunks = arrayChunk(userInfoResponse, 1); - - const tokenResponse = await multicall(network, provider, abi, tokenCalls, { - blockTag - }); - - for (let i = 0; i < userInfoChunks.length; i++) { - const value = userInfoChunks[i]; - const score = await processValues( - value, - tokenResponse, - network, - provider, - blockTag - ); - - entries.set(addresses[i], score); - } - - return Object.fromEntries(entries); -} diff --git a/src/strategies/proof-of-humanity/README.md b/src/strategies/proof-of-humanity/README.md index f4853e1d6..34cff1329 100644 --- a/src/strategies/proof-of-humanity/README.md +++ b/src/strategies/proof-of-humanity/README.md @@ -2,7 +2,7 @@ It checks if an address is registered with Proof of Humanity. -It takes the address of the Proof of Humanity contract as a paramter: +It takes the address of the Proof of Humanity contract as a parameter: ```json { diff --git a/src/strategies/protofi-erc721-tier-weighted/README.md b/src/strategies/protofi-erc721-tier-weighted/README.md deleted file mode 100644 index ac298c140..000000000 --- a/src/strategies/protofi-erc721-tier-weighted/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# protofi-erc721-tier-weighted - -This strategy returns the voting power of a wallet given by the sum of the NFT token owned, weighted by the tier of the NFT. Works specifically for Protofi NFTs. -With the parameter "countUsed" you decide if taking into account used NFTs as voting power. - -Here is an example of parameters: - -```json -{ - "address": "0x1aDB6f30561116B4283169DdD1Ca16ed2A34355A", - "symbol": "PNFT", - "tierToWeight": [10,20,30,40,50], - "countUsed": true -} -``` diff --git a/src/strategies/protofi-erc721-tier-weighted/examples.json b/src/strategies/protofi-erc721-tier-weighted/examples.json deleted file mode 100644 index baab1db4b..000000000 --- a/src/strategies/protofi-erc721-tier-weighted/examples.json +++ /dev/null @@ -1,23 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "protofi-erc721-tier-weighted", - "params": { - "address": "0x1aDB6f30561116B4283169DdD1Ca16ed2A34355A", - "symbol": "PNFT", - "tierToWeight": [10, 20, 30, 40, 50], - "countUsed": false - } - }, - "network": "250", - "addresses": [ - "0x5bF13Edc7De3E95029ffDC0C65C193E9bBEBcead", - "0x6f3064d973C08Dd9c88D43080549F474E5827d71", - "0x7751f0B8CfbC18b8931Ae54E234F908014D6D46d", - "0x346F59ac0aF2C85DB1250322b2beD4eEb4c61313", - "0x8a347ec3cB809D9a53d2B8d74e23f08d908e19dc" - ], - "snapshot": 31508266 - } -] diff --git a/src/strategies/protofi-erc721-tier-weighted/index.ts b/src/strategies/protofi-erc721-tier-weighted/index.ts deleted file mode 100644 index de84fa575..000000000 --- a/src/strategies/protofi-erc721-tier-weighted/index.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Multicaller } from '../../utils'; -import { BigNumber } from '@ethersproject/bignumber'; - -export const author = 'theothercrypto'; -export const version = '0.1.0'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256)', - 'function tokenTier(uint256 index) external view returns (uint256)', - 'function tokenUsed(uint256 _id) public view virtual returns (bool)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - // First, get the balance of the token - const callWalletToBalanceOf = new Multicaller(network, provider, abi, { - blockTag - }); - for (const walletAddress of addresses) { - callWalletToBalanceOf.call(walletAddress, options.address, 'balanceOf', [ - walletAddress - ]); - } - const walletToBalanceOf: Record = - await callWalletToBalanceOf.execute(); - - // Second, get the tokenId's for each token - const callWalletToAddresses = new Multicaller(network, provider, abi, { - blockTag - }); - for (const [walletAddress, count] of Object.entries(walletToBalanceOf)) { - for (let index = 0; index < count.toNumber(); index++) { - callWalletToAddresses.call( - walletAddress.toString() + '-' + index.toString(), - options.address, - 'tokenOfOwnerByIndex', - [walletAddress, index] - ); - } - } - const walletIDToAddresses: Record = - await callWalletToAddresses.execute(); - - // Third, given the tokenIds for each token - const callWalletToTiers = new Multicaller(network, provider, abi, { - blockTag - }); - for (const [walletAddress, tokenId] of Object.entries(walletIDToAddresses)) { - callWalletToTiers.call( - walletAddress.toString() + '-' + tokenId.toString(), - options.address, - 'tokenTier', - [tokenId] - ); - } - - const walletIDToTiers: Record = - await callWalletToTiers.execute(); - - // Third, given the tokenIds for each token get if the token is used - const callWalletToUsed = new Multicaller(network, provider, abi, { - blockTag - }); - for (const [walletAddress, tokenId] of Object.entries(walletIDToAddresses)) { - callWalletToUsed.call( - walletAddress.toString() + '-' + tokenId.toString(), - options.address, - 'tokenUsed', - [tokenId] - ); - } - const walletIDToUsed: Record = - await callWalletToUsed.execute(); - - // Ultimately, sum the weights for each tokenId and assign votes based on the - // strategy parameters - const walletToLpBalance = {} as Record; - for (const [walletID, tokenTier] of Object.entries(walletIDToTiers)) { - const address = walletID.split('-')[0]; - const used = walletIDToUsed[walletID]; - if (!options.countUsed && used) { - // Its used and - continue; - } - - // Voting power given by the tier of NFTs owned - const tokenIdValue = options.tierToWeight[tokenTier - 1]; - - walletToLpBalance[address] = walletToLpBalance[address] - ? walletToLpBalance[address].add(BigNumber.from(tokenIdValue)) - : BigNumber.from(tokenIdValue); - } - - return Object.fromEntries( - Object.entries(walletToLpBalance).map(([address, balance]) => [ - address, - balance.toNumber() - ]) - ); -} diff --git a/src/strategies/proxyprotocol-erc1155-balance-of/README.md b/src/strategies/proxyprotocol-erc1155-balance-of/README.md deleted file mode 100644 index af84210fc..000000000 --- a/src/strategies/proxyprotocol-erc1155-balance-of/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Proxy ERC1155 - -This allows for a Proxy wallet to map to multiple wallets owned by the user. - -You would use the exact same parameters as erc1155-balance-of, but the signing wallet is now the proxy wallet. diff --git a/src/strategies/proxyprotocol-erc1155-balance-of/examples.json b/src/strategies/proxyprotocol-erc1155-balance-of/examples.json deleted file mode 100644 index dd62f2028..000000000 --- a/src/strategies/proxyprotocol-erc1155-balance-of/examples.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "proxyprotocol-erc1155-balance-of", - "params": { - "symbol": "ADI", - "address": "0x28472a58a490c5e09a238847f66a68a47cc76f0f", - "tokenId": "1", - "decimals": 0 - } - }, - "network": "1", - "addresses": [ - "0x346f1c338b38ef9cf18964695dd68e9956ca5d37", - "0xa164591f695b11e1c6b77925e326e20754521200" - ], - "snapshot": 15304592 - } -] diff --git a/src/strategies/proxyprotocol-erc1155-balance-of/index.ts b/src/strategies/proxyprotocol-erc1155-balance-of/index.ts deleted file mode 100644 index 38a211f6c..000000000 --- a/src/strategies/proxyprotocol-erc1155-balance-of/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import fetch from 'cross-fetch'; -import { strategy as erc1155BalanceOfStrategy } from '../erc1155-balance-of'; - -export const author = 'rawrjustin'; -export const version = '0.1.0'; - -const calculateVotingPower = (inputAddresses, addressScores, walletMap) => { - const userVotingPower = {}; - inputAddresses.forEach((input) => { - let count = 0.0; - walletMap[input.toLowerCase()].forEach((address) => { - count += addressScores[address]; - }); - userVotingPower[input] = count; - }); - return userVotingPower; -}; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - // Get the wallet mapping from proxy wallets to actual wallets - const url = 'https://api.proxychat.xyz/external/v0/getProxyWalletMappings'; - const params = { - proxyAddresses: addresses - }; - const apiResponse = await fetch(url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify(params) - }); - const data = await apiResponse.json(); - - // Flatten the wallet mapping so it's an array of real wallets to query for tokens - const arrayOfProxyWallets = Object.keys(data).map(function (key) { - return data[key]; - }); - const flattenedWalletAddresses = [].concat.apply([], arrayOfProxyWallets); - - // Query for token holdings - const addressScores = await erc1155BalanceOfStrategy( - space, - network, - provider, - flattenedWalletAddresses, - options, - snapshot - ); - - // Calculate the voting power across all wallets and map it back to original Proxy wallets. - return calculateVotingPower(addresses, addressScores, data); -} diff --git a/src/strategies/proxyprotocol-erc20-balance-of/README.md b/src/strategies/proxyprotocol-erc20-balance-of/README.md deleted file mode 100644 index 12ea50a13..000000000 --- a/src/strategies/proxyprotocol-erc20-balance-of/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Proxy ERC-20 - -This allows for a Proxy wallet to map to multiple wallets owned by the user. - -You would use the exact same parameters as erc20-balance-of, but the signing wallet is now the proxy wallet. diff --git a/src/strategies/proxyprotocol-erc20-balance-of/examples.json b/src/strategies/proxyprotocol-erc20-balance-of/examples.json deleted file mode 100644 index 89db17a96..000000000 --- a/src/strategies/proxyprotocol-erc20-balance-of/examples.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "proxyprotocol-erc20-balance-of", - "params": { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "symbol": "WETH", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x346f1c338b38ef9cf18964695dd68e9956ca5d37", - "0xa164591f695b11e1c6b77925e326e20754521200" - ], - "snapshot": 15304592 - } -] diff --git a/src/strategies/proxyprotocol-erc20-balance-of/index.ts b/src/strategies/proxyprotocol-erc20-balance-of/index.ts deleted file mode 100644 index ba3fabe34..000000000 --- a/src/strategies/proxyprotocol-erc20-balance-of/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import fetch from 'cross-fetch'; -import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; - -export const author = 'rawrjustin'; -export const version = '0.1.0'; - -const calculateVotingPower = (inputAddresses, addressScores, walletMap) => { - const userVotingPower = {}; - inputAddresses.forEach((input) => { - let count = 0.0; - walletMap[input.toLowerCase()].forEach((address) => { - count += addressScores[address]; - }); - userVotingPower[input] = count; - }); - return userVotingPower; -}; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - // Get the wallet mapping from proxy wallets to actual wallets - const url = 'https://api.proxychat.xyz/external/v0/getProxyWalletMappings'; - const params = { - proxyAddresses: addresses - }; - const response = await fetch(url, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify(params) - }); - const data = await response.json(); - - // Flatten the wallet mapping so it's an array of real wallets to query for tokens - const arrayOfProxyWallets = Object.keys(data).map(function (key) { - return data[key]; - }); - const flattenedWalletAddresses = [].concat.apply([], arrayOfProxyWallets); - - // Query for token holdings - const addressScores = await erc20BalanceOfStrategy( - space, - network, - provider, - flattenedWalletAddresses, - options, - snapshot - ); - - // Calculate the voting power across all wallets and map it back to original Proxy wallets. - return calculateVotingPower(addresses, addressScores, data); -} diff --git a/src/strategies/radicle-community-tokens/README.md b/src/strategies/radicle-community-tokens/README.md deleted file mode 100644 index 7b7209670..000000000 --- a/src/strategies/radicle-community-tokens/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# radicle-community-tokens - -This strategy is used to calculate voting power from the Radicle funding subgraph with the entity NFT and field `amtPerSec`. Each voter can have multiple NFTs, each with specific `amtPerSec`. The voters' voting power would be the sum of all the `amtPerSec`. If a fundingProject `id` is provided the summation is done only over NFTs of that project. - -Here is an example of parameters: - -```json -{ - "symbol": "DAI", - "decimals": 18, - "fundingProject": "0x488fc5d6e43259f87f816556a7ab99dc78cfbab6" -} -``` diff --git a/src/strategies/radicle-community-tokens/examples.json b/src/strategies/radicle-community-tokens/examples.json deleted file mode 100644 index 0ddc2f6c3..000000000 --- a/src/strategies/radicle-community-tokens/examples.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "radicle-community-tokens", - "params": { - "symbol": "DAI", - "decimals": 18, - "fundingProject": "0x488fc5d6e43259f87f816556a7ab99dc78cfbab6" - } - }, - "network": "4", - "addresses": [ - "0xeCa823848221a1DA310E1a711E19D82F43101B07", - "0xb5bb9A125c2F67F1F2cd9d8992955bb209490aFE" - ], - "snapshot": 9601000 - } -] diff --git a/src/strategies/radicle-community-tokens/index.ts b/src/strategies/radicle-community-tokens/index.ts deleted file mode 100644 index d3aedb53c..000000000 --- a/src/strategies/radicle-community-tokens/index.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { BigNumber } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { getAddress } from '@ethersproject/address'; -import { subgraphRequest } from '../../utils'; - -const FUNDING_SUBGRAPH_URL = { - '4': 'https://api.studio.thegraph.com/query/9578/funding-subgraph-v5/v0.0.1' // Rinkeby testnet -}; - -export const author = 'AmirSarraf'; -export const version = '0.1.0'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - let params = {}; - const fundingProject = options.fundingProject; - const mainField: string = fundingProject ? 'fundingProjects' : 'nfts'; - - if (fundingProject) { - // parameters to query nfts belonging to the provided addresses in a certain fundingProject - params = { - fundingProjects: { - __args: { - id: fundingProject.toLowerCase() - }, - id: true, - nfts: { - __args: { - where: { - nftReceiver_in: addresses.map((address) => address.toLowerCase()) - } - }, - amtPerSec: true, - nftReceiver: true - } - } - }; - } else { - // parameters to query nfts belonging to the provided addresses - params = { - nfts: { - __args: { - where: { - nftReceiver_in: addresses.map((address) => address.toLowerCase()) - }, - first: 1000 - }, - amtPerSec: true, - nftReceiver: true - } - }; - } - - if (snapshot !== 'latest') { - // @ts-ignore - params[mainField].__args.block = { number: snapshot }; - } - - let result = await subgraphRequest(FUNDING_SUBGRAPH_URL[network], params); - result = fundingProject - ? result?.fundingProjects?.find((proj) => proj.id == fundingProject) //double checking id - : result; - - const score: Record = {}; - if (result && result.nfts) { - result.nfts.forEach((nft) => { - const userAddress = getAddress(nft.nftReceiver); - const userScore = nft.amtPerSec; - if (!score[userAddress]) score[userAddress] = BigNumber.from(0); - score[userAddress] = score[userAddress].add(BigNumber.from(userScore)); - }); - } - - return Object.fromEntries( - Object.entries(score).map(([address, balance]) => [ - address, - parseFloat(formatUnits(balance.toString(), options.decimals)) - ]) - ); -} diff --git a/src/strategies/rdnt-capital-voting/examples.json b/src/strategies/rdnt-capital-voting/examples.json index cffa2db05..a5805037b 100644 --- a/src/strategies/rdnt-capital-voting/examples.json +++ b/src/strategies/rdnt-capital-voting/examples.json @@ -36,5 +36,25 @@ "0x2eAA7327e9B5Ff46bc2B7452acE9e44A1528eb84" ], "snapshot": 26920121 + }, + { + "name": "RDNT in dLP - Mainnet", + "strategy": { + "name": "rdnt-capital-voting", + "params": { + "rdnt": "0x137dDB47Ee24EaA998a535Ab00378d6BFa84F893", + "lpToken": "0xcF7b51ce5755513d4bE016b0e28D6EDEffa1d52a", + "lockingContract": "0x28E395a54a64284DBA39652921Cd99924f4e3797", + "balancerPoolId": "0xcf7b51ce5755513d4be016b0e28d6edeffa1d52a000200000000000000000617", + "balancerVault": "0xBA12222222228d8Ba445958a75a0704d566BF2C8" + } + }, + "network": "1", + "addresses": [ + "0x2eAA7327e9B5Ff46bc2B7452acE9e44A1528eb84", + "0x8EDdC30B12A5Cc60d046fc1B1b887eb2c1353a87", + "0x4011091Dbe57bd4521F598616fe4BB3978ea3005" + ], + "snapshot": 18483999 } ] diff --git a/src/strategies/rdnt-capital-voting/index.ts b/src/strategies/rdnt-capital-voting/index.ts index 72ad21a47..57a64cd9a 100644 --- a/src/strategies/rdnt-capital-voting/index.ts +++ b/src/strategies/rdnt-capital-voting/index.ts @@ -2,7 +2,7 @@ import { BigNumberish } from '@ethersproject/bignumber'; import { formatUnits } from '@ethersproject/units'; import { Multicaller, multicall, call } from '../../utils'; -export const author = 'JDoy99'; +export const author = 'JD0x2e'; export const version = '0.1.0'; const erc20Abi = [ @@ -84,7 +84,7 @@ export async function strategy( // Get RDNT per LP token (LP provider dependent) let rdntPerLp; - if (network === '42161') { + if (network === '42161' || network === '1') { rdntPerLp = await rdntPerBalancerLpToken( network, provider, diff --git a/src/strategies/rebased/examples.json b/src/strategies/rebased/examples.json deleted file mode 100644 index bfc39d664..000000000 --- a/src/strategies/rebased/examples.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "name": "Rebased", - "strategy": { - "name": "rebased", - "params": { - "symbol": "REB", - "uniswap": "0x54f5f952cca8888227276581F26978F99FDBa64E", - "sharePool": "0x574D80f005B9f5a26e6D4E0bcbD379EABD7edEb0", - "token": "0x87f5f9ebe40786d49d35e1b5997b07ccaa8adbff", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0xde389fb34f54bec6d09bd98152a69be15c43163f", - "0xbe80fd79e26ce0a605e5d2803e876f1b009d70cc", - "0x55c307cbe54a1c1c105838a9d0fd60b75d7ff951", - "0x01BaD7E976d59CE92295Dbacd5da7fc06FE05412", - "0x64be824f732312490224a67537d49b8580068abf" - ], - "snapshot": 11941425 - } -] diff --git a/src/strategies/rebased/index.ts b/src/strategies/rebased/index.ts deleted file mode 100644 index 97923be5e..000000000 --- a/src/strategies/rebased/index.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { formatUnits, parseUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = 'codingsh'; -export const version = '0.1.0'; - -const abi = [ - { - constant: true, - inputs: [ - { - internalType: 'address', - name: '', - type: 'address' - } - ], - name: 'balanceOf', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function' - }, - { - inputs: [], - name: 'totalSupply', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256' - } - ], - stateMutability: 'view', - type: 'function' - }, - { - constant: true, - inputs: [ - { - internalType: 'address', - name: 'addr', - type: 'address' - } - ], - name: 'totalStakedFor', - outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], - payable: false, - stateMutability: 'view', - type: 'function' - } -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - - multi.call('uniswapBalance', options.token, 'balanceOf', [options.uniswap]); - multi.call('uniswapTotalSupply', options.uniswap, 'totalSupply'); - addresses.forEach((address) => { - multi.call( - `scores.${address}.totalStaked`, - options.sharePool, - 'totalStakedFor', - [address] - ); - multi.call(`scores.${address}.uniswap`, options.uniswap, 'balanceOf', [ - address - ]); - multi.call(`scores.${address}.balance`, options.token, 'balanceOf', [ - address - ]); - }); - - const result = await multi.execute(); - const rebasedPerLP = result.uniswapBalance; - - return Object.fromEntries( - Array(addresses.length) - .fill('') - .map((_, i) => { - const lpBalances = result.scores[addresses[i]].uniswap; - const stakedLpBalances = result.scores[addresses[i]].totalStaked; - const tokenBalances = result.scores[addresses[i]].balance; - const lpBalance = lpBalances.add(stakedLpBalances); - const rebasedLpBalance = lpBalance - .add(tokenBalances) - .mul(rebasedPerLP) - .div(parseUnits('1', 18)); - return [ - addresses[i], - parseFloat(formatUnits(rebasedLpBalance, options.decimals)) - ]; - }) - ); -} diff --git a/src/strategies/riskharbor-underwriter/README.md b/src/strategies/riskharbor-underwriter/README.md deleted file mode 100644 index ebd323100..000000000 --- a/src/strategies/riskharbor-underwriter/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# riskharbor-underwriter - -This strategy allows underwriters in a given Risk Harbor vault to vote based on the shares issued to them across their various positions in that vault. This strategy works by querying the vault's subgraph to compute how many shares each user holds and divides that amount by the decimals in the underwriting asset. - -```json -{ - "SUBGRAPH_URL": "https://api.thegraph.com/subgraphs/name/some-protocol/v1-protocol", - "VAULT_ADDR": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" -} -``` diff --git a/src/strategies/riskharbor-underwriter/examples.json b/src/strategies/riskharbor-underwriter/examples.json deleted file mode 100644 index 154fe0f76..000000000 --- a/src/strategies/riskharbor-underwriter/examples.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "riskharbor-underwriter", - "params": { - "SUBGRAPH_URL": "https://api.thegraph.com/subgraphs/name/risk-harbor/v2-arbitrum", - "VAULT_ADDR": "0xbcA81A2118982182d897845571BE950aE94C619c" - } - }, - "network": "42161", - "addresses": [ - "0xD4D4e905d7F1Eb095769fAce2C2bE516865E4981", - "0xd401c5B54A079420C6C7D9405faFc9a10CD8a4ed", - "0x010dab3779810cf08ac213b9efa915821bb43e26", - "0x0dcdd4f4a70ebb2eaffd5a01bd6cacde14dae4f0", - "0xfc9bffa77c2b725add71f4ad88bbe228d5601eb3", - "0xc2e63f57958d5ad5ced8fc18a7b763d9c8327237" - ], - "snapshot": 22260293 - } -] diff --git a/src/strategies/riskharbor-underwriter/index.ts b/src/strategies/riskharbor-underwriter/index.ts deleted file mode 100644 index a615a771f..000000000 --- a/src/strategies/riskharbor-underwriter/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { getAddress } from '@ethersproject/address'; -import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { subgraphRequest } from '../../utils'; - -export const author = 'dewpe'; -export const version = '0.1.1'; - -export async function strategy( - _space, - _network, - _provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const positionsQuery = { - underwriterPositions: { - __args: { - where: { - shares_not: '0', - vault: options.VAULT_ADDR.toLowerCase(), - user_in: addresses.map((addr: string) => addr.toLowerCase()) - }, - block: blockTag != 'latest' ? { number: blockTag } : null, - first: 1000 - }, - shares: true, - user: { - id: true - } - } - }; - - const decimalsQuery = { - vault: { - __args: { - id: options.VAULT_ADDR.toLowerCase(), - block: blockTag != 'latest' ? { number: blockTag } : null - }, - underwritingToken: { - decimals: true - } - } - }; - - const decimals = (await subgraphRequest(options.SUBGRAPH_URL, decimalsQuery)) - .vault.underwritingToken.decimals; - - const positions = ( - await subgraphRequest(options.SUBGRAPH_URL, positionsQuery) - ).underwriterPositions; - - // Go through each position and reduce it down to the form: - // userAddr: balance - const agUserBals: Record = {}; - positions.forEach((position) => { - const shares = BigNumber.from(position.shares); - if (shares.isZero()) return; - // If key already has a value, then increase it - if (agUserBals[position.user.id]) { - agUserBals[position.user.id] = ( - agUserBals[position.user.id] as BigNumber - ).add(shares); - } else { - agUserBals[position.user.id] = shares; - } - }); - - return Object.fromEntries( - Object.entries(agUserBals).map(([address, balance]) => [ - getAddress(address), - // Divide each bal by 1eDecimals - parseFloat(formatUnits(balance, decimals)) - ]) - ); -} diff --git a/src/strategies/ruler-staked-token/README.md b/src/strategies/ruler-staked-token/README.md deleted file mode 100644 index 493bd5251..000000000 --- a/src/strategies/ruler-staked-token/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# ruler-staked-token - -This strategy returns the balance of a specific ERC20 token staked in a ruler-style contract. - -Here is an example of parameters: - -```json -{ - "tokenAddress": "0x2aECCB42482cc64E087b6D2e5Da39f5A7A7001f8", - "stakingAddress": "0x3423c8Af3a95D9FEE7Ec06c4e0E905D4fd559F89", - "symbol": "RULER", - "decimals": 18 -} -``` diff --git a/src/strategies/ruler-staked-token/examples.json b/src/strategies/ruler-staked-token/examples.json deleted file mode 100644 index 3045a5202..000000000 --- a/src/strategies/ruler-staked-token/examples.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "ruler-staked-token", - "params": { - "tokenAddress": "0xb1EECFea192907fC4bF9c4CE99aC07186075FC51", - "stakingAddress": "0x3423c8Af3a95D9FEE7Ec06c4e0E905D4fd559F89", - "symbol": "RULER-WETH SLP", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x0291eb432CB4a2613a7415018933E3Db45Bcd769", - "0x7DE2bF548eaAd49588eB334696d7C2d4443C0575", - "0xBDbFdf3e82fC9d2bE1352e252aB1Ce2287fC2122", - "0x17EA85484cD4E97bE63fC02F20a196EDEAa937a9" - ], - "snapshot": 12777196 - } -] diff --git a/src/strategies/ruler-staked-token/index.ts b/src/strategies/ruler-staked-token/index.ts deleted file mode 100644 index 35aa25da4..000000000 --- a/src/strategies/ruler-staked-token/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; - -export const author = 'drop-out-dev'; -export const version = '0.1.0'; - -const abi = [ - { - inputs: [ - { internalType: 'address', name: '_lpToken', type: 'address' }, - { internalType: 'address', name: '_account', type: 'address' } - ], - name: 'getUser', - outputs: [ - { - components: [ - { internalType: 'uint256', name: 'amount', type: 'uint256' }, - { - internalType: 'uint256[]', - name: 'rewardsWriteoffs', - type: 'uint256[]' - } - ], - internalType: 'struct IBonusRewards.User', - name: '', - type: 'tuple' - }, - { internalType: 'uint256[]', name: '', type: 'uint256[]' } - ], - stateMutability: 'view', - type: 'function' - } -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const response = await multicall( - network, - provider, - abi, - addresses.map((address: any) => [ - options.stakingAddress, - 'getUser', - [options.tokenAddress, address] - ]), - { blockTag } - ); - return Object.fromEntries( - response.map(([userInfo], i) => [ - addresses[i], - parseFloat(formatUnits(userInfo.amount, options.decimals)) - ]) - ); -} diff --git a/src/strategies/saddle-finance-v2/README.md b/src/strategies/saddle-finance-v2/README.md deleted file mode 100644 index d9ba0d12e..000000000 --- a/src/strategies/saddle-finance-v2/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Saddle Finance Governance Strategy - -Custom strategy which includes unclaimed SDL tokens in the user's wallet, retroactive drop and team/advisor/investor vesting contracts. diff --git a/src/strategies/saddle-finance-v2/examples.json b/src/strategies/saddle-finance-v2/examples.json deleted file mode 100644 index 22ae6a208..000000000 --- a/src/strategies/saddle-finance-v2/examples.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "name": "Saddle Finance", - "strategy": { - "name": "saddle-finance", - "params": { - "symbol": "SDL" - } - }, - "network": "1", - "addresses": [ - "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B", - "0x664f45ed5084abcf2f8e1a95e320b06cc700591b", - "0xf4a0e9ad90f441670016742c8977f79553ee8ee8", - "0x243fFB6d39aD73327242D08329b8273B81ed0Ab0", - "0xcb10d759caaa8ec12a4d2e59f9d55018dd8b1c9a" - ], - "snapshot": 13754897 - } -] diff --git a/src/strategies/saddle-finance-v2/index.ts b/src/strategies/saddle-finance-v2/index.ts deleted file mode 100644 index a68252422..000000000 --- a/src/strategies/saddle-finance-v2/index.ts +++ /dev/null @@ -1,153 +0,0 @@ -import fetch from 'cross-fetch'; -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; -import { vestingContractAddrs } from './vestingContractAddrs'; - -export const author = 'saddle-finance'; -export const version = '0.1.0'; - -const SDLTokenAddress = '0xf1Dc500FdE233A4055e25e5BbF516372BC4F6871'; -const RetroRewardsContract = '0x5DCA270671935cf3dF78bd8373C22BE250198a03'; - -const abi = [ - 'function balanceOf(address) external view returns (uint256)', - 'function beneficiary() external view returns (address)', - 'function vestings(address) external view returns (bool isVerified, uint120 totalAmount, uint120 released)', - 'function vestedAmount() public view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const remappedMerkleDataRes = await fetch( - 'https://gateway.pinata.cloud/ipfs/QmV73GEaijyiBFHu1vRdZBFffoCHaXYWG5SpurbEgr4VK6', - { - method: 'GET', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - } - } - ); - const remappedMerkleData = await remappedMerkleDataRes.json(); - - const userWalletBalanceResponse = multicall( - network, - provider, - abi, - addresses.map((address: any) => [ - SDLTokenAddress, - 'balanceOf', - [address.toLowerCase()] - ]), - { blockTag } - ); - - const beneficiaries = multicall( - network, - provider, - abi, - vestingContractAddrs.map((vestingContractAddress: any) => [ - vestingContractAddress.toLowerCase(), - 'beneficiary' - ]), - { blockTag } - ); - - const vestedAndUnclaimedAmountRes = multicall( - network, - provider, - abi, - vestingContractAddrs.map((vestingContractAddress: any) => [ - vestingContractAddress.toLowerCase(), - 'vestedAmount' - ]), - { blockTag } - ); - - const retroAddrs = Object.keys(remappedMerkleData); - - const userVestingsRes = multicall( - network, - provider, - abi, - retroAddrs.map((retroAddr: any) => [ - RetroRewardsContract, - 'vestings', - [retroAddr.toLowerCase()] - ]), - { blockTag } - ); - - const balances = await Promise.all([ - userWalletBalanceResponse, - vestedAndUnclaimedAmountRes, - beneficiaries, - userVestingsRes - ]); - - const retroUserBalances = {}; - retroAddrs.forEach((addr, i) => { - const userVesting = balances[3][i]; - if (userVesting?.isVerified) { - retroUserBalances[addr.toLowerCase()] = parseFloat( - formatUnits( - userVesting.totalAmount.sub(userVesting.released).toString(), - 18 - ) - ); - } else { - retroUserBalances[addr.toLowerCase()] = parseFloat( - formatUnits(remappedMerkleData[addr].amount, 18) - ); - } - }); - - const mappedBeneficiariesToUnclaimedAmount = balances[2].reduce( - (acc, addr, i) => ({ - ...acc, - [addr]: parseFloat(formatUnits(balances[1][i][0].toString(), 18)) - }), - {} - ); - - const userWalletBalances = balances[0].map((amount, i) => { - return [ - addresses[i].toLowerCase(), - parseFloat(formatUnits(amount.toString(), 18)) - ]; - }); - - const userTotal = {}; - // loop through user, investor/advisor/team-member, and airdrop wallets to calculate total. - userWalletBalances.forEach(([address, amount]) => { - const addr = address.toLowerCase(); - if (userTotal[addr]) userTotal[addr] += amount; - else userTotal[addr] = amount; - }); - for (const [address, amount] of Object.entries(retroUserBalances)) { - const addr = address.toLowerCase(); - if (userTotal[addr]) userTotal[addr] += amount; - else userTotal[addr] = amount; - } - for (const [address, amount] of Object.entries( - mappedBeneficiariesToUnclaimedAmount - )) { - const addr = address.toLowerCase(); - if (userTotal[addr]) userTotal[addr] += amount; - else userTotal[addr] = amount; - } - - const finalUserBalances = Object.fromEntries( - addresses.map((addr) => [addr, userTotal[addr.toLowerCase()]]) - ); - - return finalUserBalances; -} diff --git a/src/strategies/saddle-finance-v2/vestingContractAddrs.ts b/src/strategies/saddle-finance-v2/vestingContractAddrs.ts deleted file mode 100644 index 5034e7a78..000000000 --- a/src/strategies/saddle-finance-v2/vestingContractAddrs.ts +++ /dev/null @@ -1,74 +0,0 @@ -export const vestingContractAddrs = [ - '0x5dfbceea7a5f6556356c7a66d2a43332755d68a5', - '0xa440423cc4731909d21cda5b80cdf4a0e998a046', - '0x1e82992cd3f1f495827b545fa1d0845316c3404d', - '0xafc5d02588035124273291e35cacc11ce4249295', - '0xd17c31796d3cb41d9d211904780320c4be286172', - '0x85f99b73d0edd9cdb3462c94ebe4c5758684bdf1', - '0x92ff688d17504ff04f6551150ff34de61cf6f772', - '0x3f2763cace9b48f0cdbe84e049b5695ce3cf7d7e', - '0xb960fafebb589ca3500eb9350eea503548bccfc2', - '0xc7b2f1a2d0838370f88a2fd5c2e3f64d8af89a18', - '0x85c77d06f326381390b619ef202fe8fb9ce40679', - '0xcdec570c3eff689d97eaf3ad9eb31993dcf04f51', - '0xeed792fda7bd79398d4f3cc28f02bb65bfb7700f', - '0x878a65846a37b8cb117662bfdda596ed99b50f0d', - '0x5f4d8017ab0b5476a7177b2f1200f1ccf23f396d', - '0x8e0b95b6040188ac4a51da2eaf11ef93cc9af89f', - '0xa382a5427b387a8ea419d7259496d5b5d8930d43', - '0xcd57f671c59e32af35258c19ed112bac6c5db48b', - '0xf8264afe6483e7149ad9bc9d27759e37ce03f0ed', - '0x32b58b1bc7d10d5313b87b4e45c17d9cd342dcf6', - '0x7024716497d385ad9e5762a17e5d91893af5a47b', - '0xb8196a14c3318eb39518bc1977b99ea000e02f66', - '0xfdc134499b7de70ee88f4594761b8f6acf9c64a0', - '0xf5d69f455474f1f78654b08138178622dc20651a', - '0x64cac463ac033534bdbc94b9da06193b95cf779f', - '0x2a611277d378b475ba7bad5d601a94d19f6a5eb3', - '0x12ce3e43d2d6d793f2af61ff8e8ae7df88704b32', - '0xb5c81597f982dedf7452aecfe9ea0d2317d0a6cb', - '0x3ce780be5cfa346f60d1919451ec0dc9df316a12', - '0xa5a5f2cdefbbec9b107032edaa737c0b947acc9c', - '0xf1dfa2b7331f31317d15d74121485068589e0d8d', - '0x8a81e676d2f32c9cbaa0f5ea48d36ef7172eda97', - '0x94fcefc941ef42510e166746c9a8ab8fe4933cbc', - '0xb6fa5b81f6898b9acfa2d5af352b3ae25105028b', - '0x6672fbe9793970fd762ed7a48cbae81db7bb0a5e', - '0x7a1c42297c00823736fd91e3d0f2cc7ca848e98e', - '0xc896e23a786b51a55fe0c2d5091faa4bf2ee0896', - '0xa0e5ca644b026377f8f280e35438bf8acb0b5790', - '0x5c17b22a49ac26305d9a001fdc41733e59d868d0', - '0xdf239a0397c4f1d39b4cf414a1a06aa0f3797fba', - '0x76cb506fc99c10000145796b7e5e00d91b06829b', - '0x338179f237eecc39d3c0ad1a776ad02b1bf3761a', - '0xe1aeca359b91eadcd9934b3584b39fefff4c3b16', - '0xcfb49d2b349d389c41e5f915d1250e36a4eb42ca', - '0xee08c493a458876520813b256e9688eaede6a91a', - '0xdddf8b5c211fd97967eda1b7ad6330b9066bdee4', - '0x971b5eda88a400974556ac82d37389de8f140543', - '0xd6c29b1a8106584dc21ac3db4f4863e3caa47a60', - '0x5412a79e9cae0bad06bd9dd33f97ae2e196519e1', - '0x3e5f69698628b92e0a47f9c2c9e14ab892216096', - '0xe68319e9389554af7fe3f7ed41ff1901632634b6', - '0x493ecf1ece448ee83f72098cff0e196fb2948cb9', - '0x3d2ab86da84b2496168e5cf841d42a4ac27511d3', - '0x3bbfb974fc85286ca6db8162c312459a92f3e302', - '0xfde9512c0c4d7b092674229a42ab4aea5f743da1', - '0x597e475a5ddd90b3eb2135ac47319bd866f685d8', - '0xb39c77901054766662b77e3269d3b622e7cacab4', - '0x0622927ecf00406d48d05c39134bcd53ed396cb4', - '0xdcae005fcf34cf3e2b12b662dded94d0f7bf2977', - '0xdc0e9a031e9fc09681495a5ae5912954cbd858e4', - '0xbe3e1228d471fc747d7a4c68823910153ee552a5', - '0x45545bd03ccb1cd84d3c8f000a7d6c709d84720d', - '0x1ac13ff6e1bbca5b49c3f12468689289fb93c388', - '0x3cfd17f9cf57164ed64b91d25f72c2c6dfaeab48', - '0xaf8094420749b0131200b8e85f5018688261f110', - '0x039827df17ae5449b31162ec579bbbbe72300188', - '0xc7b3dbc8424a11255cf895c2916f24e0063dcdc3', - '0x2f246c27ed9f4839dff70233ce250a3f6024f484', - '0xa663ae21db74048e50401f542703e0802a3afeb9', - '0xf8b70d8cf29ee045acc5623ebe61037b33228fd1', - '0x5c85b43468da23f86016f508f14ca927bfd8a737', - '0x41092b4ecf2c4db719ec5ab67dbd0c66f095ee97' -]; diff --git a/src/strategies/saddle-finance/README.md b/src/strategies/saddle-finance/README.md deleted file mode 100644 index d9ba0d12e..000000000 --- a/src/strategies/saddle-finance/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Saddle Finance Governance Strategy - -Custom strategy which includes unclaimed SDL tokens in the user's wallet, retroactive drop and team/advisor/investor vesting contracts. diff --git a/src/strategies/saddle-finance/examples.json b/src/strategies/saddle-finance/examples.json deleted file mode 100644 index 22ae6a208..000000000 --- a/src/strategies/saddle-finance/examples.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "name": "Saddle Finance", - "strategy": { - "name": "saddle-finance", - "params": { - "symbol": "SDL" - } - }, - "network": "1", - "addresses": [ - "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B", - "0x664f45ed5084abcf2f8e1a95e320b06cc700591b", - "0xf4a0e9ad90f441670016742c8977f79553ee8ee8", - "0x243fFB6d39aD73327242D08329b8273B81ed0Ab0", - "0xcb10d759caaa8ec12a4d2e59f9d55018dd8b1c9a" - ], - "snapshot": 13754897 - } -] diff --git a/src/strategies/saddle-finance/index.ts b/src/strategies/saddle-finance/index.ts deleted file mode 100644 index 44a22533a..000000000 --- a/src/strategies/saddle-finance/index.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; -import fetch from 'cross-fetch'; -import { vestingContractAddrs } from './vestingContractAddrs'; - -export const author = 'saddle-finance'; -export const version = '0.1.0'; - -const SDLTokenAddress = '0xf1Dc500FdE233A4055e25e5BbF516372BC4F6871'; -const RetroRewardsContract = '0x5DCA270671935cf3dF78bd8373C22BE250198a03'; - -const abi = [ - 'function balanceOf(address) external view returns (uint256)', - 'function beneficiary() external view returns (address)', - 'function vestings(address) external view returns (bool isVerified, uint120 totalAmount, uint120 released)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const remappedMerkleDataRes = await fetch( - 'https://gateway.pinata.cloud/ipfs/QmV73GEaijyiBFHu1vRdZBFffoCHaXYWG5SpurbEgr4VK6', - { - method: 'GET', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - } - } - ); - const remappedMerkleData = await remappedMerkleDataRes.json(); - - const userWalletBalanceResponse = multicall( - network, - provider, - abi, - addresses.map((address: any) => [ - SDLTokenAddress, - 'balanceOf', - [address.toLowerCase()] - ]), - { blockTag } - ); - - const vestingAddrsBalanceRes = multicall( - network, - provider, - abi, - vestingContractAddrs.map((vestingContractAddress: any) => [ - SDLTokenAddress, - 'balanceOf', - [vestingContractAddress.toLowerCase()] - ]), - { blockTag } - ); - - const beneficiaries = multicall( - network, - provider, - abi, - vestingContractAddrs.map((vestingContractAddress: any) => [ - vestingContractAddress.toLowerCase(), - 'beneficiary' - ]), - { blockTag } - ); - - const retroAddrs = Object.keys(remappedMerkleData); - - const userVestingsRes = multicall( - network, - provider, - abi, - retroAddrs.map((retroAddr: any) => [ - RetroRewardsContract, - 'vestings', - [retroAddr.toLowerCase()] - ]), - { blockTag } - ); - - const balances = await Promise.all([ - userWalletBalanceResponse, - vestingAddrsBalanceRes, - beneficiaries, - userVestingsRes - ]); - - const retroUserBalances = {}; - retroAddrs.forEach((addr, i) => { - const userVesting = balances[3][i]; - if (userVesting?.isVerified) { - retroUserBalances[addr.toLowerCase()] = parseFloat( - formatUnits( - userVesting.totalAmount.sub(userVesting.released).toString(), - 18 - ) - ); - } else { - retroUserBalances[addr.toLowerCase()] = parseFloat( - formatUnits(remappedMerkleData[addr].amount, 18) - ); - } - }); - - const mappedBeneficiariesToVestingContract = balances[2].reduce( - (acc, addr, i) => ({ - ...acc, - [addr]: parseFloat(formatUnits(balances[1][i][0].toString(), 18)) - }), - {} - ); - - const userWalletBalances = balances[0].map((amount, i) => { - return [ - addresses[i].toLowerCase(), - parseFloat(formatUnits(amount.toString(), 18)) - ]; - }); - - const userTotal = {}; - // loop through user, investor/advisor/team-member, and airdrop wallets to calculate total. - userWalletBalances.forEach(([address, amount]) => { - const addr = address.toLowerCase(); - if (userTotal[addr]) userTotal[addr] += amount; - else userTotal[addr] = amount; - }); - for (const [address, amount] of Object.entries(retroUserBalances)) { - const addr = address.toLowerCase(); - if (userTotal[addr]) userTotal[addr] += amount; - else userTotal[addr] = amount; - } - for (const [address, amount] of Object.entries( - mappedBeneficiariesToVestingContract - )) { - const addr = address.toLowerCase(); - if (userTotal[addr]) userTotal[addr] += amount; - else userTotal[addr] = amount; - } - - const finalUserBalances = Object.fromEntries( - addresses.map((addr) => [addr, userTotal[addr.toLowerCase()]]) - ); - - return finalUserBalances; -} diff --git a/src/strategies/saddle-finance/vestingContractAddrs.ts b/src/strategies/saddle-finance/vestingContractAddrs.ts deleted file mode 100644 index 5034e7a78..000000000 --- a/src/strategies/saddle-finance/vestingContractAddrs.ts +++ /dev/null @@ -1,74 +0,0 @@ -export const vestingContractAddrs = [ - '0x5dfbceea7a5f6556356c7a66d2a43332755d68a5', - '0xa440423cc4731909d21cda5b80cdf4a0e998a046', - '0x1e82992cd3f1f495827b545fa1d0845316c3404d', - '0xafc5d02588035124273291e35cacc11ce4249295', - '0xd17c31796d3cb41d9d211904780320c4be286172', - '0x85f99b73d0edd9cdb3462c94ebe4c5758684bdf1', - '0x92ff688d17504ff04f6551150ff34de61cf6f772', - '0x3f2763cace9b48f0cdbe84e049b5695ce3cf7d7e', - '0xb960fafebb589ca3500eb9350eea503548bccfc2', - '0xc7b2f1a2d0838370f88a2fd5c2e3f64d8af89a18', - '0x85c77d06f326381390b619ef202fe8fb9ce40679', - '0xcdec570c3eff689d97eaf3ad9eb31993dcf04f51', - '0xeed792fda7bd79398d4f3cc28f02bb65bfb7700f', - '0x878a65846a37b8cb117662bfdda596ed99b50f0d', - '0x5f4d8017ab0b5476a7177b2f1200f1ccf23f396d', - '0x8e0b95b6040188ac4a51da2eaf11ef93cc9af89f', - '0xa382a5427b387a8ea419d7259496d5b5d8930d43', - '0xcd57f671c59e32af35258c19ed112bac6c5db48b', - '0xf8264afe6483e7149ad9bc9d27759e37ce03f0ed', - '0x32b58b1bc7d10d5313b87b4e45c17d9cd342dcf6', - '0x7024716497d385ad9e5762a17e5d91893af5a47b', - '0xb8196a14c3318eb39518bc1977b99ea000e02f66', - '0xfdc134499b7de70ee88f4594761b8f6acf9c64a0', - '0xf5d69f455474f1f78654b08138178622dc20651a', - '0x64cac463ac033534bdbc94b9da06193b95cf779f', - '0x2a611277d378b475ba7bad5d601a94d19f6a5eb3', - '0x12ce3e43d2d6d793f2af61ff8e8ae7df88704b32', - '0xb5c81597f982dedf7452aecfe9ea0d2317d0a6cb', - '0x3ce780be5cfa346f60d1919451ec0dc9df316a12', - '0xa5a5f2cdefbbec9b107032edaa737c0b947acc9c', - '0xf1dfa2b7331f31317d15d74121485068589e0d8d', - '0x8a81e676d2f32c9cbaa0f5ea48d36ef7172eda97', - '0x94fcefc941ef42510e166746c9a8ab8fe4933cbc', - '0xb6fa5b81f6898b9acfa2d5af352b3ae25105028b', - '0x6672fbe9793970fd762ed7a48cbae81db7bb0a5e', - '0x7a1c42297c00823736fd91e3d0f2cc7ca848e98e', - '0xc896e23a786b51a55fe0c2d5091faa4bf2ee0896', - '0xa0e5ca644b026377f8f280e35438bf8acb0b5790', - '0x5c17b22a49ac26305d9a001fdc41733e59d868d0', - '0xdf239a0397c4f1d39b4cf414a1a06aa0f3797fba', - '0x76cb506fc99c10000145796b7e5e00d91b06829b', - '0x338179f237eecc39d3c0ad1a776ad02b1bf3761a', - '0xe1aeca359b91eadcd9934b3584b39fefff4c3b16', - '0xcfb49d2b349d389c41e5f915d1250e36a4eb42ca', - '0xee08c493a458876520813b256e9688eaede6a91a', - '0xdddf8b5c211fd97967eda1b7ad6330b9066bdee4', - '0x971b5eda88a400974556ac82d37389de8f140543', - '0xd6c29b1a8106584dc21ac3db4f4863e3caa47a60', - '0x5412a79e9cae0bad06bd9dd33f97ae2e196519e1', - '0x3e5f69698628b92e0a47f9c2c9e14ab892216096', - '0xe68319e9389554af7fe3f7ed41ff1901632634b6', - '0x493ecf1ece448ee83f72098cff0e196fb2948cb9', - '0x3d2ab86da84b2496168e5cf841d42a4ac27511d3', - '0x3bbfb974fc85286ca6db8162c312459a92f3e302', - '0xfde9512c0c4d7b092674229a42ab4aea5f743da1', - '0x597e475a5ddd90b3eb2135ac47319bd866f685d8', - '0xb39c77901054766662b77e3269d3b622e7cacab4', - '0x0622927ecf00406d48d05c39134bcd53ed396cb4', - '0xdcae005fcf34cf3e2b12b662dded94d0f7bf2977', - '0xdc0e9a031e9fc09681495a5ae5912954cbd858e4', - '0xbe3e1228d471fc747d7a4c68823910153ee552a5', - '0x45545bd03ccb1cd84d3c8f000a7d6c709d84720d', - '0x1ac13ff6e1bbca5b49c3f12468689289fb93c388', - '0x3cfd17f9cf57164ed64b91d25f72c2c6dfaeab48', - '0xaf8094420749b0131200b8e85f5018688261f110', - '0x039827df17ae5449b31162ec579bbbbe72300188', - '0xc7b3dbc8424a11255cf895c2916f24e0063dcdc3', - '0x2f246c27ed9f4839dff70233ce250a3f6024f484', - '0xa663ae21db74048e50401f542703e0802a3afeb9', - '0xf8b70d8cf29ee045acc5623ebe61037b33228fd1', - '0x5c85b43468da23f86016f508f14ca927bfd8a737', - '0x41092b4ecf2c4db719ec5ab67dbd0c66f095ee97' -]; diff --git a/src/strategies/safe-vested/examples.json b/src/strategies/safe-vested/examples.json old mode 100755 new mode 100644 diff --git a/src/strategies/safe-vested/index.ts b/src/strategies/safe-vested/index.ts old mode 100755 new mode 100644 diff --git a/src/strategies/safety-module-bpt-power/README.md b/src/strategies/safety-module-bpt-power/README.md deleted file mode 100644 index eafa910a9..000000000 --- a/src/strategies/safety-module-bpt-power/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# staked-psp-balance - -This strategy computes the voting power of a staker relative to one token involved in a Aave like safety module that accepts arbitrary balancer LP token as staked token. -It uses balancer-pool-id strategy. - -To simplify async flow, it requires to pass couple of parameters: -- balancer pool id -- safety module (address, decimals) -- voting token (address, decimals) - - -This strategy works under 2 different regimes: - -1/ if voting_token matches reward_token of safety module --> count for staked tokens and unclaimed rewards - -2/ else --> count for staked tokens only \ No newline at end of file diff --git a/src/strategies/safety-module-bpt-power/examples.json b/src/strategies/safety-module-bpt-power/examples.json deleted file mode 100644 index a562035d7..000000000 --- a/src/strategies/safety-module-bpt-power/examples.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "name": "Example safety module bpt query", - "strategy": { - "name": "safety-module-bpt-power", - "params": { - "symbol": "PSP", - "balancerPoolId": "0xcb0e14e96f2cefa8550ad8e4aea344f211e5061d00020000000000000000011a", - "safetyModule": { - "address": "0xC8DC2Ec5f5e02bE8b37A8444a1931F02374A17ab", - "decimals": 18 - }, - "votingToken": { - "address": "0xcAfE001067cDEF266AfB7Eb5A286dCFD277f3dE5", - "decimals": 18 - } - } - }, - "network": "1", - "addresses": [ - "0x0ddc793680ff4f5793849c8c6992be1695cbe72a", - "0x9c0d72f2ac26420cb7eeb155bf401b672840e87b", - "0x7494eb2916cad8649f4f91eb1db6e20be605dad6", - "0xb7b65acf585c29070b9926c089fcfa1eb9983d3d", - "0x9824697f7c12cabada9b57842060931c48dea969", - "0x5b52e503c9e1440b47991bc0a64599c1c916084c", - "0x3ce06981bc523f950e3df346878216365b24b6fe", - "0xf2078c58d6c38c893af4e40d7b09843ec3b7d26c", - "0xcff61382e659603046358f86a119efd127d5bb48", - "0x8f60501dE5b9b01F9EAf1214dbE1924aA97F7fd0", - "0x9B8e8dD9151260c21CB6D7cc59067cd8DF306D58", - "0x17ea92D6FfbAA1c7F6B117c1E9D0c88ABdc8b84C", - "0x38C0039247A31F3939baE65e953612125cB88268" - ], - "snapshot": 14215928 - } -] diff --git a/src/strategies/safety-module-bpt-power/index.ts b/src/strategies/safety-module-bpt-power/index.ts deleted file mode 100644 index 4cbc5ea8f..000000000 --- a/src/strategies/safety-module-bpt-power/index.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { strategy as balancerPoolIdStrategy } from '../balancer-poolid'; -import { BigNumberish } from '@ethersproject/bignumber'; -import { Multicaller } from '../../utils'; -import { formatUnits } from '@ethersproject/units'; - -export const author = 'mwamedacen'; -export const version = '0.1.0'; - -interface Options { - balancerPoolId: string; - safetyModule: { - address: string; - decimals: number; - }; - votingToken: { - address: string; - decimals: number; - }; -} - -type FetchSafetyModuleScoreOutput = Promise; - -async function fetchSafetyModuleScore( - space: string, - network: string, - provider, - addresses: string[], - options: Options, - snapshot: number -): FetchSafetyModuleScoreOutput { - const scores = await balancerPoolIdStrategy( - space, - network, - provider, - [options.safetyModule.address], - { - poolId: options.balancerPoolId, - token: options.votingToken.address - }, - snapshot - ); - - return parseFloat(scores[options.safetyModule.address]); -} - -const SafetyModuleMinABI = [ - 'function totalSupply() external view returns (uint256)', - 'function STAKED_TOKEN() external view returns (address)', - 'function REWARD_TOKEN() external view returns (address)', - 'function decimals() view returns (uint8)', - 'function balanceOf(address account) external view returns (uint256)', - 'function getTotalRewardsBalance(address staker) view returns (uint256)' -]; - -const TOTAL_SUPPLY_ATTR = 'totalSupply'; -const STAKED_TOKEN_ATTR = 'stakedToken'; -const REWARD_TOKEN_ATTR = 'rewardToken'; -const BALANCE_OF_ATTR = 'balanceOf'; -const REWARDS_OF_ATTR = 'totalRewardsBalance'; - -type FetchAccountsSafetyModuleStakesAndRewardsOuput = Promise<{ - [address: string]: { - [BALANCE_OF_ATTR]: BigNumberish; - [REWARD_TOKEN_ATTR]: BigNumberish; - }; -}>; - -async function fetchAccountsSafetyModuleStakesAndRewards( - space: string, - network: string, - provider, - addresses: string[], - options: Options, - snapshot: number -): FetchAccountsSafetyModuleStakesAndRewardsOuput { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, SafetyModuleMinABI, { - blockTag - }); - - addresses.forEach((address) => { - multi.call( - `${BALANCE_OF_ATTR}_${address}`, - options.safetyModule.address, - 'balanceOf', - [address] - ); - multi.call( - `${REWARDS_OF_ATTR}_${address}`, - options.safetyModule.address, - 'getTotalRewardsBalance', - [address] - ); - }); - - const result: Record = await multi.execute(); - - return Object.entries(result).reduce((acc, [key, value]) => { - const [attr, addr] = key.split('_'); - - if (!acc[addr]) { - acc[addr] = {}; - } - - acc[addr][attr] = value; - - return acc; - }, {}); -} - -type FetchSafetyModuleGlobalStateOutput = Promise<{ - [TOTAL_SUPPLY_ATTR]: BigNumberish; - [REWARD_TOKEN_ATTR]: string; - [STAKED_TOKEN_ATTR]: string; -}>; - -async function fetchSafetyModuleGlobalState( - space: string, - network: string, - provider, - addresses: string[], - options: Options, - snapshot: number -): FetchSafetyModuleGlobalStateOutput { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, SafetyModuleMinABI, { - blockTag - }); - - multi.call(STAKED_TOKEN_ATTR, options.safetyModule.address, 'STAKED_TOKEN'); - multi.call(REWARD_TOKEN_ATTR, options.safetyModule.address, 'REWARD_TOKEN'); - multi.call(TOTAL_SUPPLY_ATTR, options.safetyModule.address, 'totalSupply'); - - const result: { - [STAKED_TOKEN_ATTR]: string; - [REWARD_TOKEN_ATTR]: string; - [TOTAL_SUPPLY_ATTR]: BigNumberish; - } = await multi.execute(); - - return result; -} - -export async function strategy( - space: string, - network: string, - provider, - addresses: string[], - options: Options, - snapshot: number -) { - const [safetyModuleScore, accountsStakesAndRewards, safetyModuleGlobalState] = - await Promise.all( - [ - fetchSafetyModuleScore, - fetchAccountsSafetyModuleStakesAndRewards, - fetchSafetyModuleGlobalState - ].map((fn) => - fn(space, network, provider, addresses, options, snapshot) - ) as [ - FetchSafetyModuleScoreOutput, - FetchAccountsSafetyModuleStakesAndRewardsOuput, - FetchSafetyModuleGlobalStateOutput - ] - ); - - const safetyModuleStakedToken = safetyModuleGlobalState[STAKED_TOKEN_ATTR]; - - if ( - safetyModuleStakedToken.toLowerCase() !== - options.balancerPoolId.substring(0, 42).toLowerCase() - ) { - throw new Error( - `safety-module-bpt-power, safety module's staken token ${safetyModuleStakedToken} doesn't match balancer pool ${options.balancerPoolId}` - ); - } - - const safetyModuleRewardsToken = safetyModuleGlobalState[REWARD_TOKEN_ATTR]; - - const votingAndRewardTokenMatching = - safetyModuleRewardsToken.toLowerCase() === - options.votingToken.address.toLowerCase(); - - const safetyModuleTotalSupply = parseFloat( - formatUnits( - safetyModuleGlobalState[TOTAL_SUPPLY_ATTR], - options.safetyModule.decimals - ) - ); - - const scores = Object.fromEntries( - Object.entries(accountsStakesAndRewards).map( - ([address, accountStakesAndRewards]) => { - const accountSafetyModuleBalance = parseFloat( - formatUnits( - accountStakesAndRewards[BALANCE_OF_ATTR], - options.safetyModule.decimals - ) - ); - - const accountSharePercent = - accountSafetyModuleBalance / safetyModuleTotalSupply; - - const accountStakedScore = accountSharePercent * safetyModuleScore; - - if (!votingAndRewardTokenMatching) { - return [address, accountStakedScore]; - } - - const accountRewardsScore = parseFloat( - formatUnits( - accountStakesAndRewards[REWARDS_OF_ATTR], - options.votingToken.decimals - ) - ); - - const accountStakedAndRewardsScore = - accountStakedScore + accountRewardsScore; - - return [address, accountStakedAndRewardsScore]; - } - ) - ); - - return scores; -} diff --git a/src/strategies/safety-module-bpt-power/schema.json b/src/strategies/safety-module-bpt-power/schema.json deleted file mode 100644 index 565093bc9..000000000 --- a/src/strategies/safety-module-bpt-power/schema.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "Strategy", - "type": "object", - "properties": { - "symbol": { - "type": "string", - "title": "Symbol", - "examples": ["e.g. PSP"], - "maxLength": 16 - }, - "balancerPoolId": { - "type": "string", - "title": "BalancerPoolId", - "examples": [ - "e.g. 0xcb0e14e96f2cefa8550ad8e4aea344f211e5061d00020000000000000000011a" - ], - "pattern": "^0x[a-fA-F0-9]{64}$", - "minLength": 66, - "maxLength": 66 - }, - "safetyModule": { - "type": "object", - "title": "SafetyModule", - "properties": { - "address": { - "type": "string", - "title": "Address", - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "decimals": { - "type": "number", - "title": "Decimals", - "examples": ["e.g. 18"] - } - } - }, - "votingToken": { - "type": "object", - "title": "VotingToken", - "properties": { - "address": { - "type": "string", - "title": "Address", - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "decimals": { - "type": "number", - "title": "Decimals", - "examples": ["e.g. 18"] - } - } - } - }, - "required": ["balancerPoolId", "safetyModule", "votingToken"], - "additionalProperties": false - } - } -} diff --git a/src/strategies/sd-boost-twavp/README.md b/src/strategies/sd-boost-twavp/README.md index 2cbcb284b..e59b9b189 100644 --- a/src/strategies/sd-boost-twavp/README.md +++ b/src/strategies/sd-boost-twavp/README.md @@ -1,6 +1,6 @@ # sd-boost-twavp -This strategy is used by StakeDAO to vote with sdToken with Time Weigthed Averaged Voting Power system and veSDT voting boost. +This strategy is used by StakeDAO to vote with sdToken with Time Weighted Averaged Voting Power system and veSDT voting boost. _sampleSize is in days_ _sampleStep is the number of block for TWAVP_ _avgBlockTime is in seconds_ diff --git a/src/strategies/sd-vote-boost-twavp-balanceof/README.md b/src/strategies/sd-vote-boost-twavp-balanceof/README.md new file mode 100644 index 000000000..d3f1f089f --- /dev/null +++ b/src/strategies/sd-vote-boost-twavp-balanceof/README.md @@ -0,0 +1,21 @@ +# sd-vote-boost-twavp-balanceof + +This strategy is used by Stake DAO to vote with sdToken using Time Weighted Averaged Voting Power (TWAVP) system based on a balanceOf with possibility to whitelist addresses to by pass TWAVP. + +>_sampleSize: in days_ +>_sampleStep: the number of block for `average` calculation (max 5)_ +>_blockPerSec: the number of block per seconds of the destination chain + +Here is an example of parameters: + +```json +{ + "sdTokenGauge": "0xE2496134149e6CD3f3A577C2B08A6f54fC23e6e4", + "symbol": "sdToken-gauge", + "decimals": 18, + "sampleSize": 10, + "sampleStep": 5, + "blockPerSec": 3, + "whiteListedAddress": [] +} +``` \ No newline at end of file diff --git a/src/strategies/sd-vote-boost-twavp-balanceof/examples.json b/src/strategies/sd-vote-boost-twavp-balanceof/examples.json new file mode 100644 index 000000000..acfda0bac --- /dev/null +++ b/src/strategies/sd-vote-boost-twavp-balanceof/examples.json @@ -0,0 +1,24 @@ +[ + { + "name": "Stake DAO vote boost using TWAVP balanceOf", + "strategy": { + "name": "sd-vote-boost-twavp-balanceof", + "params": { + "sdTokenGauge": "0xE2496134149e6CD3f3A577C2B08A6f54fC23e6e4", + "symbol": "sdToken-gauge", + "decimals": 18, + "sampleSize": 10, + "sampleStep": 5, + "blockPerSec": 3, + "whiteListedAddress": [] + } + }, + "network": "56", + "addresses": [ + "0xb734Ec7A75d65406fde5bcf9156cAB673ba1e1C5", + "0xee439Ee079AC05D9d33a6926A16e0c820fB2713A", + "0xc1133c83D409724727fF6699F14F040746e5AD01" + ], + "snapshot": 35181258 + } +] diff --git a/src/strategies/sd-vote-boost-twavp-balanceof/index.ts b/src/strategies/sd-vote-boost-twavp-balanceof/index.ts new file mode 100644 index 000000000..f1b822a40 --- /dev/null +++ b/src/strategies/sd-vote-boost-twavp-balanceof/index.ts @@ -0,0 +1,141 @@ +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)' +]; + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +): Promise> { + // 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, + options.blockPerSec + ); + + // Query working balance of users + const balanceOfQueries = addresses.map((address: any) => [ + options.sdTokenGauge, + 'balanceOf', + [address] + ]); + + // Execute multicall `sampleStep` times + const response: any[] = []; + for (let i = 0; i < options.sampleStep; i++) { + // Use good block number + blockTag = blockList[i]; + + response.push( + await multicall(network, provider, abi, balanceOfQueries, { blockTag }) + ); + } + + return Object.fromEntries( + Array(addresses.length) + .fill('x') + .map((_, i) => { + // Init array of working balances for user + const userBalances: BigNumber[] = []; + + for (let j = 0; j < options.sampleStep; j++) { + const balance = response[j].shift()[0]; + userBalances.push(balance); + } + + // Get average balance + const averageBalanceOf = parseFloat( + formatUnits( + average(userBalances, addresses[i], options.whiteListedAddress), + options.decimals + ) + ); + + // Return address and voting power + return [addresses[i], Number(averageBalanceOf)]; + }) + ); +} + +function getPreviousBlocks( + currentBlockNumber: number, + numberOfBlocks: number, + daysInterval: number, + blockPerSec: number +): number[] { + const blocksPerDay = 86400 / blockPerSec; + + // 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 + const blockNumber = + currentBlockNumber - totalBlocksInterval + blockInterval * i; + // Add block number to array + blockNumbers.push(Math.round(blockNumber)); + } + + // Return array of block numbers + return blockNumbers; +} + +function average( + numbers: BigNumber[], + address: string, + whiteListedAddress: string[] +): BigNumber { + // If no numbers, return 0 to avoid division by 0. + if (numbers.length === 0) return BigNumber.from(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 = BigNumber.from(0); + // Loop through all elements and add them to sum + for (let i = 0; i < numbers.length; i++) { + sum = sum.add(numbers[i]); + } + + // Return sum divided by array length to get mean + return sum.div(numbers.length); +} diff --git a/src/strategies/sd-vote-boost-twavp-v2/README.md b/src/strategies/sd-vote-boost-twavp-v2/README.md index 6dee8304a..11d613f6b 100644 --- a/src/strategies/sd-vote-boost-twavp-v2/README.md +++ b/src/strategies/sd-vote-boost-twavp-v2/README.md @@ -1,6 +1,6 @@ # sd-vote-boost-twavp-v2 -This strategy is used by Stake DAO to vote with sdToken using Time Weigthed Averaged Voting Power (TWAVP) system and adapted for veSDT boost delegation with possibility to whiteliste address to by pass TWAVP. +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) diff --git a/src/strategies/sd-vote-boost-twavp-v3/README.md b/src/strategies/sd-vote-boost-twavp-v3/README.md index 20f94d0ea..4042db7bb 100644 --- a/src/strategies/sd-vote-boost-twavp-v3/README.md +++ b/src/strategies/sd-vote-boost-twavp-v3/README.md @@ -1,6 +1,6 @@ # sd-vote-boost-twavp-v3 -This strategy is used by Stake DAO to vote with sdToken using Time Weigthed Averaged Voting Power (TWAVP) system and adapted for veSDT boost delegation with possibility to whiteliste address to by pass TWAVP. +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) diff --git a/src/strategies/sd-vote-boost-twavp-vsdtoken/README.md b/src/strategies/sd-vote-boost-twavp-vsdtoken/README.md new file mode 100644 index 000000000..71da0f5b0 --- /dev/null +++ b/src/strategies/sd-vote-boost-twavp-vsdtoken/README.md @@ -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"] +} +``` \ No newline at end of file diff --git a/src/strategies/sd-vote-boost-twavp-vsdtoken/examples.json b/src/strategies/sd-vote-boost-twavp-vsdtoken/examples.json new file mode 100644 index 000000000..54cda66e5 --- /dev/null +++ b/src/strategies/sd-vote-boost-twavp-vsdtoken/examples.json @@ -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 + } +] diff --git a/src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts b/src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts new file mode 100644 index 000000000..0da66ee1d --- /dev/null +++ b/src/strategies/sd-vote-boost-twavp-vsdtoken/index.ts @@ -0,0 +1,180 @@ +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> { + // 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] + ]); + 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; +} diff --git a/src/strategies/sd-vote-boost-twavp/README.md b/src/strategies/sd-vote-boost-twavp/README.md index 8b15abd26..8991546b3 100644 --- a/src/strategies/sd-vote-boost-twavp/README.md +++ b/src/strategies/sd-vote-boost-twavp/README.md @@ -1,6 +1,6 @@ # sd-vote-boost-twavp -This strategy is used by Stake DAO to vote with sdToken using Time Weigthed Averaged Voting Power (TWAVP) system and adapted for veSDT boost delegation. +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. ``` VotingPower(user) = veToken.balanceOf(liquidLocker) * (average.sdTokenGauge.working_balances(user) / sdTokenGauge.working_supply) diff --git a/src/strategies/selfswap/README.md b/src/strategies/selfswap/README.md deleted file mode 100644 index 42c764887..000000000 --- a/src/strategies/selfswap/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# selfswap - -Fetches [SELF](https://bscscan.com/address/0x7a364484303b38bce7b0ab60a20da8f2f4370129) balance from the following sources: - -- Wallet -- SELF-BNB LP Farm -- SELF Pool -- SELF Vault -- Pools that were active at the time of the snapshot diff --git a/src/strategies/selfswap/examples.json b/src/strategies/selfswap/examples.json deleted file mode 100644 index 82b20d8c5..000000000 --- a/src/strategies/selfswap/examples.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "selfswap", - "params": { - "symbol": "SELF" - } - }, - "network": "56", - "addresses": [ - "0x26e82CB17cfd4ef12096f38f3ba0DAD6ea5B5035", - "0x21fF20E7e1B820020415707298b92299CF0951fE", - "0x2b3D1D31ac5C053cf89a92EE9c94dbF3774D6366", - "0x273e3fD65450032a44AC6CA36F6551D74A459B6A" - ], - "snapshot": 16308675 - } -] diff --git a/src/strategies/selfswap/index.ts b/src/strategies/selfswap/index.ts deleted file mode 100644 index 76531aa09..000000000 --- a/src/strategies/selfswap/index.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; -import { strategy as masterChefPoolBalanceStrategy } from '../masterchef-pool-balance'; -import { formatEther } from '@ethersproject/units'; -import { Zero, WeiPerEther } from '@ethersproject/constants'; -import { BigNumber } from '@ethersproject/bignumber'; -import { subgraphRequest, Multicaller } from '../../utils'; - -const chunk = (arr, size) => - Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => - arr.slice(i * size, i * size + size) - ); - -const PAGE_SIZE = 1000; - -export const author = 'Cr3k'; -export const version = '0.0.1'; - -const SELF_ADDRESS = '0x7a364484303b38bce7b0ab60a20da8f2f4370129'; -const SELF_VAULT_ADDRESS = '0xeb4f1307DE7DF263E8e54d083fE7db1e281e710D'; -const SELF_BNB_LP_ADDRESS = '0x9C6FF656A563Ec9057460D8a400E2AC7c2AE0a1C'; - -const MASTER_CHEF_ADDRESS = { - v1: '0x3d03d12F95Bdc4509804f9Bcee4139b7789DC516' -}; - -const vaultAbi = [ - 'function getPricePerFullShare() view returns (uint256)', - 'function userInfo(address) view returns (uint256 shares, uint256 lastDepositedTime, uint256 selfAtLastUserAction, uint256 lastUserActionTime)' -]; - -const smartChefUrl = 'https://api.thegraph.com/subgraphs/name/cr3k/smartchef'; - -async function getPools(provider, snapshot: any) { - let blockNumber = snapshot; - if (blockNumber === 'latest') { - blockNumber = await provider.getBlockNumber(); - } - - const params = { - smartChefs: { - __args: { - where: { - stakeToken: SELF_ADDRESS.toLowerCase(), - endBlock_gte: blockNumber, - startBlock_lt: blockNumber - } - }, - id: true - } - }; - - const pools = await subgraphRequest(smartChefUrl, params); - - return pools.smartChefs; -} - -async function getSmartChefStakedSELFAmount( - snapshot: any, - poolAddresses: string[], - addresses: string[] -) { - const addressChunks = chunk(addresses, 1500); - let results: any[] = []; - - for (const addressChunk of addressChunks) { - const params = { - users: { - __args: { - where: { - pool_in: poolAddresses.map((addr) => addr.toLowerCase()), - address_in: addressChunk.map((addr) => addr.toLowerCase()), - stakeAmount_gt: '0' - }, - first: PAGE_SIZE - }, - address: true, - stakeAmount: true - } - }; - - let page = 0; - let triedBlockNumber = false; - - while (true) { - // @ts-ignore - params.users.__args.skip = page * PAGE_SIZE; - if (snapshot !== 'latest' && !triedBlockNumber) { - // @ts-ignore - params.users.__args.block = { number: snapshot }; - } else { - // @ts-ignore - delete params.users.__args.block; - } - let result; - try { - result = await subgraphRequest(smartChefUrl, params); - } catch (error) { - if (!triedBlockNumber) { - triedBlockNumber = true; - continue; - } else { - throw error; - } - } - if (!Array.isArray(result.users) && !triedBlockNumber) { - triedBlockNumber = true; - continue; - } - results = results.concat(result.users); - page++; - if (result.users.length < PAGE_SIZE) break; - } - } - - return results.reduce>((acc, user) => { - if (acc[user.address]) { - acc[user.address] = (acc[user.address] as BigNumber).add( - user.stakeAmount - ); - } else { - acc[user.address] = BigNumber.from(user.stakeAmount); - } - return acc; - }, {}); -} - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const pools = await getPools(provider, snapshot); - - const userPoolBalance = await getSmartChefStakedSELFAmount( - snapshot, - pools.map((p) => p.id), - addresses - ); - - const blockTag = snapshot; - - const erc20Balance = await erc20BalanceOfStrategy( - space, - network, - provider, - addresses, - { - address: SELF_ADDRESS, - symbol: 'SELF', - decimals: 18 - }, - snapshot - ); - - const selfBnbLpBalance = await masterChefPoolBalanceStrategy( - space, - network, - provider, - addresses, - { - chefAddress: MASTER_CHEF_ADDRESS.v1, - uniPairAddress: SELF_BNB_LP_ADDRESS, - pid: '251', - symbol: 'SELF-BNB LP', - tokenIndex: 0 - }, - snapshot - ); - - const selfVaultBalance = await getVaultBalance( - network, - provider, - addresses, - blockTag - ); - - return Object.fromEntries( - addresses.map((address) => [ - address, - erc20Balance[address] + - selfBnbLpBalance[address] + - parseFloat( - formatEther( - (userPoolBalance[address.toLowerCase()] || Zero).add( - selfVaultBalance[address] || Zero - ) - ) - ) - ]) - ); -} - -async function getVaultBalance(network, provider, addresses, blockTag) { - const vaultMulti = new Multicaller(network, provider, vaultAbi, { blockTag }); - - vaultMulti.call( - SELF_VAULT_ADDRESS, - SELF_VAULT_ADDRESS, - 'getPricePerFullShare' - ); - - addresses.forEach((address) => - vaultMulti.call( - `${SELF_VAULT_ADDRESS}-${address}`, - SELF_VAULT_ADDRESS, - 'userInfo', - [address] - ) - ); - - const vaultMultiRes = await vaultMulti.execute(); - - return Object.fromEntries( - addresses.map((address) => [ - address, - (vaultMultiRes[SELF_VAULT_ADDRESS] || Zero) - .mul(vaultMultiRes[`${SELF_VAULT_ADDRESS}-${address}`]?.shares || Zero) - .div(WeiPerEther) - ]) - ); -} diff --git a/src/strategies/single-staking-vault-balanceof/README.md b/src/strategies/single-staking-vault-balanceof/README.md deleted file mode 100644 index a7c2d76da..000000000 --- a/src/strategies/single-staking-vault-balanceof/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# single-staking-vault-balanceof - -Used for fetching the staked token balance in a single staking vault - -The only parameter is the vault address. The vault must -have the function call `wantLockedTotal(address)` which should -return the amount of tokens in the vault. - -```json -{ - "vaultAddress": "0xA68E643e1942fA8635776b718F6EeD5cEF2a3F15" -} -``` diff --git a/src/strategies/single-staking-vault-balanceof/examples.json b/src/strategies/single-staking-vault-balanceof/examples.json deleted file mode 100644 index 5a4bbb5e6..000000000 --- a/src/strategies/single-staking-vault-balanceof/examples.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "single-staking-vault-balanceof", - "params": { - "symbol": "STAKE", - "vaultAddress": "0xA68E643e1942fA8635776b718F6EeD5cEF2a3F15" - } - }, - "network": "1666600000", - "addresses": [ - "0xD20B976584bF506BAf5cC604D1f0A1B8D07138dA", - "0x4ff9B7C1424b9E4375BbbDF3357a318412c02E0c", - "0x57B7713c0E013cfbEC0E4C6c8B264dAf7598ebA9", - "0xB989B490F9899a5AD56a4255A3C84457040B59dc" - ], - "snapshot": 18263021 - } -] diff --git a/src/strategies/single-staking-vault-balanceof/index.ts b/src/strategies/single-staking-vault-balanceof/index.ts deleted file mode 100644 index 729c3b7a4..000000000 --- a/src/strategies/single-staking-vault-balanceof/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; - -export const author = 'foxthefarmer'; -export const version = '0.0.1'; - -const vaultAbi = ['function wantLockedTotal(address) view returns (uint256)']; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const vaultBalancesCalls: any = multicall( - network, - provider, - vaultAbi, - addresses.map((address: any) => [ - options.vaultAddress, - 'wantLockedTotal', - [address] - ]), - { blockTag } - ); - - const vaultBalances = await Promise.all([vaultBalancesCalls]); - - return Object.fromEntries( - Object.entries(addresses).map((address: any, index) => [ - address[1], - parseFloat(formatUnits(vaultBalances[0][index].toString(), 18)) - ]) - ); -} diff --git a/src/strategies/snote/examples.json b/src/strategies/snote/examples.json new file mode 100644 index 000000000..1ec38cd4e --- /dev/null +++ b/src/strategies/snote/examples.json @@ -0,0 +1,16 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "snote", + "params": { "symbol": "sNOTE" } + }, + "network": "1", + "addresses": [ + "0xCece1920D4dBb96BAf88705ce0A6Eb3203ed2eB1", + "0x4E8014fF5bacE498DAB1a9E2B5c3f4240bC059B6", + "0x741AA7CFB2c7bF2A1E7D4dA2e3Df6a56cA4131F3" + ], + "snapshot": 18629000 + } +] diff --git a/src/strategies/snote/index.ts b/src/strategies/snote/index.ts new file mode 100644 index 000000000..1eae9a07a --- /dev/null +++ b/src/strategies/snote/index.ts @@ -0,0 +1,80 @@ +import { BigNumberish } from '@ethersproject/bignumber'; +import { formatUnits } from '@ethersproject/units'; +import { Multicaller } from '../../utils'; + +export const author = 'kaiserpy'; +export const version = '0.1.0'; + +const abi = [ + 'function getVotes(address account) external view returns (uint256)', + 'function votingPowerWithoutDelegation(address account) external view returns (uint256)', + 'function delegates(address account) external view returns (address)' +]; + +const STAKED_NOTE_CONTRACT_ADDRESS = + '0x38de42f4ba8a35056b33a746a6b45be9b1c3b9d2'; + +interface VotingInfo { + [address: string]: BigNumberish; +} + +export async function strategy( + space: any, + network: string, + provider: any, + addresses: string[], + options: any, + snapshot: number | string +) { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + // Helper function to fetch data using multicall + async function fetchMulticallData(method: string) { + const multi = new Multicaller(network, provider, abi, { blockTag }); + addresses.forEach((address) => + multi.call(address, STAKED_NOTE_CONTRACT_ADDRESS, method, [address]) + ); + return await multi.execute(); + } + + // Fetch delegate votes, non-delegated votes, and delegation information + const delegatedVotes: VotingInfo = await fetchMulticallData('getVotes'); + const nonDelegatedVotes: VotingInfo = await fetchMulticallData( + 'votingPowerWithoutDelegation' + ); + const delegationInfo: Record = await fetchMulticallData( + 'delegates' + ).then((result2: Record) => + Object.fromEntries( + Object.entries(result2).map(([address, delegate]) => [ + address, + delegate.toLowerCase() === '0x0000000000000000000000000000000000000000' + ]) + ) + ); + + // Process and filter the data + const delegateVotingPowers = Object.fromEntries( + Object.entries(delegatedVotes).map(([address, balance]) => [ + address, + parseFloat(formatUnits(balance, 8)) + ]) + ); + const votingPowers = Object.fromEntries( + Object.entries(nonDelegatedVotes).map(([address, balance]) => [ + address, + parseFloat(formatUnits(balance, 8)) + ]) + ); + + const filteredBalances: Record = {}; + addresses.forEach((address) => { + if (delegationInfo[address]) { + const delegateVotingPower = delegateVotingPowers[address] || 0; + const votingPower = votingPowers[address] || 0; + filteredBalances[address] = delegateVotingPower + votingPower; + } + }); + + return filteredBalances; +} diff --git a/src/strategies/snowswap/README.md b/src/strategies/snowswap/README.md deleted file mode 100644 index 8a83fd7c6..000000000 --- a/src/strategies/snowswap/README.md +++ /dev/null @@ -1,12 +0,0 @@ -Snowswap - -Checks for the number of SNOW's staked in Frosty’s pool (https://etherscan.io/address/0x7d2c8b58032844f222e2c80219975805dce1921c) and adds it to the balance of the voters FLAME ERC20 token - -Here is an example of parameters: - -{ - "address": "0xfe9A29aB92522D14Fc65880d817214261D8479AE", - "symbol": "SNOW", - "decimals": 18, - "snowStakingAddress": "0x7d2c8b58032844f222e2c80219975805dce1921c" -} diff --git a/src/strategies/snowswap/examples.json b/src/strategies/snowswap/examples.json deleted file mode 100644 index 2b75c2774..000000000 --- a/src/strategies/snowswap/examples.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "name": "Snowswap", - "strategy": { - "name": "snowswap", - "params": { - "address": "0xfe9A29aB92522D14Fc65880d817214261D8479AE", - "symbol": "SNOW", - "decimals": 18, - "snowStakingAddress": "0x7d2c8b58032844f222e2c80219975805dce1921c" - } - }, - "network": "1", - "addresses": [ - "0x109763295D1636D6b311ab690c63e2A7A4606bC7", - "0x7d2c8b58032844f222e2c80219975805dce1921c" - ], - "snapshot": 13087619 - } -] diff --git a/src/strategies/snowswap/index.ts b/src/strategies/snowswap/index.ts deleted file mode 100644 index bcccaa601..000000000 --- a/src/strategies/snowswap/index.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; -import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; - -export const author = 'jairsnowswap'; -export const version = '0.1.0'; - -const stakedAbi = [ - 'function balanceOf(address account) external view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const { snowStakingAddress, decimals } = options; - - const snowBalances = await erc20BalanceOfStrategy( - space, - network, - provider, - addresses, - options, - snapshot - ); - - const stakedTokenBalances = new Multicaller(network, provider, stakedAbi, { - blockTag - }); - - addresses.forEach((address: string) => - stakedTokenBalances.call(address, snowStakingAddress, 'balanceOf', [ - address - ]) - ); - const result: Record = - await stakedTokenBalances.execute(); - - return Object.fromEntries( - Object.entries(result).map(([address, output]) => [ - address, - parseFloat(formatUnits(output, decimals)) + snowBalances[address] - ]) - ); -} diff --git a/src/strategies/solv-voucher-claimable/README.md b/src/strategies/solv-voucher-claimable/README.md deleted file mode 100644 index 04e7da0c6..000000000 --- a/src/strategies/solv-voucher-claimable/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# solv-voucher-claimable - -This strategy is to let owners of [Solv vesting vouchers](https://solv.finance/) vote with the voting power equal to their claimable token amount in the voucher at the time of the snapshot. - -This can be combined with `erc20-balance-of` strategy to give the voting power of "amount held in wallet" + "amount available in the vesting voucher". - -The parameters are the `address` of the vesting voucher contract and the `symbol` of the underlying token. Here is an example of parameters: - -```json -{ - "address": "0x522f8fe415e08b600b8bd6c1db74a1b696845d0d", - "symbol": "PDT" -} -``` diff --git a/src/strategies/solv-voucher-claimable/examples.json b/src/strategies/solv-voucher-claimable/examples.json deleted file mode 100644 index 95c270dba..000000000 --- a/src/strategies/solv-voucher-claimable/examples.json +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "name": "PDT Vesting Voucher Claimable Amounts", - "strategy": { - "name": "solv-voucher-claimable", - "params": { - "address": "0x522f8fe415e08b600b8bd6c1db74a1b696845d0d", - "symbol": "PDT" - } - }, - "network": "1", - "addresses": [ - "0x0d5f507074db8ead56f5875219dbf1ff73bcd429", - "0xf96225d26fa257b200420696092e02cc141cf3d8", - "0xd3be4499f28ef91a6b6dd5ab6beb0588039d72ba", - "0x661c3a6db7241f2a7b3b1c1d73fe98198d089fc2", - "0xceeab2af38e6b086cdce120c49f93b65f0b92b76", - "0xc40fc1c553737b2aa8572fdb036986510219f233" - ], - "snapshot": 14955800 - }, - { - "name": "VERA Vesting Voucher Claimable Amounts", - "strategy": { - "name": "solv-voucher-claimable", - "params": { - "address": "0x928b35660f8388042d871e82eb40234901461354", - "symbol": "VERA" - } - }, - "network": 56, - "addresses": [ - "0x0d5f507074db8ead56f5875219dbf1ff73bcd429", - "0xf96225d26fa257b200420696092e02cc141cf3d8", - "0xd3be4499f28ef91a6b6dd5ab6beb0588039d72ba", - "0x661c3a6db7241f2a7b3b1c1d73fe98198d089fc2", - "0xceeab2af38e6b086cdce120c49f93b65f0b92b76", - "0xc40fc1c553737b2aa8572fdb036986510219f233" - ], - "snapshot": 19287600 - } -] diff --git a/src/strategies/solv-voucher-claimable/index.ts b/src/strategies/solv-voucher-claimable/index.ts deleted file mode 100644 index 32fb035c3..000000000 --- a/src/strategies/solv-voucher-claimable/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { BigNumber, FixedNumber } from '@ethersproject/bignumber'; -import { Multicaller } from '../../utils'; - -export const author = 'mitesh-mutha'; -export const version = '0.1.0'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256)', - 'function tokenURI(uint256 tokenId) external view returns (string)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - // Fetch the balanceOf the addresses i.e. how many vouchers do they hold? - const balanceOfMulti = new Multicaller(network, provider, abi, { blockTag }); - addresses.forEach((address) => - balanceOfMulti.call(address, options.address, 'balanceOf', [address]) - ); - const ownedCounts: Record = await balanceOfMulti.execute(); - - // Fetch the voucher token IDs held for each address - const tokenIdsMulti = new Multicaller(network, provider, abi, { blockTag }); - addresses.map((address) => { - let ownedCount = ownedCounts[address]; - while (ownedCount.gt(0)) { - const index = ownedCount.sub(1); - tokenIdsMulti.call( - `${address}-${index.toString()}`, - options.address, - 'tokenOfOwnerByIndex', - [address, index.toNumber()] - ); - ownedCount = index; - } - }); - const ownerTokenIds: Record = await tokenIdsMulti.execute(); - - // Fetch the voucher data for each voucher held by an address among the address - const tokenURIMulti = new Multicaller(network, provider, abi, { blockTag }); - Object.entries(ownerTokenIds).map(([addressWithIndex, tokenId]) => { - tokenURIMulti.call(`${addressWithIndex}`, options.address, `tokenURI`, [ - tokenId - ]); - }); - const ownerTokenURIs: Record = await tokenURIMulti.execute(); - - // Go through the list of results and sum up claimable values - const claimableVotingPower: Record = {}; - Object.entries(ownerTokenURIs).map(([addressWithIndex, tokenURI]) => { - const address = addressWithIndex.split('-')[0]; - if (tokenURI.split(',')[0] == 'data:application/json') { - const tokenData = JSON.parse(tokenURI.slice(22)); - const claimableAmount = tokenData['properties']['claimableAmount']; - if (!claimableVotingPower[address]) - claimableVotingPower[address] = FixedNumber.from(0); - claimableVotingPower[address] = claimableVotingPower[address].addUnsafe( - FixedNumber.fromString(claimableAmount) - ); - } - }); - - // Return the computed values - return Object.fromEntries( - Object.entries(claimableVotingPower).map(([address, votingPower]) => [ - address, - votingPower.toUnsafeFloat() - ]) - ); -} diff --git a/src/strategies/spacefi-blp/README.md b/src/strategies/spacefi-blp/README.md new file mode 100644 index 000000000..d55676fbf --- /dev/null +++ b/src/strategies/spacefi-blp/README.md @@ -0,0 +1,33 @@ +# spacefi-blp + +This strategy returns the assets of nft and xspace in spacefi, including staking and in the market + +Here is an example of parameters: + +```json +[ + { + "name": "Example query", + "strategy": { + "name": "spacefi-blp", + "params": { + "blpAddress": "0x67B6c6E67F8f2CD1948ECD02221D141CBa3A4984", + "symbol": "blp", + "decimals": 18 + } + }, + "network": "324", + "addresses": [ + "0x8633500EF5c41CE955B4958AD5e61ca58A2B3cB6", + "0xAc2fbBC12cEe75158C38c5Ca34EfBb6343aFdcB3", + "0xb4B72F3843154B37580721ba9233fE86Da6e0416", + "0x89023d1284F565aF9Ad115ACDb2F512d35024723", + "0xEeC60E6a0Ca0F80Fa16e0E2267Ff4C2c1A46a447", + "0xfC87549072F3217140E7046d88D3873C8bF3B014", + "0xb6cE6Dd21598f24E840Bf2D43D8d4a1d57faEf4E", + "0x524E2631ceBFce2aF66FAcDBB160aC94A00751B3" + ], + "snapshot": 24739965 + } +] +``` diff --git a/src/strategies/spacefi-blp/examples.json b/src/strategies/spacefi-blp/examples.json new file mode 100644 index 000000000..083f3ec61 --- /dev/null +++ b/src/strategies/spacefi-blp/examples.json @@ -0,0 +1,25 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "spacefi-blp", + "params": { + "blpAddress": "0x67B6c6E67F8f2CD1948ECD02221D141CBa3A4984", + "symbol": "blp", + "decimals": 18 + } + }, + "network": "324", + "addresses": [ + "0x8633500EF5c41CE955B4958AD5e61ca58A2B3cB6", + "0xAc2fbBC12cEe75158C38c5Ca34EfBb6343aFdcB3", + "0xb4B72F3843154B37580721ba9233fE86Da6e0416", + "0x89023d1284F565aF9Ad115ACDb2F512d35024723", + "0xEeC60E6a0Ca0F80Fa16e0E2267Ff4C2c1A46a447", + "0xfC87549072F3217140E7046d88D3873C8bF3B014", + "0xb6cE6Dd21598f24E840Bf2D43D8d4a1d57faEf4E", + "0x524E2631ceBFce2aF66FAcDBB160aC94A00751B3" + ], + "snapshot": 24739965 + } +] diff --git a/src/strategies/spacefi-blp/index.ts b/src/strategies/spacefi-blp/index.ts new file mode 100644 index 000000000..410917481 --- /dev/null +++ b/src/strategies/spacefi-blp/index.ts @@ -0,0 +1,37 @@ +import { BigNumberish } from '@ethersproject/bignumber'; +import { formatUnits } from '@ethersproject/units'; +import { Multicaller } from '../../utils'; + +export const author = 'SpaceFinance'; +export const version = '0.1.0'; + +const abi = [ + 'function userInfo(address account) external view returns (uint256,uint256,uint256)' +]; + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +) { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + const { blpAddress, decimals } = options; + + const multi = new Multicaller(network, provider, abi, { blockTag }); + addresses.forEach((address) => + multi.call(address, blpAddress, 'userInfo', [address]) + ); + const result: Record = + await multi.execute(); + + return Object.fromEntries( + Object.entries(result).map(([address, blp]) => [ + address, + Number(formatUnits(blp[0], decimals)) + + Number(formatUnits(blp[2], decimals)) + ]) + ); +} diff --git a/src/strategies/squadz-power/README.md b/src/strategies/squadz-power/README.md deleted file mode 100644 index 295cd2668..000000000 --- a/src/strategies/squadz-power/README.md +++ /dev/null @@ -1,15 +0,0 @@ -Strategy for using the "power" stat of Squadz collections for voting power. - -Power factors in: -- if an address has a currently active membership -- how many memberships an address has been minted total - -Parameters should look like: -``` -{ - "symbol": "SQDZ", // the token symbol for your collection - "collectionAddress": "0xd5746787be995887c59eff90611778b9cb67f0db", // the address for your collection -} -``` - -RARE: If your collection was forked onto the Squadz engine (see heyshell.xyz), you will also need to include the `forkNumber` in the parameters object. \ No newline at end of file diff --git a/src/strategies/squadz-power/examples.json b/src/strategies/squadz-power/examples.json deleted file mode 100644 index f27dbe1a6..000000000 --- a/src/strategies/squadz-power/examples.json +++ /dev/null @@ -1,36 +0,0 @@ -[ - { - "name": "Example squadz-power query goerli", - "strategy": { - "name": "squadz-power", - "params": { - "symbol": "SQDZ", - "collectionAddress": "0xd5746787be995887c59eff90611778b9cb67f0db" - } - }, - "network": "5", - "addresses": [ - "0x4171160db0e7e2c75a4973b7523b437c010dd9d4", - "0xd50fc49ff389558d23a76cf246da147ff53d8df8" - ], - "snapshot": 6679906 - }, - { - "name": "Example squadz-power query polygon", - "strategy": { - "name": "squadz-power", - "params": { - "symbol": "SQDZ", - "collectionAddress": "0xe56a303d9494bc55bd4ea570af6fb69efbd1aa63" - } - }, - "network": "137", - "addresses": [ - "0xd50fc49ff389558d23a76cf246da147ff53d8df8", - "0xc8d61fe5db0ef7b4512ce4d086c9c1c3f091fb75", - "0x4171160db0e7e2c75a4973b7523b437c010dd9d4", - "0x57421dea5152997c5ca37c3c6a5891c2c0217078" - ], - "snapshot": 26893958 - } -] diff --git a/src/strategies/squadz-power/index.ts b/src/strategies/squadz-power/index.ts deleted file mode 100644 index 7b388de98..000000000 --- a/src/strategies/squadz-power/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { multicall } from '../../utils'; - -export const author = 'EzraWeller'; -export const version = '0.0.1'; - -const engineAddresses: { [network: string]: string } = { - '5': '0x7beaa4e60e0faab603e99813f0f2330704b53086', - '80001': '0x39235b78626d8fa4ef6a81ba5616c58708ba4ea5', - '137': '0xb4a1a96ffa514b295b9a0de127288ec7d09e4e7c', - '4': '0xbeea7483aef24502a27eb7a35aad55280f8e2ebc' -}; - -const engineAbi = [ - 'function getMemberInfo(address, uint256, address) view returns (uint256, uint256, uint256, bool, bool, uint256, uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - if (!Object.keys(engineAddresses).includes(network)) - throw new Error( - 'Invalid network:' + network + 'not in' + Object.keys(engineAddresses) - ); - - const engineAddress = engineAddresses[network]; - const forkNumber = options.forkNumber ?? 0; - - const response = await multicall( - network, - provider, - engineAbi, - addresses.map((member) => [ - engineAddress, - 'getMemberInfo', - [options.collectionAddress, forkNumber, member] - ]), - { blockTag } - ); - - return Object.fromEntries( - response.map((value, i) => [addresses[i], parseInt(value[5])]) - ); -} diff --git a/src/strategies/stakedao-governance-update/README.md b/src/strategies/stakedao-governance-update/README.md deleted file mode 100644 index 1fd27719d..000000000 --- a/src/strategies/stakedao-governance-update/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# stakedao-governance-update - -This strategy is used by StakeDAO to vote for the governance on the protocol. -Here is an example of parameters: - -```json -{ - "SDT_ETHEREUM": "0x73968b9a57c6E53d41345FD57a6E6ae27d6CDB2F", - "SDT_POLYGON": "0x361A5a4993493cE00f61C32d4EcCA5512b82CE90", - "SDT_RARI_ETHEREUM": "0x1066AB47a342152C564AF62D179aA4B659a11F7d", - "xSDT_RARI_ETHEREUM": "0x806323188117b73315fC9EB3FAa3a48A8D080376", - "veSDT_ETHEREUM": "0x0C30476f66034E11782938DF8e4384970B6c9e8a", - "network_ETHEREUM": "1", - "network_POLYGON": "137", - "symbol": "sdToken", - "decimals": 18 -} -``` diff --git a/src/strategies/stakedao-governance-update/examples.json b/src/strategies/stakedao-governance-update/examples.json deleted file mode 100644 index 9c5fb5100..000000000 --- a/src/strategies/stakedao-governance-update/examples.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "stakedao-governance-update", - "params": { - "SDT_ETHEREUM": "0x73968b9a57c6E53d41345FD57a6E6ae27d6CDB2F", - "SDT_POLYGON": "0x361A5a4993493cE00f61C32d4EcCA5512b82CE90", - "SDT_RARI_ETHEREUM": "0x1066AB47a342152C564AF62D179aA4B659a11F7d", - "xSDT_RARI_ETHEREUM": "0x806323188117b73315fC9EB3FAa3a48A8D080376", - "veSDT_ETHEREUM": "0x0C30476f66034E11782938DF8e4384970B6c9e8a", - "network_ETHEREUM": "1", - "network_POLYGON": "137", - "symbol": "sdToken", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x983ec6d045713d1b87f110a7edab9fc7996eefc0", - "0x6d75ffbffd1e63e5072f0ffbf6c4eefa16043967", - "0xbd2471B4150619a42093fFBA3a7AF35335ceC5B6" - ], - "snapshot": 14316000 - } -] diff --git a/src/strategies/stakedao-governance-update/index.ts b/src/strategies/stakedao-governance-update/index.ts deleted file mode 100644 index 49f137783..000000000 --- a/src/strategies/stakedao-governance-update/index.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { multicall } from '../../utils'; -import { getProvider } from '../../utils'; - -export const author = 'clement-ux'; -export const version = '0.0.1'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function totalSupply() external view returns (uint256)', - 'function locked(address arg0) external view returns (int128,uint256)' -]; - -const F = 4; // veSDT vote multiplicator - -const chunk = (arr, size) => - Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => - arr.slice(i * size, i * size + size) - ); -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - // *** Query *** // - - // Ethereum Side - const SDT_Query_ETH = addresses.map((address: any) => [ - options.SDT_ETHEREUM, - 'balanceOf', - [address] - ]); - - const SDT_Locked_Query_ETH = addresses.map((address: any) => [ - options.veSDT_ETHEREUM, - 'locked', - [address] - ]); - const veSDT_Query_ETH = addresses.map((address: any) => [ - options.veSDT_ETHEREUM, - 'balanceOf', - [address] - ]); - - const SDT_RARI_Query_ETH = addresses.map((address: any) => [ - options.SDT_RARI_ETHEREUM, - 'balanceOf', - [address] - ]); - const xSDT_RARI_Query_ETH = addresses.map((address: any) => [ - options.xSDT_RARI_ETHEREUM, - 'balanceOf', - [address] - ]); - // Polygon Side - const SDT_Query_POLYGON = addresses.map((address: any) => [ - options.SDT_POLYGON, - 'balanceOf', - [address] - ]); - - // *** Response *** // - const responseETH = await multicall( - options.network_ETHEREUM, - getProvider(options.network_ETHEREUM), - abi, - [ - ...SDT_Query_ETH, - ...SDT_Locked_Query_ETH, - ...SDT_RARI_Query_ETH, - ...xSDT_RARI_Query_ETH, - ...veSDT_Query_ETH - ], - { - blockTag - } - ); - const responsePOLYGON = await multicall( - options.network_POLYGON, - getProvider(options.network_POLYGON), - abi, - [...SDT_Query_POLYGON], - { - blockTag - } - ); - - const responseCleanETH = responseETH.slice(0, responseETH.length); - const responseCleanPOLYGON = responsePOLYGON.slice(0, responsePOLYGON.length); - const chunksETH = chunk(responseCleanETH, addresses.length); - const chunksPOLY = chunk(responseCleanPOLYGON, addresses.length); - - const SDT_ETH = chunksETH[0]; - const SDTLocked_ETH = chunksETH[1]; - const fSDT = chunksETH[2]; - const fxSDT = chunksETH[3]; - const veSDT = chunksETH[4]; - const SDT_POLY = chunksPOLY[0]; - - return Object.fromEntries( - Array(addresses.length) - .fill('x') - .map((_, i) => { - const SDT_ETHi = SDT_ETH[i][0]; - const SDTLocked_ETHi = SDTLocked_ETH[i][0]; - const veSDTi = veSDT[i][0]; - const fSDTi = fSDT[i][0]; - const fxSDTi = fxSDT[i][0]; - const SDT_POLYi = SDT_POLY[i][0]; - - // Print statements - //console.log(`==================${addresses[i]}==================\n`); - //console.log(`${SDT_ETHi / 10 ** 18} SDT_ETH`); - //console.log(`${SDTLocked_ETHi / 10 ** 18} SDTLocked_ETH`); - //console.log(`${veSDTi / 10 ** 18} veSDT`); - //console.log(`${fSDTi / 10 ** 18} fSDT`); - //console.log(`${fxSDTi / 10 ** 18} fxSDT`); - //console.log(`${SDT_POLYi / 10 ** 18} SDT_POLY`); - - return [ - addresses[i], - SDT_ETHi.add(SDTLocked_ETHi) - .add(veSDTi.mul(F)) - .add(fSDTi) - .add(fxSDTi) - .add(SDT_POLYi) / - 10 ** 18 - ]; - }) - ); -} diff --git a/src/strategies/station-constant-if-badge/README.md b/src/strategies/station-constant-if-badge/README.md new file mode 100644 index 000000000..1a91af583 --- /dev/null +++ b/src/strategies/station-constant-if-badge/README.md @@ -0,0 +1,5 @@ +# station-constant-if-badge + +This strategy returns a constant if a voter/proposer has a specific badge in a [Station](https://www.station.express/) [GroupOS](https://groupos.xyz/) group; If not, zero is returned. + +This strategy can be used as a validation strategy that only allows certain members to vote/propose. diff --git a/src/strategies/station-constant-if-badge/examples.json b/src/strategies/station-constant-if-badge/examples.json new file mode 100644 index 000000000..6d069a333 --- /dev/null +++ b/src/strategies/station-constant-if-badge/examples.json @@ -0,0 +1,25 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "station-constant-if-badge", + "params": { + "membershipERC721": "0x33dbde2e093b7cf8446d9ac0de79220d42423501", + "badgesERC1155": "0xd1502a7659eaad60278ae3ef27edea849504f4da", + "badgeId": 1, + "constant": 1337, + "erc6551Registry": "0x000000006551c19487814612e58FE06813775758", + "erc6551Implementation": "0x509b531c8e979c85375370c0ba92ac44173c2d12", + "erc6551Salt": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "network": "1", + "addresses": [ + "0x5A8CA059a7e185ad01695136dbe5A721D4e053D6", + "0x1ba671ce4718d76D42609affbB567Bf1E71E0852", + "0x008B1FFe244bF31896C833bFf4Ad9009E7A0f4Eb", + "0x1b515D521dd0CBA174Bee666ED8f42449C8cA8bb" + ], + "snapshot": 18779365 + } +] diff --git a/src/strategies/station-constant-if-badge/index.ts b/src/strategies/station-constant-if-badge/index.ts new file mode 100644 index 000000000..9d7c64038 --- /dev/null +++ b/src/strategies/station-constant-if-badge/index.ts @@ -0,0 +1,53 @@ +import { + getAllMembers, + filterMembers, + fetchBadgeBalances +} from '../station-score-if-badge'; + +export const author = 'espendk'; +export const version = '1.0.1'; + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +): Promise> { + let members = await getAllMembers( + network, + provider, + snapshot, + options.membershipERC721, + options.erc6551Registry, + options.erc6551Implementation, + options.erc6551Salt + ); + + // Keep only members in the list of queried addresses + members = filterMembers(members, (member) => + addresses.includes(member.address) + ); + + await fetchBadgeBalances( + network, + provider, + snapshot, + members, + options.badgesERC1155, + options.badgeId + ); + + // Keep only members with a badge + members = filterMembers(members, (member) => + member.TBAs.some((tba) => (tba.badgeBalance ?? 0) > 0) + ); + + // Build address -> const + const result = {}; + for (const [, member] of members) { + result[member.address] = options.constant; + } + return result; +} diff --git a/src/strategies/station-constant-if-badge/schema.json b/src/strategies/station-constant-if-badge/schema.json new file mode 100644 index 000000000..b160185df --- /dev/null +++ b/src/strategies/station-constant-if-badge/schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/Strategy", + "definitions": { + "Strategy": { + "title": "Strategy", + "type": "object", + "properties": { + "membershipERC721": { + "type": "string", + "title": "Membership ERC721 contract address", + "examples": ["e.g. 0x651bf9C1A1dEC27b49061F2356482F7c6F3D18fb"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "badgesERC1155": { + "type": "string", + "title": "Badges ERC1155 contract address", + "examples": ["e.g. 0xd775e55e314164cce7f71f9f70fc905c907fc65e"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "badgeId": { + "type": "number", + "title": "ERC115 token ID for the Badge members should have", + "examples": ["e.g. 1"], + "minimum": 0 + }, + "constant": { + "type": "number", + "title": "Constant to return for voters who have the badge", + "examples": ["e.g. 1337"] + }, + "erc6551Registry": { + "type": "string", + "title": "ERC6551 registry address", + "examples": ["e.g. 0x02101dfB77FDE026414827Fdc604ddAF224F0921"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "erc6551Implementation": { + "type": "string", + "title": "ERC6551 implementation address", + "examples": ["e.g. 0x2d25602551487c3f3354dd80d76d54383a243358"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "erc6551Salt": { + "type": "string", + "title": "ERC6551 salt", + "examples": [ + "e.g. 0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "pattern": "^0x[a-fA-F0-9]{64}$", + "minLength": 66, + "maxLength": 66 + } + }, + "required": [], + "additionalProperties": false + } + } +} diff --git a/src/strategies/station-score-if-badge/README.md b/src/strategies/station-score-if-badge/README.md new file mode 100644 index 000000000..05250d6b7 --- /dev/null +++ b/src/strategies/station-score-if-badge/README.md @@ -0,0 +1,3 @@ +# station-score-if-badge + +This strategy returns a score for members of a [Station](https://www.station.express/) [GroupOS](https://groupos.xyz/) group that have a specific badge. diff --git a/src/strategies/station-score-if-badge/examples.json b/src/strategies/station-score-if-badge/examples.json new file mode 100644 index 000000000..0bf2f5ee2 --- /dev/null +++ b/src/strategies/station-score-if-badge/examples.json @@ -0,0 +1,26 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "station-score-if-badge", + "params": { + "membershipERC721": "0x33dbde2e093b7cf8446d9ac0de79220d42423501", + "badgesERC1155": "0xd1502a7659eaad60278ae3ef27edea849504f4da", + "badgeId": 1, + "scoreERC20": "0x5f120453dfd0c55f55370d1f718089ae0fcf6387", + "scoreDecimals": 18, + "erc6551Registry": "0x000000006551c19487814612e58FE06813775758", + "erc6551Implementation": "0x509b531c8e979c85375370c0ba92ac44173c2d12", + "erc6551Salt": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "network": "1", + "addresses": [ + "0x5A8CA059a7e185ad01695136dbe5A721D4e053D6", + "0x1ba671ce4718d76D42609affbB567Bf1E71E0852", + "0x008B1FFe244bF31896C833bFf4Ad9009E7A0f4Eb", + "0x1b515D521dd0CBA174Bee666ED8f42449C8cA8bb" + ], + "snapshot": 18779365 + } +] diff --git a/src/strategies/station-score-if-badge/index.ts b/src/strategies/station-score-if-badge/index.ts new file mode 100644 index 000000000..78a5046dc --- /dev/null +++ b/src/strategies/station-score-if-badge/index.ts @@ -0,0 +1,266 @@ +import { BigNumberish } from '@ethersproject/bignumber'; +import { formatUnits } from '@ethersproject/units'; +import { Contract } from '@ethersproject/contracts'; +import { Multicaller } from '../../utils'; + +export const author = 'espendk'; +export const version = '1.0.1'; + +// To avoid future memory issues, we limit the number of members supported by the strategy +export const MAX_MEMBERS = 500; + +export const erc721_abi = [ + 'function totalMinted() external view returns (uint256)', + 'function ownerOf(uint256 tokenId) external view returns (address)' +]; + +export const erc6551_registry_abi = [ + 'function account(address implementation, bytes32 salt, uint256 chainId, address tokenContract, uint256 tokenId) external view returns (address)' +]; + +export const erc1155_abi = [ + 'function balanceOf(address account, uint256 id) external view returns (uint256)' +]; + +export const erc20_abi = [ + 'function balanceOf(address account) external view returns (uint256)' +]; + +export type TokenBoundAccount = { + tokenID: number; + address: string; + badgeBalance?: number; + score?: number; +}; + +export type Member = { + address: string; + TBAs: TokenBoundAccount[]; +}; + +/** Enumerates all accounts with a membership NFT and returns a map of their + * addresses to their membership NFT ID and the corresponding token-bound account (TBA). + * + * Assumptions: + * - Membership NFTs are never burned. + */ +export async function getAllMembers( + network, + provider, + snapshot, + membershipERC721: string, + erc6551Registry: string, + erc6551Implementation: string, + erc6551Salt: string +): Promise> { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + // Get the number of membership NFTs that have been minted + const membershipERC721Contract = new Contract( + membershipERC721, + erc721_abi, + provider + ); + const totalMinted = await membershipERC721Contract.totalMinted({ blockTag }); + if (totalMinted > MAX_MEMBERS) { + throw new Error( + `Max number (${MAX_MEMBERS}) of members exceeded: ${totalMinted}` + ); + } + + // Get the member addresses and TBAs for all the membership NFTs that have been minted + const membershipERC721Multicaller = new Multicaller( + network, + provider, + erc721_abi, + { blockTag } + ); + const erc6551RegistryMulticaller = new Multicaller( + network, + provider, + erc6551_registry_abi, + { blockTag } + ); + + for (let id = 1; id <= totalMinted; ++id) { + membershipERC721Multicaller.call(id, membershipERC721, 'ownerOf', [id]); + erc6551RegistryMulticaller.call(id, erc6551Registry, 'account', [ + erc6551Implementation, + erc6551Salt, + network, + membershipERC721, + id + ]); + } + + const ownerByTokenIdPromise: Promise> = + membershipERC721Multicaller.execute(); + const tokenBoundAccountsByTokenId: Record = + await erc6551RegistryMulticaller.execute(); + const ownerByTokenId: Record = await ownerByTokenIdPromise; + + const members = new Map(); + for (let id = 1; id <= totalMinted; ++id) { + const memberAddress = ownerByTokenId[id]; + const tba = tokenBoundAccountsByTokenId[id]; + let member = members.get(memberAddress); + if (member === undefined) { + member = { address: memberAddress, TBAs: [] }; + members.set(memberAddress, member); + } + member.TBAs.push({ tokenID: id, address: tba }); + } + + return members; +} + +/** Fetches the badge balance for all the given members' TBA's. */ +export async function fetchBadgeBalances( + network, + provider, + snapshot, + members: Map, + badgesERC1155: string, + badgeId: number +) { + if (members.size === 0) { + return; + } + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + // Get the Badge balances for all the token bound accounts + const erc1155Multicaller = new Multicaller(network, provider, erc1155_abi, { + blockTag + }); + for (const [, member] of members) { + for (const tba of member.TBAs) { + erc1155Multicaller.call(tba.address, badgesERC1155, 'balanceOf', [ + tba.address, + badgeId + ]); + } + } + + const badgeBalanceByTBA: Record = + await erc1155Multicaller.execute(); + + for (const [, member] of members) { + for (const tba of member.TBAs) { + tba.badgeBalance = parseInt(badgeBalanceByTBA[tba.address]); + } + } +} + +/** Fetches the score for all the given members' TBA's. */ +export async function fetchScores( + network, + provider, + snapshot, + members: Map, + scoreERC20: string, + scoreDecimals: number +) { + if (members.size === 0) { + return; + } + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + // Get the Scores for all the members with the badge + const erc20Multicaller = new Multicaller(network, provider, erc20_abi, { + blockTag + }); + for (const [, member] of members) { + for (const tba of member.TBAs) { + erc20Multicaller.call(tba.address, scoreERC20, 'balanceOf', [ + tba.address + ]); + } + } + + const scoreByMember: Record = + await erc20Multicaller.execute(); + + for (const [, member] of members) { + for (const tba of member.TBAs) { + tba.score = parseFloat( + formatUnits(scoreByMember[tba.address], scoreDecimals) + ); + } + } +} + +/** Filters the members by a predicate. */ +export function filterMembers( + members: Map, + predicate: (member: Member) => boolean +): Map { + const result = new Map(); + + for (const [, member] of members) { + if (predicate(member)) { + result.set(member.address, member); + } + } + + return result; +} + +export async function strategy( + space, + network, + provider, + addresses, + options, + snapshot +): Promise> { + let members = await getAllMembers( + network, + provider, + snapshot, + options.membershipERC721, + options.erc6551Registry, + options.erc6551Implementation, + options.erc6551Salt + ); + + // Keep only members in the list of queried addresses + members = filterMembers(members, (member) => + addresses.includes(member.address) + ); + + await fetchBadgeBalances( + network, + provider, + snapshot, + members, + options.badgesERC1155, + options.badgeId + ); + + // Keep only members with a badge + members = filterMembers(members, (member) => + member.TBAs.some((tba) => (tba.badgeBalance ?? 0) > 0) + ); + + await fetchScores( + network, + provider, + snapshot, + members, + options.scoreERC20, + options.scoreDecimals + ); + + // Build address -> score mapping + // Include only scores for TBA's with a badge + const result = {}; + for (const [, member] of members) { + result[member.address] = 0; + for (const tba of member.TBAs) { + if ((tba.badgeBalance ?? 0) > 0) { + result[member.address] += tba.score; + } + } + } + return result; +} diff --git a/src/strategies/station-score-if-badge/schema.json b/src/strategies/station-score-if-badge/schema.json new file mode 100644 index 000000000..c6ccb3598 --- /dev/null +++ b/src/strategies/station-score-if-badge/schema.json @@ -0,0 +1,76 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/Strategy", + "definitions": { + "Strategy": { + "title": "Strategy", + "type": "object", + "properties": { + "membershipERC721": { + "type": "string", + "title": "Membership ERC721 contract address", + "examples": ["e.g. 0xd71c8619209cc95a81f8d9ba4fd704d9eff3ddd6"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "badgesERC1155": { + "type": "string", + "title": "Badges ERC1155 contract address", + "examples": ["e.g. 0xd775e55e314164cce7f71f9f70fc905c907fc65e"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "badgeId": { + "type": "number", + "title": "ERC115 token ID for the Badge members should have", + "examples": ["e.g. 1"], + "minimum": 0 + }, + "scoreERC20": { + "type": "string", + "title": "Member score ERC20 contract address", + "examples": ["e.g. 0x30D602cBfe96FC2C83fF31Bdf79d48De65f80733"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "scoreDecimals": { + "type": "number", + "title": "Member score decimals", + "examples": ["e.g. 18"], + "minimum": 0 + }, + "erc6551Registry": { + "type": "string", + "title": "ERC6551 registry address", + "examples": ["e.g. 0x02101dfB77FDE026414827Fdc604ddAF224F0921"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "erc6551Implementation": { + "type": "string", + "title": "ERC6551 implementation address", + "examples": ["e.g. 0x2d25602551487c3f3354dd80d76d54383a243358"], + "pattern": "^0x[a-fA-F0-9]{40}$", + "minLength": 42, + "maxLength": 42 + }, + "erc6551Salt": { + "type": "string", + "title": "ERC6551 salt", + "examples": [ + "e.g. 0x0000000000000000000000000000000000000000000000000000000000000000" + ], + "pattern": "^0x[a-fA-F0-9]{64}$", + "minLength": 66, + "maxLength": 66 + } + }, + "required": [], + "additionalProperties": false + } + } +} diff --git a/src/strategies/streamr/README.md b/src/strategies/streamr/README.md new file mode 100644 index 000000000..861a0d8e1 --- /dev/null +++ b/src/strategies/streamr/README.md @@ -0,0 +1,18 @@ +# Streamr snapshot strategy + +The Streamr Network is a peer-to-peer network for publishing and subscribing to data in real-time. Applications use it for decentralized messaging, for example sharing data across applications or broadcasting real-time state changes to large audiences. The decentralized nature of the system makes the data transport scalable, robust, secure, tamper proof, and censorship resistant. + +Operators are the node running "miners" in the Streamr Network. They run Streamr nodes, subscribe to streams, and stake DATA in the Sponsorship contract(s) of those streams. When they subscribe, they help making that stream more robust. In return, they receive DATA tokens from the Sponsorship contract, in proportion to their stake. + +This is why part of the Operators' DATA tokens are staked in Sponsorships (through an Operator contract that they control). Only a small portion of DATA is expected to be in the Streamr Network participants' wallets, the rest is staked or delegated into the Streamr Network. + +'''The point of the Streamr snapshot strategy''' is to allocate voting power not only according to DATA token holding (as in the plain erc-20-balance-of strategy), but also counting in the DATA tokens the token holders control via staking and delegation (NOTE: at first, only implemented for stakers. Counting delegated DATA may be added later). + +## Parameters + +```json +{ + "tokenAddress": "0x3a9A81d576d83FF21f26f325066054540720fC34", + "operatorFactoryAddress": "0x935734e66729b69260543Cf6e5EfeB42AC962183" +} +``` diff --git a/src/strategies/streamr/examples.json b/src/strategies/streamr/examples.json new file mode 100644 index 000000000..7e0c62765 --- /dev/null +++ b/src/strategies/streamr/examples.json @@ -0,0 +1,36 @@ +[ + { + "name": "Streamr", + "strategy": { + "name": "streamr", + "params": { + "tokenAddress": "0x3a9A81d576d83FF21f26f325066054540720fC34", + "operatorFactoryAddress": "0x935734e66729b69260543Cf6e5EfeB42AC962183" + } + }, + "network": "137", + "addresses": [ + "0xD6c2bF543491337D81eC9b7d96CFbC04fCB3F4a0", + "0x2112a4b0F6500Bc0c1AebbAb547eaAB6862acC57", + "0x488c0Ba58dAc039fAA30D91DdF86CD75B7Dbe4cD", + "0x013B3576Ef573b7D1eD82Fc4C08bb9dB24EBFa4f", + "0xAd6A86cC6F6e1169292e7bf325564e0B9AC812A7", + "0x9bC7F7Aa4b4395391089dA8588fFb0842b5483A6", + "0xab034dCE46BC70Fd1A032f883B46f6d2BC18DD1a", + "0xB20C20ba1B94A2Fb746091c8D83937Ee35644251", + "0x02fdB03705ed8F92fB8C5073990FC5D4d169FAF8", + "0xc65394747cCCFc5e5DED39da191Bc1C3f9FDB3a2", + "0x5015C1654D8829d549EE976e462bD8B7adE847C5", + "0x6Fc263FF819609E5F4c3d5b8D742Fa94B8a3fEaD", + "0xA39E67290f89dC2419d5F6bf7fE8cc448A80CD1A", + "0x8d1BEc0692cbFC14CC53af1f0D1aBFFC79DBC04f", + "0x02880624Bb0743E3144e8943E29Fd1DDe66cF72c", + "0x8da1A015bb11BcC718CF1f49804282Fa5dBA75C7", + "0x444d26eC73DBaC8d78299597135dA5C0234BafA1", + "0x2594664714882D47aC207b4C46751649d886Bdf8", + "0x41c00f648E781742A5C2CB83B34a05D11D833C4D", + "0x40f076d4AbE73Be1eB21B862A578cDd583910556" + ], + "snapshot": 50305259 + } +] diff --git a/src/strategies/streamr/index.ts b/src/strategies/streamr/index.ts new file mode 100644 index 000000000..d29822e6d --- /dev/null +++ b/src/strategies/streamr/index.ts @@ -0,0 +1,71 @@ +import { BigNumber } from '@ethersproject/bignumber'; +import { formatEther } from '@ethersproject/units'; +import { Multicaller } from '../../utils'; + +export const author = 'streamr-dev'; +export const version = '0.1.1'; + +const ADDRESS_ZERO = '0x0000000000000000000000000000000000000000'; + +const abi = [ + 'function balanceOf(address account) external view returns (uint256)', // in DATA token + 'function operators(address owner) external view returns (address)', // in OperatorFactory, returns operator contract address + 'function valueWithoutEarnings() external view returns (uint)' // in Operator contract +]; + +type Balances = { + tokens: BigNumber; + staked?: BigNumber; +}; + +export async function strategy( + space, + network, + provider, + addresses: string[], + options, + snapshot +): Promise> { + const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + + // find the Operator contract deployed by the DATA token holder, will return ADDRESS_ZERO if not found + const getContractOf = new Multicaller(network, provider, abi, { blockTag }); + for (const tokenHolder of addresses) { + getContractOf.call( + tokenHolder, + options.operatorFactoryAddress, + 'operators', + [tokenHolder] + ); + } + const contractOf: Record = await getContractOf.execute(); + + // get both the "cash in hand", and DATA tokens staked through the Operator contract (Operator value) + const getBalances = new Multicaller(network, provider, abi, { blockTag }); + for (const tokenHolder of addresses) { + getBalances.call( + `${tokenHolder}.tokens`, + options.tokenAddress, + 'balanceOf', + [tokenHolder] + ); + if (contractOf[tokenHolder] != ADDRESS_ZERO) { + getBalances.call( + `${tokenHolder}.staked`, + contractOf[tokenHolder], + 'valueWithoutEarnings', + [] + ); + } + } + const balances: Record = await getBalances.execute(); + + return Object.fromEntries( + Object.entries(balances).map( + ([address, { tokens, staked = BigNumber.from(0) }]) => [ + address, + parseFloat(formatEther(tokens.add(staked))) + ] + ) + ); +} diff --git a/src/strategies/gysr-pending-rewards/schema.json b/src/strategies/streamr/schema.json similarity index 61% rename from src/strategies/gysr-pending-rewards/schema.json rename to src/strategies/streamr/schema.json index 468a4eb77..781cc5ac2 100644 --- a/src/strategies/gysr-pending-rewards/schema.json +++ b/src/strategies/streamr/schema.json @@ -6,30 +6,24 @@ "title": "Strategy", "type": "object", "properties": { - "symbol": { + "tokenAddress": { "type": "string", - "title": "Symbol", - "examples": ["e.g. UNI"], - "maxLength": 16 - }, - "pool": { - "type": "string", - "title": "Pool Address", - "examples": ["e.g. 0xE48eddbBcA614be5416f20dE57D858562b72479d"], + "title": "DATA token address", + "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], "pattern": "^0x[a-fA-F0-9]{40}$", "minLength": 42, "maxLength": 42 }, - "rewardToken": { + "operatorFactoryAddress": { "type": "string", - "title": "Reward Token Address", + "title": "OperatorFactory address", "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], "pattern": "^0x[a-fA-F0-9]{40}$", "minLength": 42, "maxLength": 42 } }, - "required": ["pool"], + "required": ["tokenAddress", "operatorFactoryAddress"], "additionalProperties": false } } diff --git a/src/strategies/sumami-holders/README.md b/src/strategies/sumami-holders/README.md deleted file mode 100644 index 40fc546a8..000000000 --- a/src/strategies/sumami-holders/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Contract call strategy - -Allows the tokens locked in marinate contract to be used to calculate voter scores. - -## Examples - -Can be used instead of the erc20-balance-of strategy, the space config will look like this: - -```JSON -{ - "address": "0xe6d557d416ff5640235119369c7e26AA18a906D7", - "marinateLevels": [0, 1, 2, 3], - "symbol": "sUMAMI", - "marinateAddress": "0x190a6b6E8e4D9B8324E1F97127c588C5b082d94b", - "decimals": 9 -} diff --git a/src/strategies/sumami-holders/examples.json b/src/strategies/sumami-holders/examples.json deleted file mode 100644 index 57093d9c2..000000000 --- a/src/strategies/sumami-holders/examples.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "sumami-holders", - "params": { - "address": "0xe6d557d416ff5640235119369c7e26AA18a906D7", - "marinateLevels": [0, 1, 2, 3], - "symbol": "sUMAMI", - "marinateAddress": "0x190a6b6E8e4D9B8324E1F97127c588C5b082d94b", - "decimals": 9 - } - }, - "network": "42161", - "addresses": [ - "0x541D67bEdBfe820b4E58712bf032C7250548D733", - "0x93615705c74bfd7d7c0b0d9cfd5d04ee00d90471", - "0xb9d24761ebb3e3a88b8cf20e77f56ccf026a2044", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e", - "0x1d5E65a087eBc3d03a294412E46CE5D6882969f4", - "0x1f254336E5c46639A851b9CfC165697150a6c327", - "0x2ec3F80BeDA63Ede96BA20375032CDD3aAfb3030", - "0x4AcBcA6BE2f8D2540bBF4CA77E45dA0A4a095Fa2", - "0x4F3D348a6D09837Ae7961B1E0cEe2cc118cec777", - "0x6D7f23A509E212Ba7773EC1b2505d1A134f54fbe", - "0x07a1f6fc89223c5ebD4e4ddaE89Ac97629856A0f", - "0x8d5F05270da470e015b67Ab5042BDbE2D2FEFB48", - "0xb2273e70eda1cb2e16bdfd7a15c031d12400f7ca", - "0x8f60501dE5b9b01F9EAf1214dbE1924aA97F7fd0", - "0x9B8e8dD9151260c21CB6D7cc59067cd8DF306D58", - "0x17ea92D6FfbAA1c7F6B117c1E9D0c88ABdc8b84C", - "0x9a32b1a10504489f9b88106acec79c672630158c" - ], - "snapshot": 4178460 - } -] diff --git a/src/strategies/sumami-holders/index.ts b/src/strategies/sumami-holders/index.ts deleted file mode 100644 index 0c8226da4..000000000 --- a/src/strategies/sumami-holders/index.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; -import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; - -export const author = 'arugulo'; -export const version = '0.1.0'; - -// Merged ABI for sUMAMI and Marinate contracts -const abi = [ - 'function balanceOf(address account) view returns (uint256)', - 'function stakedBalance(address account, uint32 level) view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - if (options.marinateLevels.length > 4) { - return []; - } - const sUmamiBalances = await erc20BalanceOfStrategy( - space, - network, - provider, - addresses, - options, - snapshot - ); - - const marinateBalances = await Promise.all( - options.marinateLevels.map((level: number) => - multicall( - network, - provider, - abi, - addresses.map((address: any) => [ - options.marinateAddress, - 'stakedBalance', - [address, level], - { blockTag } - ]), - { blockTag } - ) - ) - ); - - const totalMarinateBalances = marinateBalances.reduce( - //@ts-ignore - (prev: any, cur: any) => - cur.map( - (balance, idx) => - (prev[idx] || 0) + - parseFloat(formatUnits(balance.toString(), options.decimals)) - ), - [] - ); - - return Object.fromEntries( - Object.entries(sUmamiBalances).map((address, index) => [ - address[0], - //@ts-ignore - address[1] + totalMarinateBalances[index] - ]) - ); -} diff --git a/src/strategies/sunder/examples.json b/src/strategies/sunder/examples.json deleted file mode 100644 index 3827e91e5..000000000 --- a/src/strategies/sunder/examples.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "sunder", - "params": { - "token": "0xaF5A2E9E0BCf9Ec3bf447F511F8Ac028F5959077", - "symbol": "sudner (token)", - "decimals": 18, - "balanceOfAdds": "0x20a183539DF9dE2223991329FE68d5cCc5c8C0D8" - } - }, - "network": "42", - "addresses": [ - "0x02Ec1090D59cbAA9D58EAeBb3328dF56A6dEd2D3", - "0x714AfdEB0c24127ec03149005AD59F80bbFD71d7" - ], - "snapshot": 26570329 - } -] diff --git a/src/strategies/sunder/index.ts b/src/strategies/sunder/index.ts deleted file mode 100644 index 4a5501e4a..000000000 --- a/src/strategies/sunder/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = 'krotos-arch'; -export const version = '0.0.1'; - -const abi = [ - 'function balanceOfDToken(address _token, address _account) public returns (uint256 _balance)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - addresses.forEach((address) => - multi.call(address, options.balanceOfAdds, 'balanceOfDToken', [ - options.token, - address - ]) - ); - const result: Record = await multi.execute(); - - return Object.fromEntries( - Object.entries(result).map(([address, balance]) => [ - address, - parseFloat(formatUnits(balance, options.decimals)) - ]) - ); -} diff --git a/src/strategies/svs-staking/examples.json b/src/strategies/svs-staking/examples.json deleted file mode 100644 index 27cea55ea..000000000 --- a/src/strategies/svs-staking/examples.json +++ /dev/null @@ -1,23 +0,0 @@ -[ - { - "name": "SVS stakers and holders", - "strategy": { - "name": "svs-staking", - "params": { - "symbol": "SVS", - "decimals": 0, - "tokenAddress": "0x219B8aB790dECC32444a6600971c7C3718252539", - "stakingAddress": "0x12753244901f9E612A471c15C7E5336e813D2e0B" - } - }, - "network": "1", - "addresses": [ - "0x24d19f100ba142543a863fc2294b188e35ab55b9", - "0xC0d6d28811df8466E4BAC33CfC9Ed6e745900a07", - "0xFdA9F18221d14F5FAD1ff8a0dbA92A716Ba8144D", - "0x43522a5abA7c0FEad84b6614C208B5162A1b4ADF", - "0xcA50e4211B330295212FF9C1160F5DF3E0Ccd9D8" - ], - "snapshot": 13449843 - } -] diff --git a/src/strategies/svs-staking/index.ts b/src/strategies/svs-staking/index.ts deleted file mode 100644 index 480b014e5..000000000 --- a/src/strategies/svs-staking/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; - -export const author = 'fsjuhl'; -export const version = '0.1.0'; - -const stakingAbi = [ - 'function getVampsBuried(address burier) view returns (uint256[])' -]; - -const tokenAbi = ['function balanceOf(address owner) view returns (uint256)']; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const stakersResponse = await multicall( - network, - provider, - stakingAbi, - addresses.map((address: any) => [ - options.stakingAddress, - 'getVampsBuried', - [address] - ]), - { blockTag } - ); - - const holdersResponse = await multicall( - network, - provider, - tokenAbi, - addresses.map((address: any) => [ - options.tokenAddress, - 'balanceOf', - [address] - ]), - { blockTag } - ); - - return Object.fromEntries( - stakersResponse.map((value, i) => [ - addresses[i], - value[0].length + - parseFloat( - formatUnits(holdersResponse[i][0].toString(), options.decimals) - ) - ]) - ); -} diff --git a/src/strategies/swapr/README.md b/src/strategies/swapr/README.md deleted file mode 100644 index 4d486c7b2..000000000 --- a/src/strategies/swapr/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Swapr - -This strategy returns balances of the underlying token in Swapr pairs (including the liquidity staked on the farming campaigns offered by the platform). - -Here is an example of parameters: - -```json -{ - "address": "0xdE903E2712288A1dA82942DDdF2c20529565aC30" -} -``` - -- _address_ - the underlying token diff --git a/src/strategies/swapr/commons.ts b/src/strategies/swapr/commons.ts deleted file mode 100644 index b2b64f6ff..000000000 --- a/src/strategies/swapr/commons.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const SWAPR_SUBGRAPH_URL = { - '1': 'https://api.thegraph.com/subgraphs/name/luzzif/swapr-mainnet-v2', - '100': 'https://api.thegraph.com/subgraphs/name/luzzif/swapr-xdai-v2', - '42161': - 'https://api.thegraph.com/subgraphs/name/luzzif/swapr-arbitrum-one-v2' -}; - -export const mergeBalanceMaps = ( - outputMap: { [address: string]: number }, - inputMap: { [address: string]: number } -) => { - Object.entries(inputMap).forEach(([account, balance]) => { - outputMap[account] = (outputMap[account] || 0) + balance; - }); -}; diff --git a/src/strategies/swapr/examples.json b/src/strategies/swapr/examples.json deleted file mode 100644 index ef6992687..000000000 --- a/src/strategies/swapr/examples.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "swapr", - "params": { - "symbol": "SWPR", - "address": "0xdE903E2712288A1dA82942DDdF2c20529565aC30" - } - }, - "network": "42161", - "addresses": ["0xa5A29f81EEE450eC189b2F8B4562af1785595D69"], - "snapshot": 2867713 - } -] diff --git a/src/strategies/swapr/index.ts b/src/strategies/swapr/index.ts deleted file mode 100644 index bf4ae8cfb..000000000 --- a/src/strategies/swapr/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { getSwaprLiquidityProvidersBalance } from './swapr-lps'; -import { strategy as erc20BalanceOfStartegy } from '../erc20-balance-of'; -import { mergeBalanceMaps } from './commons'; -import { getAddress } from '@ethersproject/address'; - -export const author = 'luzzif'; -export const version = '0.1.0'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const lpData = await getSwaprLiquidityProvidersBalance( - network, - addresses, - options, - snapshot - ); - - let i = 0; - const PAGE_SIZE = 250; - let rawErc20HoldersData: { [address: string]: number } = {}; - while (true) { - const pageData = await erc20BalanceOfStartegy( - space, - network, - provider, - addresses.slice(PAGE_SIZE * i, PAGE_SIZE * i + PAGE_SIZE), - options, - snapshot - ); - rawErc20HoldersData = { ...rawErc20HoldersData, ...pageData }; - if (Object.keys(pageData).length < PAGE_SIZE) break; - i++; - } - - // make sure the addresses have the correct casing before - // merging the balance maps - const erc20HoldersData = Object.entries(rawErc20HoldersData).reduce( - (accumulator, [address, balance]) => { - accumulator[getAddress(address)] = balance; - return accumulator; - }, - {} - ); - - const score: { [address: string]: number } = {}; - mergeBalanceMaps(score, lpData); - mergeBalanceMaps(score, erc20HoldersData); - return score; -} diff --git a/src/strategies/swapr/swapr-lps.ts b/src/strategies/swapr/swapr-lps.ts deleted file mode 100644 index 2ccf51dba..000000000 --- a/src/strategies/swapr/swapr-lps.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { Decimal } from 'decimal.js-light'; -import { getAddress } from '@ethersproject/address'; -import { mergeBalanceMaps, SWAPR_SUBGRAPH_URL } from './commons'; -import { subgraphRequest } from '../../utils'; - -interface StandardLiquidityPosition { - id: string; - user: { id: string }; - liquidityTokenBalance: string; - pair: { - totalSupply: string; - reserve0: string; - reserve1: string; - }; -} - -interface StakedLiquidityPosition { - id: string; - user: { id: string }; - stakedAmount: string; - targetedPair: { - totalSupply: string; - reserve0: string; - reserve1: string; - }; -} - -const mergeStandardAndStakedPositions = ( - standardPositions: StandardLiquidityPosition[], - stakedPositions: StakedLiquidityPosition[] -) => { - return stakedPositions.reduce( - (accumulator: StandardLiquidityPosition[], stakedPosition) => { - const index = accumulator.findIndex( - (p) => p.user.id.toLowerCase() === stakedPosition.user.id.toLowerCase() - ); - if (index >= 0) - accumulator[index].liquidityTokenBalance = new Decimal( - accumulator[index].liquidityTokenBalance - ) - .plus(stakedPosition.stakedAmount) - .toString(); - else - accumulator.push({ - ...stakedPosition, - pair: stakedPosition.targetedPair, - liquidityTokenBalance: stakedPosition.stakedAmount - }); - return accumulator; - }, - standardPositions - ); -}; - -const getPositions = async ( - network, - addresses, - options, - snapshot -): Promise<{ - positionsByToken0: StandardLiquidityPosition[]; - positionsByToken1: StandardLiquidityPosition[]; -}> => { - const wantedTokenAddress = options.address; - const swaprSubgraphUrl = SWAPR_SUBGRAPH_URL[network]; - - const [token0Query, token1Query] = ['token0', 'token1'].map((key) => ({ - pairs: { - __args: { - where: { - [key]: wantedTokenAddress.toLowerCase() - }, - first: 1000 - }, - id: true - } - })); - if (snapshot !== 'latest') { - // @ts-ignore - token0Query.pairs.__args.block = { number: snapshot }; - // @ts-ignore - token1Query.pairs.__args.block = { number: snapshot }; - } - const swprPairsByToken0 = await subgraphRequest( - swaprSubgraphUrl, - token0Query - ); - const swprPairsByToken1 = await subgraphRequest( - swaprSubgraphUrl, - token1Query - ); - - const [liquidityPositionsByToken0Query, liquidityPositionsByToken1Query] = [ - swprPairsByToken0, - swprPairsByToken1 - ].map((wrappedPairs) => ({ - liquidityPositions: { - __args: { - where: { - user_in: addresses.map((address) => address.toLowerCase()), - pair_in: wrappedPairs.pairs.map((pair) => pair.id), - liquidityTokenBalance_gt: 0 - }, - first: 1000 - }, - user: { - id: true - }, - liquidityTokenBalance: true, - pair: { - totalSupply: true, - reserve0: true, - reserve1: true - } - } - })); - const liquidityPositionsByToken0 = await subgraphRequest( - swaprSubgraphUrl, - liquidityPositionsByToken0Query - ); - const liquidityPositionsByToken1 = await subgraphRequest( - swaprSubgraphUrl, - liquidityPositionsByToken1Query - ); - - const [ - liquidityMiningPositionsByToken0Query, - liquidityMiningPositionsByToken1Query - ] = [swprPairsByToken0, swprPairsByToken1].map((wrappedPairs) => ({ - liquidityMiningPositions: { - __args: { - where: { - user_in: addresses.map((address) => address.toLowerCase()), - targetedPair_in: wrappedPairs.pairs.map((pair) => pair.id), - stakedAmount_gt: 0 - }, - first: 1000 - }, - user: { - id: true - }, - stakedAmount: true, - targetedPair: { - totalSupply: true, - reserve0: true, - reserve1: true - } - } - })); - - const liquidityMiningPositionsByToken0 = await subgraphRequest( - swaprSubgraphUrl, - liquidityMiningPositionsByToken0Query - ); - const liquidityMiningPositionsByToken1 = await subgraphRequest( - swaprSubgraphUrl, - liquidityMiningPositionsByToken1Query - ); - - return { - positionsByToken0: mergeStandardAndStakedPositions( - liquidityPositionsByToken0.liquidityPositions, - liquidityMiningPositionsByToken0.liquidityMiningPositions - ), - positionsByToken1: mergeStandardAndStakedPositions( - liquidityPositionsByToken1.liquidityPositions, - liquidityMiningPositionsByToken1.liquidityMiningPositions - ) - }; -}; - -const lpDataToBalanceMap = ( - positions: StandardLiquidityPosition[], - useToken0Data: boolean -) => { - return positions.reduce( - (accumulator: { [address: string]: number }, position) => { - const userLpTokenBalance = new Decimal(position.liquidityTokenBalance); - const pairTotalSupply = new Decimal(position.pair.totalSupply); - const userPoolPercentage = userLpTokenBalance.dividedBy(pairTotalSupply); - const userHolding = new Decimal( - useToken0Data ? position.pair.reserve0 : position.pair.reserve1 - ).mul(userPoolPercentage); - const userAddress = getAddress(position.user.id); - accumulator[userAddress] = - (accumulator[userAddress] || 0) + userHolding.toNumber(); - return accumulator; - }, - {} - ); -}; - -export const getSwaprLiquidityProvidersBalance = async ( - network, - addresses, - options, - snapshot -): Promise<{ - [address: string]: number; -}> => { - const { positionsByToken0, positionsByToken1 } = await getPositions( - network, - addresses, - options, - snapshot - ); - const balanceMap: { [address: string]: number } = {}; - mergeBalanceMaps(balanceMap, lpDataToBalanceMap(positionsByToken0, true)); - mergeBalanceMaps(balanceMap, lpDataToBalanceMap(positionsByToken1, false)); - return balanceMap; -}; diff --git a/src/strategies/synthetix-non-quadratic/README.md b/src/strategies/synthetix-non-quadratic/README.md deleted file mode 100644 index 5aaf2215f..000000000 --- a/src/strategies/synthetix-non-quadratic/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Non-Quadratic Debt Percentage Strategy - -Calculates the weighting of voters, based on their debt percentage in the previous fee period. - -## Examples - -Can be used instead of the erc20-balance-of strategy, the space config will look like this: - -```JSON -{ - "strategies": [ - ["synthetix-non-quadratic"] - ] -} -``` diff --git a/src/strategies/synthetix-non-quadratic/examples.json b/src/strategies/synthetix-non-quadratic/examples.json deleted file mode 100644 index 937bf7e13..000000000 --- a/src/strategies/synthetix-non-quadratic/examples.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "synthetix-non-quadratic", - "params": { - "address": "0x023c66b7e13d30a3c46aa433fd2829763d5817c5", - "symbol": "WD" - } - }, - "network": "1", - "addresses": [ - "0x78b037B39704e88a82DD23CFBE1f57f6AeF8EBC5", - "0x0bc3668d2AaFa53eD5E5134bA13ec74ea195D000", - "0xcAc59F91E4536Bc0E79aB816a5cD54e89f10433C", - "0x6dc88B231Cd04Dd1b1e525161162993F47140006", - "0x935D2fD458fdf41B6F7B62471f593797866a3Ce6", - "0x24e445fe7708Bf4bC2ae8d4df1694C98Af8BDE4F", - "0x49be88f0fcc3a8393a59d3688480d7d253c37d2a", - "0x27Cc4d6bc95b55a3a981BF1F1c7261CDa7bB0931" - ], - "snapshot": 13228907 - } -] diff --git a/src/strategies/synthetix-non-quadratic/index.ts b/src/strategies/synthetix-non-quadratic/index.ts deleted file mode 100644 index 0d42dd088..000000000 --- a/src/strategies/synthetix-non-quadratic/index.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { getAddress } from '@ethersproject/address'; -import { BigNumber } from '@ethersproject/bignumber'; -import { Contract } from '@ethersproject/contracts'; -import { Provider } from '@ethersproject/providers'; -import { - subgraphRequest - // ipfsGet -} from '../../utils'; -import { - DebtCacheABI, - debtL1, - debtL2, - returnGraphParams, - SNXHoldersResult, - SynthetixStateABI -} from '../synthetix/helper'; - -export const author = 'andytcf'; -export const version = '1.0.0'; - -const MED_PRECISE_UNIT = 1e18; - -// @TODO: check if most-up-to-date version (using https://contracts.synthetix.io/SynthetixState) -const SynthetixStateContractAddress = - '0x4b9Ca5607f1fF8019c1C6A3c2f0CC8de622D5B82'; -// @TODO: check if most-up-to-date version (using http://contracts.synthetix.io/DebtCache) -const DebtCacheContractAddress = '0xe92B4c7428152052B0930c81F4c687a5F1A12292'; - -const defaultGraphs = { - '1': 'https://api.thegraph.com/subgraphs/name/killerbyte/synthetix', - '10': 'https://api.thegraph.com/subgraphs/name/synthetixio-team/optimism-issuance' -}; - -// @TODO: update with the latest ovm snapshot -// const ovmSnapshotJSON = 'QmNwvhq4By1Mownjycg7bWSXqbJWMVyAWRZ1K4mjxuvGXg'; - -const loadLastDebtLedgerEntry = async ( - provider: Provider, - snapshot: number | string -) => { - const contract = new Contract( - SynthetixStateContractAddress, - SynthetixStateABI, - provider - ); - - const lastDebtLedgerEntry = await contract.lastDebtLedgerEntry({ - blockTag: snapshot - }); - - return BigNumber.from(lastDebtLedgerEntry); -}; - -const loadL1TotalDebt = async ( - provider: Provider, - snapshot: number | string -) => { - const contract = new Contract( - DebtCacheContractAddress, - DebtCacheABI, - provider - ); - - const currentDebtObject = await contract.currentDebt({ - blockTag: snapshot - }); - - return Number(currentDebtObject.debt) / MED_PRECISE_UNIT; -}; - -export async function strategy( - _space, - _network, - _provider, - _addresses, - _, - snapshot -) { - const score = {}; - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - /* Global Constants */ - - const totalL1Debt = await loadL1TotalDebt(_provider, snapshot); // (high-precision 1e18) - const lastDebtLedgerEntry = await loadLastDebtLedgerEntry( - _provider, - snapshot - ); - - /* EDIT THESE FOR OVM */ - - // @TODO update the currentDebt for the snapshot from (https://contracts.synthetix.io/ovm/DebtCache) - const totalL2Debt = 22617610; - // @TODO update the lastDebtLedgerEntry from (https://contracts.synthetix.io/ovm/SynthetixState) - const lastDebtLedgerEntryL2 = 20222730523217499684984991; - // @TODO update the comparison between OVM:ETH c-ratios at the time of snapshot - const normalisedL2CRatio = 600 / 450; - // @TODO update the L2 block number to use - const L2BlockNumber = 1770186; - - const scaledTotalL2Debt = totalL2Debt * normalisedL2CRatio; - - /* --------------- */ - - /* Using the subgraph, we get the relevant L1 calculations */ - - const l1Results = (await subgraphRequest( - defaultGraphs[1], - returnGraphParams(blockTag, _addresses) - )) as SNXHoldersResult; - - if (l1Results && l1Results.snxholders) { - for (let i = 0; i < l1Results.snxholders.length; i++) { - const holder = l1Results.snxholders[i]; - const vote = await debtL1( - holder.initialDebtOwnership, - holder.debtEntryAtIndex, - totalL1Debt, - scaledTotalL2Debt, - lastDebtLedgerEntry, - false - ); - score[getAddress(holder.id)] = vote; - } - } - - /* Using the subgraph, we get the relevant L2 calculations */ - - const l2Results = (await subgraphRequest( - defaultGraphs[10], - returnGraphParams(L2BlockNumber, _addresses) - )) as SNXHoldersResult; - - // @notice fallback for when subgraph is down - /* - const OVMSnapshot = await ipfsGet('gateway.pinata.cloud', ovmSnapshotJSON); - const array = Object.assign( - {}, - ...OVMSnapshot.data.snxholders.map((key) => ({ - [getAddress(key.id)]: { - initialDebtOwnership: key.initialDebtOwnership, - debtEntryAtIndex: key.debtEntryAtIndex - } - })) - ); - for (let k = 0; k < _addresses.length; k++) { - const address = _addresses[k]; - if (array[getAddress(address)]) { - score[getAddress(address)] += await quadraticWeightedVoteL2( - array[getAddress(address)].initialDebtOwnership, - array[getAddress(address)].debtEntryAtIndex, - totalL1Debt, - scaledTotalL2Debt, - lastDebtLedgerEntryL2 - ); - } else { - continue; - } - } - */ - - if (l2Results && l2Results.snxholders) { - for (let i = 0; i < l2Results.snxholders.length; i++) { - const holder = l2Results.snxholders[i]; - - const vote = await debtL2( - holder.initialDebtOwnership, - holder.debtEntryAtIndex, - totalL1Debt, - scaledTotalL2Debt, - lastDebtLedgerEntryL2, - false - ); - - if (score[getAddress(holder.id)]) { - score[getAddress(holder.id)] += vote; - } else { - score[getAddress(holder.id)] = vote; - } - } - } - - return score || {}; -} diff --git a/src/strategies/synthetix-non-quadratic_2/README.md b/src/strategies/synthetix-non-quadratic_2/README.md deleted file mode 100644 index 1d849ec83..000000000 --- a/src/strategies/synthetix-non-quadratic_2/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Non-Quadratic Debt Percentage Strategy - -Calculates the weighting of voters, based on their debt percentage in the previous fee period. - -## Examples - -Can be used instead of the erc20-balance-of strategy, the space config will look like this: - -```JSON -{ - "strategies": [ - ["synthetix-non-quadratic_2"] - ] -} -``` diff --git a/src/strategies/synthetix-non-quadratic_2/examples.json b/src/strategies/synthetix-non-quadratic_2/examples.json deleted file mode 100644 index c18fd6915..000000000 --- a/src/strategies/synthetix-non-quadratic_2/examples.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "synthetix-non-quadratic_2", - "params": { - "address": "0x023c66b7e13d30a3c46aa433fd2829763d5817c5", - "symbol": "WD", - "totalL2Debt": 40201854, - "lastDebtLedgerEntryL2": 10909822163955610660349890, - "L2BlockNumber": 4750287 - } - }, - "network": "1", - "addresses": [ - "0x78b037B39704e88a82DD23CFBE1f57f6AeF8EBC5", - "0x0bc3668d2AaFa53eD5E5134bA13ec74ea195D000", - "0xcAc59F91E4536Bc0E79aB816a5cD54e89f10433C", - "0x6dc88B231Cd04Dd1b1e525161162993F47140006", - "0x935D2fD458fdf41B6F7B62471f593797866a3Ce6", - "0x24e445fe7708Bf4bC2ae8d4df1694C98Af8BDE4F", - "0x49be88f0fcc3a8393a59d3688480d7d253c37d2a", - "0x27Cc4d6bc95b55a3a981BF1F1c7261CDa7bB0931" - ], - "snapshot": 14443440 - } -] diff --git a/src/strategies/synthetix-non-quadratic_2/index.ts b/src/strategies/synthetix-non-quadratic_2/index.ts deleted file mode 100644 index bcc5609fc..000000000 --- a/src/strategies/synthetix-non-quadratic_2/index.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { getAddress } from '@ethersproject/address'; -import { BigNumber } from '@ethersproject/bignumber'; -import { Contract } from '@ethersproject/contracts'; -import { Provider } from '@ethersproject/providers'; -import { subgraphRequest } from '../../utils'; -import { - DebtCacheABI, - debtL1, - debtL2, - returnGraphParams, - SNXHoldersResult, - SynthetixStateABI -} from '../synthetix/helper'; - -export const author = 'andytcf'; -export const version = '1.0.0'; - -const MED_PRECISE_UNIT = 1e18; - -// @TODO: check if most-up-to-date version (using https://contracts.synthetix.io/SynthetixState) -const SynthetixStateContractAddress = - '0x4b9Ca5607f1fF8019c1C6A3c2f0CC8de622D5B82'; -// @TODO: check if most-up-to-date version (using http://contracts.synthetix.io/DebtCache) -const DebtCacheContractAddress = '0x1620Aa736939597891C1940CF0d28b82566F9390'; - -const defaultGraphs = { - '1': 'https://api.thegraph.com/subgraphs/name/synthetixio-team/synthetix', - '10': 'https://api.thegraph.com/subgraphs/name/synthetixio-team/optimism-main' -}; - -const loadLastDebtLedgerEntry = async ( - provider: Provider, - snapshot: number | string -) => { - const contract = new Contract( - SynthetixStateContractAddress, - SynthetixStateABI, - provider - ); - - const lastDebtLedgerEntry = await contract.lastDebtLedgerEntry({ - blockTag: snapshot - }); - - return BigNumber.from(lastDebtLedgerEntry); -}; - -const loadL1TotalDebt = async ( - provider: Provider, - snapshot: number | string -) => { - const contract = new Contract( - DebtCacheContractAddress, - DebtCacheABI, - provider - ); - - const currentDebtObject = await contract.currentDebt({ - blockTag: snapshot - }); - - return Number(currentDebtObject.debt) / MED_PRECISE_UNIT; -}; - -export async function strategy( - _space, - _network, - _provider, - _addresses, - _options, - snapshot -) { - const score = {}; - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - /* Global Constants */ - - const totalL1Debt = await loadL1TotalDebt(_provider, snapshot); // (high-precision 1e18) - const lastDebtLedgerEntry = await loadLastDebtLedgerEntry( - _provider, - snapshot - ); - - /* EDIT THESE FOR OVM */ - - // @TODO update the currentDebt for the snapshot from (https://contracts.synthetix.io/ovm/DebtCache) - // const totalL2Debt = 40201854; - const totalL2Debt = _options.totalL2Debt; - // @TODO update the lastDebtLedgerEntry from (https://contracts.synthetix.io/ovm/SynthetixState) - // const lastDebtLedgerEntryL2 = 10909822163955610660349890; - const lastDebtLedgerEntryL2 = _options.lastDebtLedgerEntryL2; - // @TODO update the comparison between OVM:ETH c-ratios at the time of snapshot - const normalisedL2CRatio = 500 / 400; - // @TODO update the L2 block number to use - // const L2BlockNumber = 4750287; - const L2BlockNumber = _options.L2BlockNumber; - - const scaledTotalL2Debt = totalL2Debt * normalisedL2CRatio; - - /* --------------- */ - - /* Using the subgraph, we get the relevant L1 calculations */ - - const l1Results = (await subgraphRequest( - defaultGraphs[1], - returnGraphParams(blockTag, _addresses) - )) as SNXHoldersResult; - - if (l1Results && l1Results.snxholders) { - for (let i = 0; i < l1Results.snxholders.length; i++) { - const holder = l1Results.snxholders[i]; - const vote = await debtL1( - holder.initialDebtOwnership, - holder.debtEntryAtIndex, - totalL1Debt, - scaledTotalL2Debt, - lastDebtLedgerEntry, - false - ); - score[getAddress(holder.id)] = vote; - } - } - - /* Using the subgraph, we get the relevant L2 calculations */ - - const l2Results = (await subgraphRequest( - defaultGraphs[10], - returnGraphParams(L2BlockNumber, _addresses) - )) as SNXHoldersResult; - - if (l2Results && l2Results.snxholders) { - for (let i = 0; i < l2Results.snxholders.length; i++) { - const holder = l2Results.snxholders[i]; - - const vote = await debtL2( - holder.initialDebtOwnership, - holder.debtEntryAtIndex, - totalL1Debt, - scaledTotalL2Debt, - lastDebtLedgerEntryL2, - false - ); - - if (score[getAddress(holder.id)]) { - score[getAddress(holder.id)] += vote; - } else { - score[getAddress(holder.id)] = vote; - } - } - } - - return score || {}; -} diff --git a/src/strategies/synthetix-quadratic_2/README.md b/src/strategies/synthetix-quadratic_2/README.md deleted file mode 100644 index d80b9dd4b..000000000 --- a/src/strategies/synthetix-quadratic_2/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Quadratic Debt Percentage Strategy - -Calculates the quadratic weighting of voters, based on their debt percentage in the previous fee period. - -## Examples - -Can be used instead of the erc20-balance-of strategy, the space config will look like this: - -```JSON -{ - "strategies": [ - ["synthetix-quadratic_2"] - ] -} -``` diff --git a/src/strategies/synthetix-quadratic_2/examples.json b/src/strategies/synthetix-quadratic_2/examples.json deleted file mode 100644 index ea890a6cc..000000000 --- a/src/strategies/synthetix-quadratic_2/examples.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "synthetix-quadratic_2", - "params": { - "address": "0x023c66b7e13d30a3c46aa433fd2829763d5817c5", - "symbol": "WD", - "totalL2Debt": 40201854, - "lastDebtLedgerEntryL2": 10909822163955610660349890, - "L2BlockNumber": 4750287 - } - }, - "network": "1", - "addresses": [ - "0x78b037B39704e88a82DD23CFBE1f57f6AeF8EBC5", - "0x0bc3668d2AaFa53eD5E5134bA13ec74ea195D000", - "0xcAc59F91E4536Bc0E79aB816a5cD54e89f10433C", - "0x6dc88B231Cd04Dd1b1e525161162993F47140006", - "0x935D2fD458fdf41B6F7B62471f593797866a3Ce6", - "0x24e445fe7708Bf4bC2ae8d4df1694C98Af8BDE4F", - "0x49be88f0fcc3a8393a59d3688480d7d253c37d2a", - "0x27Cc4d6bc95b55a3a981BF1F1c7261CDa7bB0931" - ], - "snapshot": 14443440 - } -] diff --git a/src/strategies/synthetix-quadratic_2/index.ts b/src/strategies/synthetix-quadratic_2/index.ts deleted file mode 100644 index 8f030c1ac..000000000 --- a/src/strategies/synthetix-quadratic_2/index.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { getAddress } from '@ethersproject/address'; -import { BigNumber } from '@ethersproject/bignumber'; -import { Contract } from '@ethersproject/contracts'; -import { Provider } from '@ethersproject/providers'; -import { subgraphRequest } from '../../utils'; -import { - DebtCacheABI, - debtL1, - debtL2, - returnGraphParams, - SNXHoldersResult, - SynthetixStateABI -} from '../synthetix/helper'; - -export const author = 'andytcf'; -export const version = '1.0.0'; - -const MED_PRECISE_UNIT = 1e18; - -// @TODO: check if most-up-to-date version (using https://contracts.synthetix.io/SynthetixState) -const SynthetixStateContractAddress = - '0x4b9Ca5607f1fF8019c1C6A3c2f0CC8de622D5B82'; -// @TODO: check if most-up-to-date version (using http://contracts.synthetix.io/DebtCache) -const DebtCacheContractAddress = '0x1620Aa736939597891C1940CF0d28b82566F9390'; - -const defaultGraphs = { - '1': 'https://api.thegraph.com/subgraphs/name/synthetixio-team/synthetix', - '10': 'https://api.thegraph.com/subgraphs/name/synthetixio-team/optimism-main' -}; - -const loadLastDebtLedgerEntry = async ( - provider: Provider, - snapshot: number | string -) => { - const contract = new Contract( - SynthetixStateContractAddress, - SynthetixStateABI, - provider - ); - - const lastDebtLedgerEntry = await contract.lastDebtLedgerEntry({ - blockTag: snapshot - }); - - return BigNumber.from(lastDebtLedgerEntry); -}; - -const loadL1TotalDebt = async ( - provider: Provider, - snapshot: number | string -) => { - const contract = new Contract( - DebtCacheContractAddress, - DebtCacheABI, - provider - ); - - const currentDebtObject = await contract.currentDebt({ - blockTag: snapshot - }); - - return Number(currentDebtObject.debt) / MED_PRECISE_UNIT; -}; - -export async function strategy( - _space, - _network, - _provider, - _addresses, - _options, - snapshot -) { - const score = {}; - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - /* Global Constants */ - - const totalL1Debt = await loadL1TotalDebt(_provider, snapshot); // (high-precision 1e18) - const lastDebtLedgerEntry = await loadLastDebtLedgerEntry( - _provider, - snapshot - ); - - // @TODO update the currentDebt for the snapshot from (https://contracts.synthetix.io/ovm/DebtCache) - // const totalL2Debt = 40201854; - const totalL2Debt = _options.totalL2Debt; - // @TODO update the lastDebtLedgerEntry from (https://contracts.synthetix.io/ovm/SynthetixState) - // const lastDebtLedgerEntryL2 = 10909822163955610660349890; - const lastDebtLedgerEntryL2 = _options.lastDebtLedgerEntryL2; - // @TODO update the comparison between OVM:ETH c-ratios at the time of snapshot - const normalisedL2CRatio = 500 / 400; - // @TODO update the L2 block number to use - // const L2BlockNumber = 4750287; - const L2BlockNumber = _options.L2BlockNumber; - - const scaledTotalL2Debt = totalL2Debt * normalisedL2CRatio; - - /* --------------- */ - - /* Using the subgraph, we get the relevant L1 calculations */ - - const l1Results = (await subgraphRequest( - defaultGraphs[1], - returnGraphParams(blockTag, _addresses) - )) as SNXHoldersResult; - - if (l1Results && l1Results.snxholders) { - for (let i = 0; i < l1Results.snxholders.length; i++) { - const holder = l1Results.snxholders[i]; - const vote = await debtL1( - holder.initialDebtOwnership, - holder.debtEntryAtIndex, - totalL1Debt, - scaledTotalL2Debt, - lastDebtLedgerEntry, - true - ); - score[getAddress(holder.id)] = vote; - } - } - - /* Using the subgraph, we get the relevant L2 calculations */ - - const l2Results = (await subgraphRequest( - defaultGraphs[10], - returnGraphParams(L2BlockNumber, _addresses) - )) as SNXHoldersResult; - - if (l2Results && l2Results.snxholders) { - for (let i = 0; i < l2Results.snxholders.length; i++) { - const holder = l2Results.snxholders[i]; - - const vote = await debtL2( - holder.initialDebtOwnership, - holder.debtEntryAtIndex, - totalL1Debt, - scaledTotalL2Debt, - lastDebtLedgerEntryL2, - true - ); - - if (score[getAddress(holder.id)]) { - score[getAddress(holder.id)] += vote; - } else { - score[getAddress(holder.id)] = vote; - } - } - } - - return score || {}; -} diff --git a/src/strategies/thales/examples.json b/src/strategies/thales/examples.json index 5e0375bac..0a4f41e03 100644 --- a/src/strategies/thales/examples.json +++ b/src/strategies/thales/examples.json @@ -5,15 +5,13 @@ "name": "thales", "params": { "symbol": "THALES", - "decimals": 18, - "blockOptimism": 88627878, - "blockArbitrum": 78979794 + "decimals": 18 } }, "network": "10", "addresses": [ "0x9f8e4ee788D9b00A3409584E18034aA7B736C396", - "0x0bc3668d2AaFa53eD5E5134bA13ec74ea195D000", + "0x0D858351A5FB419C9A3760647900d2F7aD526c83", "0xcAc59F91E4536Bc0E79aB816a5cD54e89f10433C", "0x6dc88B231Cd04Dd1b1e525161162993F47140006", "0x935D2fD458fdf41B6F7B62471f593797866a3Ce6", @@ -21,6 +19,6 @@ "0x49be88f0fcc3a8393a59d3688480d7d253c37d2a", "0x27Cc4d6bc95b55a3a981BF1F1c7261CDa7bB0931" ], - "snapshot": 5003555 + "snapshot": 111660236 } ] diff --git a/src/strategies/thales/index.ts b/src/strategies/thales/index.ts index cbb2bcc15..05ef5a460 100644 --- a/src/strategies/thales/index.ts +++ b/src/strategies/thales/index.ts @@ -1,19 +1,26 @@ import { getAddress } from '@ethersproject/address'; import { formatUnits } from '@ethersproject/units'; -import { subgraphRequest } from '../../utils'; +import { getSnapshots, subgraphRequest } from '../../utils'; export const author = 'vpoklopic'; -export const version = '1.0.3'; +export const version = '1.0.4'; + +enum NetworkId { + Optimism = 10, + Arbitrum = 42161, + Base = 8453 +} const THALES_SUBGRAPH_URL = { optimism: 'https://api.thegraph.com/subgraphs/name/thales-markets/thales-token', arbitrum: - 'https://api.thegraph.com/subgraphs/name/thales-markets/thales-token-arbitrum' + 'https://api.thegraph.com/subgraphs/name/thales-markets/thales-token-arbitrum', + base: 'https://api.studio.thegraph.com/query/11948/thales-token-base/version/latest' }; -function returnGraphParams(addresses: string[]) { - return { +function returnGraphParams(addresses: string[], block: string | number) { + const graphParams = { stakers: { __args: { first: 1000, @@ -29,6 +36,15 @@ function returnGraphParams(addresses: string[]) { totalStakedAmount: true } }; + + if (block !== 'latest') { + // @ts-ignore + graphParams.stakers.__args.block = { + number: block + }; + } + + return graphParams; } export async function strategy( @@ -36,29 +52,31 @@ export async function strategy( _network, _provider, addresses, - options + options, + snapshot ) { - const optimismGraphParams = returnGraphParams(addresses); - if (options.blockOptimism !== undefined) { - // @ts-ignore - optimismGraphParams.stakers.__args.block = { - number: options.blockOptimism - }; - } + const blocks = await getSnapshots(_network, snapshot, _provider, [ + NetworkId.Optimism, + NetworkId.Arbitrum, + NetworkId.Base + ]); - const arbitrumGraphParams = returnGraphParams(addresses); - if (options.blockArbitrum !== undefined) { - // @ts-ignore - arbitrumGraphParams.stakers.__args.block = { - number: options.blockArbitrum - }; - } + const optimismGraphParams = returnGraphParams( + addresses, + blocks[NetworkId.Optimism] + ); + const arbitrumGraphParams = returnGraphParams( + addresses, + blocks[NetworkId.Arbitrum] + ); + const baseGraphParams = returnGraphParams(addresses, blocks[NetworkId.Base]); const score = {}; - const [optimismStakers, arbitrumStakers] = await Promise.all([ + const [optimismStakers, arbitrumStakers, baseStakers] = await Promise.all([ subgraphRequest(THALES_SUBGRAPH_URL.optimism, optimismGraphParams), - subgraphRequest(THALES_SUBGRAPH_URL.arbitrum, arbitrumGraphParams) + subgraphRequest(THALES_SUBGRAPH_URL.arbitrum, arbitrumGraphParams), + subgraphRequest(THALES_SUBGRAPH_URL.base, baseGraphParams) ]); // We are starting by mapping all Optimism stakers @@ -86,5 +104,21 @@ export async function strategy( }); } + // If the Optimism or Arbitrum staker is also staker on Base, add an amount + // Otherwise, just set Base staked amount as a score + if (baseStakers && baseStakers.stakers) { + baseStakers.stakers.forEach((staker) => { + const key = getAddress(staker.id); + const stakedAmount = parseFloat( + formatUnits(staker.totalStakedAmount, options.decimals) + ); + if (!!score[key]) { + score[key] += stakedAmount; + } else { + score[key] = stakedAmount; + } + }); + } + return score || {}; } diff --git a/src/strategies/tomyumswap/README.md b/src/strategies/tomyumswap/README.md deleted file mode 100644 index 132af0c87..000000000 --- a/src/strategies/tomyumswap/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Contract Call Strategy - -Fetches [TOMYUM](https://bscscan.com/address/0xa8777a7855cc5e5d4994d890eaad369050a9ff47) balance from the following sources: - -- Wallet -- TOMYUM-BNB LP Farm -- TOMYUM Pool -- TOMYUM Vault -- Pools that were active at the time of the snapshot diff --git a/src/strategies/tomyumswap/examples.json b/src/strategies/tomyumswap/examples.json deleted file mode 100644 index c9bce8660..000000000 --- a/src/strategies/tomyumswap/examples.json +++ /dev/null @@ -1,17 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "tomyumswap", - "params": { - "symbol": "TOMYUM" - } - }, - "network": "56", - "addresses": [ - "0xF677b8EF72C34f63c43f47C30612B1A3Ec1b622F", - "0xd7eAd7DD37EFf97531beA958a58282Fa6D3a31A5" - ], - "snapshot": 7151302 - } -] diff --git a/src/strategies/tomyumswap/index.ts b/src/strategies/tomyumswap/index.ts deleted file mode 100644 index 3d77ee008..000000000 --- a/src/strategies/tomyumswap/index.ts +++ /dev/null @@ -1,132 +0,0 @@ -import fetch from 'cross-fetch'; -import { subgraphRequest } from '../../utils'; - -export const author = 'tomyumswap'; -export const version = '0.0.1'; - -type VotingResponse = { - verificationHash: string; - block: number; - tomYumBalance: string; - tomYumVaultBalance: string; - tomYumPoolBalance: string; - tomYumBnbLpBalance: string; - poolsBalance: string; - total: string; -}; - -const MINIMUM_VOTING_POWER = 0.01; -const SMART_CHEF_URL = - 'https://api.thegraph.com/subgraphs/name/tomyumswap/smartchef'; -const VOTING_API_URL = 'http://voting-api.tomyumswap.com/api/'; - -/** - * Fetches voting power of one address - */ -// const fetchVotingPower = async ( -// address: string, -// block: number, -// poolAddresses: string[] -// ): Promise => { -// const response = await fetch(`${VOTING_API_URL}power`, { -// method: 'POST', -// headers: { -// 'Content-Type': 'application/json' -// }, -// body: JSON.stringify({ -// block, -// address, -// poolAddresses -// }) -// }); - -// const payload = await response.json(); -// return payload.data; -// }; - -/** - * Fetches voting power of multiple addresses - */ -const fetchVotingPowerMultiple = async ( - addresses: string[], - block: number, - poolAddresses: string[] -): Promise => { - const response = await fetch(`${VOTING_API_URL}powerV2`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - block, - addresses, - poolAddresses - }) - }); - - const payload = await response.json(); - - return payload.data; -}; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = - typeof snapshot === 'number' ? snapshot : await provider.getBlockNumber(); - - const params = { - smartChefs: { - __args: { - where: { - startBlock_lte: blockTag, - endBlock_gte: blockTag - }, - first: 1000, - orderBy: 'block', - orderDirection: 'desc' - }, - id: true, - startBlock: true, - endBlock: true - } - }; - - const results = await subgraphRequest(SMART_CHEF_URL, params); - - if (!results) { - return; - } - - try { - const poolAddresses = results.smartChefs.map((pool) => pool.id); - const votingPowerResult = await fetchVotingPowerMultiple( - addresses, - blockTag, - poolAddresses - ); - - const calculatedPower = votingPowerResult.reduce( - (accum, response, index) => { - const address = addresses[index]; - const total = parseFloat(response.total); - - return { - ...accum[index], - [address]: - total <= MINIMUM_VOTING_POWER ? MINIMUM_VOTING_POWER : total - }; - }, - {} - ); - - return calculatedPower; - } catch { - return []; - } -} diff --git a/src/strategies/uma-voting/README.md b/src/strategies/uma-voting/README.md deleted file mode 100644 index 45cd408a6..000000000 --- a/src/strategies/uma-voting/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# uma-voting strategy - -This strategy returns the UMA balance of the voting wallet for the hot wallet. - -Here is an example of parameters: - -## Example - -```JSON -[ - { - "name": "Example query", - "strategy": { - "name": "uma-voting", - "params": { - "address": "0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828", - "votingFactoryAddress": "0xDE7c02aD2b925587Bd16724810f994a2948c4a38", - "symbol": "UMA", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x6888ff4be3262f3232e1c022de731680a16ddda7", - "0x66b5409f52d23ef87bdbfaa7312f8d790755fe13", - "0xedd049c56b804bcdbf7e704cc312491e3d50485a", - "0x18a31db2ecc0c27ee445266337b144f60810c228", - "0xacd3dc5c6f9cb967b5e1f719001e85039b0c976f", - "0x3d88456277e32d62575267c3564b72387c3f21f7", - "0xdf815836fe1457f7e2c0c3d2348e94190a37c687", - "0x515978b57fc91b6a845dc64574b151c7ed3bc2a7", - "0x8a6800c753ed8ed19e0df473a97a7a94b2cecf54", - "0x1fdef3d3c7984a945008c253caa6cf380fddb16f", - "0x82b6a4ebc3904b7aabd4e85510045eff0d43a6bd", - "0x50e453636f153ded412eca1ecc3e520a206b3496", - "0x336b9de28d67d692c31df93a9b8630b2fa50d88f", - "0x2cf6988d57d0df95d3ff37cdfbfe0520f15c860f", - "0x644a7ff3cdff23c3f10f25807a40fa48aff34885", - "0xfd8355b24ab8815ca2d76d1e495dc29b4330241f", - "0xc00667d8b00f35b3565a5c4458dff1cd718e3527" - ], - "snapshot": 14515308 - } -] - -``` diff --git a/src/strategies/uma-voting/examples.json b/src/strategies/uma-voting/examples.json deleted file mode 100644 index 02bdafc92..000000000 --- a/src/strategies/uma-voting/examples.json +++ /dev/null @@ -1,56 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "uma-voting", - "params": { - "address": "0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828", - "votingFactoryAddress": "0xDE7c02aD2b925587Bd16724810f994a2948c4a38", - "symbol": "UMA", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d", - "0x691c39fdd4f89db2d885324a2b5cef00fbf8caa3", - "0xfb9e5f5874565a6ef8fcf1debc4975501331a8ca", - "0x075ad09e2b27be785c863d756096d0aebcf13118", - "0x644a7ff3cdff23c3f10f25807a40fa48aff34885", - "0xfd8355b24ab8815ca2d76d1e495dc29b4330241f", - "0x1398fc0793d8b67744bfde4a4ff338c69bba753b", - "0xc3d26aa4954816330c5ffc9bb2d46acbceaff130", - "0x9e3a4ed4eea5c27a5e82a32e90482a4827a4ae80", - "0xdcf99ba641c98e37df8ea31188ad7e7fa5501b09", - "0x3b271e8f1409e8e90c484d3591143bd7e1d2a177", - "0x112eae3093451187c8b945fb601c6df34bdc8105", - "0xfdf7f859807d1dc73873640759b2706822802529", - "0x4b070c428ddbb49524abf2d4be4998c1e3b6f3a5", - "0x8d4f93e9962f3392e4c9d10c08f2f1c757f95866", - "0x49e0cdd718d330e5a27856256236b4e56a76d3fb", - "0xabbcf7b0735e4eb152fd13fa02691cc046fac0bd", - "0xc00667d8b00f35b3565a5c4458dff1cd718e3527", - "0x11efc2638184a0382bf1bb29c4047c6111f8f3c7", - "0x7cac2ab1cf9dc459ca88a85d72ac94de2691f1e5", - "0xb0903a62f5ca7a215aa67232ba691cb801decd5b", - "0xbca0fd352dce62fa1b7c7f9fa8f0c1de7fef09d9", - "0x6888ff4be3262f3232e1c022de731680a16ddda7", - "0x66b5409f52d23ef87bdbfaa7312f8d790755fe13", - "0xedd049c56b804bcdbf7e704cc312491e3d50485a", - "0x18a31db2ecc0c27ee445266337b144f60810c228", - "0xacd3dc5c6f9cb967b5e1f719001e85039b0c976f", - "0x718648c8c531f91b528a7757dd2be813c3940608", - "0x3d88456277e32d62575267c3564b72387c3f21f7", - "0xdf815836fe1457f7e2c0c3d2348e94190a37c687", - "0x515978b57fc91b6a845dc64574b151c7ed3bc2a7", - "0x8a6800c753ed8ed19e0df473a97a7a94b2cecf54", - "0x1fdef3d3c7984a945008c253caa6cf380fddb16f", - "0x82b6a4ebc3904b7aabd4e85510045eff0d43a6bd", - "0x50e453636f153ded412eca1ecc3e520a206b3496", - "0x969397c71eb51f7fdcf6527db4d97871b05d6f63", - "0x336b9de28d67d692c31df93a9b8630b2fa50d88f", - "0x2cf6988d57d0df95d3ff37cdfbfe0520f15c860f" - ], - "snapshot": 14515308 - } -] diff --git a/src/strategies/uma-voting/index.ts b/src/strategies/uma-voting/index.ts deleted file mode 100644 index c3bf58280..000000000 --- a/src/strategies/uma-voting/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall } from '../../utils'; - -export const author = 'abg4'; -export const version = '0.1.0'; - -const abi = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function designatedVotingContracts(address) view returns (address)' -]; - -function getArgs(options, address: string) { - const args: Array = options.args || ['%{address}']; - return args.map((arg) => - typeof arg === 'string' ? arg.replace(/%{address}/g, address) : arg - ); -} - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const votingAddress = await multicall( - network, - provider, - abi, - addresses.map((address: any) => [ - options.votingFactoryAddress, - 'designatedVotingContracts', - getArgs(options, address) - ]), - { blockTag } - ); - - const response = await multicall( - network, - provider, - abi, - votingAddress.map((address: any) => [ - options.address, - 'balanceOf', - getArgs(options, address) - ]), - { blockTag } - ); - - return Object.fromEntries( - response.map((value, i) => [ - addresses[i], - parseFloat( - formatUnits( - options?.output ? value[options.output].toString() : value.toString(), - options.decimals - ) - ) - ]) - ); -} diff --git a/src/strategies/vault-token-lp-balance/README.md b/src/strategies/vault-token-lp-balance/README.md deleted file mode 100644 index b895c988c..000000000 --- a/src/strategies/vault-token-lp-balance/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# vault-token-lp-balance - -This strategy computes the amount of a token -in a vault lp pool. The vault must use the function `stakedWantTokens(pid, user)`. - -You must supply the token, vault address, lp address, and pid of the pool -in the vault chef. Here is an example of the params: - -```json -{ - "tokenAddress": "0x0159ED2E06DDCD46a25E74eb8e159Ce666B28687", - "tokenDecimals": 18, - "vaultChefAddress": "0x2914646E782Cc36297c6639734892927B3b6Fe56", - "pid": 8, - "lpAddress": "0xE2E34C07754C4CAb2b6D585C06D418628f8ba553" -} -``` diff --git a/src/strategies/vault-token-lp-balance/examples.json b/src/strategies/vault-token-lp-balance/examples.json deleted file mode 100644 index dc3be1918..000000000 --- a/src/strategies/vault-token-lp-balance/examples.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "name": "Vault Token in LP balance", - "strategy": { - "name": "vault-token-lp-balance", - "params": { - "tokenAddress": "0x0159ED2E06DDCD46a25E74eb8e159Ce666B28687", - "symbol": "FOX", - "tokenDecimals": 18, - "vaultChefAddress": "0x2914646E782Cc36297c6639734892927B3b6Fe56", - "pid": 8, - "lpAddress": "0xE2E34C07754C4CAb2b6D585C06D418628f8ba553" - } - }, - "network": "1666600000", - "addresses": [ - "0xD20B976584bF506BAf5cC604D1f0A1B8D07138dA", - "0x4ff9B7C1424b9E4375BbbDF3357a318412c02E0c", - "0x57B7713c0E013cfbEC0E4C6c8B264dAf7598ebA9", - "0xBfA58c2516cB64C7f6E19c0B3690158C38E4d0f7" - ], - "snapshot": 18263021 - } -] diff --git a/src/strategies/vault-token-lp-balance/index.ts b/src/strategies/vault-token-lp-balance/index.ts deleted file mode 100644 index 870b6b3f3..000000000 --- a/src/strategies/vault-token-lp-balance/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { multicall, Multicaller } from '../../utils'; - -export const author = 'foxthefarmer'; -export const version = '0.0.1'; - -const vaultAbi = [ - 'function poolInfo(uint256) returns (address want,uint256 allocPoint,uint256 lastRewardBlock,uint256 accAQUAPerShare,address strat)', - 'function stakedWantTokens(uint256 _pid, address _user) returns (uint256)' -]; - -const bep20Abi: any = [ - 'function totalSupply() view returns (uint256)', - 'function balanceOf(address) view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const erc20Multi = new Multicaller(network, provider, bep20Abi, { - blockTag - }); - erc20Multi.call('lpTotalSupply', options.lpAddress, 'totalSupply'); - erc20Multi.call('lpTokenBalance', options.tokenAddress, 'balanceOf', [ - options.lpAddress - ]); - - const erc20Result = await erc20Multi.execute(); - - const tokenPerLp = - parseFloat(erc20Result.lpTokenBalance.toString()) / - parseFloat(erc20Result.lpTotalSupply.toString()); - - const userVaultLpBalanceCalls = multicall( - network, - provider, - vaultAbi, - addresses.map((address: any) => [ - options.vaultChefAddress, - 'stakedWantTokens', - [options.pid, address] - ]), - { blockTag } - ); - - const vaultBalances = await Promise.all([userVaultLpBalanceCalls]); - - return Object.fromEntries( - Object.entries(addresses).map((address: any, index) => [ - address[1], - parseFloat(formatUnits(vaultBalances[0][index].toString(), 18)) * - tokenPerLp - ]) - ); -} diff --git a/src/strategies/vendor-v2-borrower-collateral-balance-of/README.md b/src/strategies/vendor-v2-borrower-collateral-balance-of/README.md new file mode 100644 index 000000000..db56218c8 --- /dev/null +++ b/src/strategies/vendor-v2-borrower-collateral-balance-of/README.md @@ -0,0 +1,13 @@ +# vendor-v2-borrower-collateral-balance-of + +This returns the voting power of a borrower locked in a vendor lending pool. + +Here is an example of parameters: + +```json +{ + "address": "0xA4B49b1A717E9e002104E2B4517A8B7086DF479b", + "collateralDecimals": 9, + "weight": 5 +} +``` diff --git a/src/strategies/vendor-v2-borrower-collateral-balance-of/examples.json b/src/strategies/vendor-v2-borrower-collateral-balance-of/examples.json new file mode 100644 index 000000000..23d38f869 --- /dev/null +++ b/src/strategies/vendor-v2-borrower-collateral-balance-of/examples.json @@ -0,0 +1,21 @@ +[ + { + "name": "Example query", + "strategy": { + "name": "vendor-v2-borrower-collateral-balance-of", + "params": { + "address": "0xA4B49b1A717E9e002104E2B4517A8B7086DF479b", + "collateralDecimals": 9, + "weight": 5 + } + }, + "network": "42161", + "addresses": [ + "0xeFAD4c712CBa7F7a136C6B3C6f861fb787a348a0", + "0xc12DE812ae612B6d514b52d529F97f6Acb524c8E", + "0x18252F28234C010Cf3353A82f3cBe71DB1B74773", + "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1" + ], + "snapshot": 170740021 + } +] diff --git a/src/strategies/vendor-v2-borrower-collateral-balance-of/index.ts b/src/strategies/vendor-v2-borrower-collateral-balance-of/index.ts new file mode 100644 index 000000000..df9d2fb6f --- /dev/null +++ b/src/strategies/vendor-v2-borrower-collateral-balance-of/index.ts @@ -0,0 +1,89 @@ +import { BigNumberish } from '@ethersproject/bignumber'; +import { formatUnits } from '@ethersproject/units'; +import { AbiCoder } from '@ethersproject/abi'; +import { Multicaller } from '../../utils'; +import { BlockTag, StaticJsonRpcProvider } from '@ethersproject/providers'; + +export const author = '0xdapper'; +export const version = '0.1.0'; + +const abi = [ + 'function debts(address borrower) external view returns (uint256, uint256)', + // (poolType, owner, expiry, colToken, protocolFee, lendToken, ltv, pauseTime, lendRatio, feeRatesAndType) + 'function getPoolSettings() external view returns (uint8, address, uint48, address, uint48, address, uint48, uint48, uint256, address[], bytes32)' +]; + +const decodePoolSettings = (poolSettings: string) => { + const abiCoder = new AbiCoder(); + const [ + [ + poolType, + owner, + expiry, + colToken, + protocolFee, + lendToken, + ltv, + pauseTime, + lendRatio, + allowList, + feeRatesAndType + ] + ] = abiCoder.decode( + [ + '(uint8, address, uint48, address, uint48, address, uint48, uint48, uint256, address[], bytes32)' + ], + poolSettings + ); + return { + poolType, + owner, + expiry, + colToken, + protocolFee, + lendToken, + ltv, + pauseTime, + lendRatio, + feeRatesAndType, + allowList + }; +}; + +export async function strategy( + space, + network, + provider: StaticJsonRpcProvider, + addresses, + options, + snapshot +): Promise> { + const blockTag: BlockTag = typeof snapshot === 'number' ? snapshot : 'latest'; + const blockTime = (await provider.getBlock(blockTag)).timestamp; + const poolSettings = decodePoolSettings( + await provider.call( + { + to: options.address, + data: '0xe4a0ce2f' + }, + blockTag + ) + ); + const hasExpired = poolSettings.expiry < blockTime; + + const multi = new Multicaller(network, provider, abi, { blockTag }); + addresses.forEach((address) => + multi.call(address, options.address, 'debts', [address]) + ); + const result: Record = + await multi.execute(); + const multiplier = hasExpired ? 0 : options.weight || 1; + + return Object.fromEntries( + Object.entries(result).map(([address, [, collAmount]]) => [ + address, + parseFloat(formatUnits(collAmount, options.collateralDecimals)) * + multiplier + ]) + ); +} diff --git a/src/strategies/proxyprotocol-erc20-balance-of/schema.json b/src/strategies/vendor-v2-borrower-collateral-balance-of/schema.json similarity index 68% rename from src/strategies/proxyprotocol-erc20-balance-of/schema.json rename to src/strategies/vendor-v2-borrower-collateral-balance-of/schema.json index 2113da8b8..3b978f04b 100644 --- a/src/strategies/proxyprotocol-erc20-balance-of/schema.json +++ b/src/strategies/vendor-v2-borrower-collateral-balance-of/schema.json @@ -6,12 +6,6 @@ "title": "Strategy", "type": "object", "properties": { - "symbol": { - "type": "string", - "title": "Symbol", - "examples": ["e.g. UNI"], - "maxLength": 16 - }, "address": { "type": "string", "title": "Contract address", @@ -20,13 +14,18 @@ "minLength": 42, "maxLength": 42 }, - "decimals": { + "collateralDecimals": { + "type": "number", + "examples": [18], + "title": "Decimals" + }, + "weight": { "type": "number", - "title": "Decimals", - "examples": ["e.g. 18"] + "title": "Weight", + "examples": [0.5, 2] } }, - "required": ["address", "decimals"], + "required": ["address", "collateralDecimals"], "additionalProperties": false } } diff --git a/src/strategies/vesper/examples.json b/src/strategies/vesper/examples.json index 27a266598..951d1d500 100644 --- a/src/strategies/vesper/examples.json +++ b/src/strategies/vesper/examples.json @@ -4,21 +4,21 @@ "strategy": { "name": "vesper", "params": { - "token": "0x1b40183efb4dd766f11bda7a7c3ad8982e998421", + "token": "0x1b40183EFB4Dd766f11bDa7A7c3AD8982e998421", "symbol": "VSP", - "votingPower": "0xEbedFD259c9FB1F5c0ab9A9f24E79F8d80E29B23", + "votingPower": "0xD744320abd3bD4445Dc3C90C889391bD454D5B30", "decimals": 18, "blocksPerPeriod": 40320, - "minBlock": 13545210 + "minBlock": 18569909 } }, "network": "1", "addresses": [ - "0xb92792552e590339a7dbf1e0d6114fbc7395c86b", - "0xf4087b7ab24bde9c445ddd0bc4df257f81277214", - "0x8b01d375e274213c860ef6ac013dbdd5286cd816", - "0xdbc13e67f678cc00591920cece4dca6322a79ac7" + "0xb92792552e590339A7DbF1E0D6114fbc7395c86b", + "0xf4087b7AB24Bde9c445ddD0bc4DF257F81277214", + "0x8b01d375e274213c860eF6ac013DBDd5286CD816", + "0xDBC13E67F678Cc00591920ceCe4dCa6322a79AC7" ], - "snapshot": 13545210 + "snapshot": 18569909 } ] diff --git a/src/strategies/volt-voting-power/README.md b/src/strategies/volt-voting-power/README.md index ff14e35d9..2e825dee0 100644 --- a/src/strategies/volt-voting-power/README.md +++ b/src/strategies/volt-voting-power/README.md @@ -1,5 +1,5 @@ # Voting shares of VOLT token stakers and holders - + This strategy computes users shares of volt token (erc-20) in an lp pool. The strategy calculates user votes using this formula : user's volt balance + staked volt balance + User LP share mapped volt balance @@ -11,9 +11,9 @@ You must supply the voltwap token address, symbol, decimals, network id, swap su "symbol": "VOLT", "tokenDecimals": 18, "lpTokenAddress": "0x1071392e4cdf7c01d433b87be92beb1f8fd663a8", - "voltAddress":"0x8df95e66cb0ef38f91d2776da3c921768982fba0", - "network":"82", - "swapSubgraph":"https://graph-meter.voltswap.finance/subgraphs/name/meterio/uniswap-v2-subgraph", - "stakingSubgraph":"https://graph-meter.voltswap.finance/subgraphs/name/meter/geyser-v2" + "voltAddress": "0x8df95e66cb0ef38f91d2776da3c921768982fba0", + "network": "82", + "swapSubgraph": "https://graph-meter.voltswap.finance/subgraphs/name/meterio/uniswap-v2-subgraph", + "stakingSubgraph": "https://graph-meter.voltswap.finance/subgraphs/name/meter/geyser-v2" } ``` diff --git a/src/strategies/vsta-pool-staking/README.md b/src/strategies/vsta-pool-staking/README.md deleted file mode 100644 index 189242083..000000000 --- a/src/strategies/vsta-pool-staking/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# vsta-pool-staking - -This strategy returns voters underlying VSTA token balance for lping and staking to our reward farms - -## Params - -- `symbol` - (**Optional**, `string`) Symbol of ERC20 token -- `decimals` - (**Required**, `number`) Decimal precision for ERC20 token -- `balancerVaultAddress` - (**Required**, `string`) Address of Balancer Vault to get token balances of LPs -- `poolAddress` - (**Required**, `string`) Address of VSTA-TOKEN lp token -- `poolId` - (**Required**, `string`) Balancer pool ID of VSTA-TOKEN lp -- `farmAddress` - (**Required**, `string`) Vesta farm address -- `vstaAddress` - (**Required**, `string`) Vesta token address - -Here is an example of parameters: - -```json -{ - "balancerVaultAddress": "0xBA12222222228d8Ba445958a75a0704d566BF2C8", - "poolAddress": "0xC61ff48f94D801c1ceFaCE0289085197B5ec44F0", - "poolId": "0xc61ff48f94d801c1ceface0289085197b5ec44f000020000000000000000004d", - "farmAddress": "0x65207da01293C692a37f59D1D9b1624F0f21177c", - "vstaAddress": "0xa684cd057951541187f288294a1e1C2646aA2d24", - "symbol": "VSTA", - "decimals": 18 -} -``` diff --git a/src/strategies/vsta-pool-staking/examples.json b/src/strategies/vsta-pool-staking/examples.json deleted file mode 100644 index 8f74a71b5..000000000 --- a/src/strategies/vsta-pool-staking/examples.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "name": "VSTA pool staking strategy", - "strategy": { - "name": "vsta-pool-staking", - "params": { - "balancerVaultAddress": "0xBA12222222228d8Ba445958a75a0704d566BF2C8", - "poolAddress": "0xC61ff48f94D801c1ceFaCE0289085197B5ec44F0", - "poolId": "0xc61ff48f94d801c1ceface0289085197b5ec44f000020000000000000000004d", - "farmAddress": "0x65207da01293C692a37f59D1D9b1624F0f21177c", - "vstaAddress": "0xa684cd057951541187f288294a1e1C2646aA2d24", - "symbol": "VSTA", - "decimals": 18 - } - }, - "network": "42161", - "addresses": [ - "0x88684f6abb5dcd4d3195cc2c714dd79de5a76999", - "0x126824925a67f98dca1eb9d92d78d51c99f3a427", - "0x7a16e350ab5929a40cd9a6f51957d5a45a6e361b" - ], - "snapshot": 44025597 - } -] diff --git a/src/strategies/vsta-pool-staking/index.ts b/src/strategies/vsta-pool-staking/index.ts deleted file mode 100644 index c4e3ca9f1..000000000 --- a/src/strategies/vsta-pool-staking/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { formatUnits } from '@ethersproject/units'; -import { getAddress } from '@ethersproject/address'; -import { Multicaller } from '../../utils'; - -export const author = 'shinitakunai'; -export const version = '0.1.0'; - -const abi = [ - 'function getPoolTokens(bytes32 poolId) external view returns (address[], uint256[], uint256)', - 'function totalSupply() external view returns (uint256)', - 'function balances(address owner) external view returns (uint256)' -]; - -export async function strategy( - _space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - multi.call('poolTokens', options.balancerVaultAddress, 'getPoolTokens', [ - options.poolId - ]); - multi.call('lpTokenTotalSupply', options.poolAddress, 'totalSupply', []); - addresses.forEach((address) => - multi.call(`userBalances.${address}`, options.farmAddress, 'balances', [ - address - ]) - ); - - const { - poolTokens: [tokens, balances], - lpTokenTotalSupply, - userBalances - } = await multi.execute(); - - const vstaIndex = tokens.findIndex( - (address) => address.toLowerCase() === options.vstaAddress.toLowerCase() - ); - - const vstaPerLp = balances[vstaIndex].div(lpTokenTotalSupply); - - const result = Object.fromEntries( - Object.entries(userBalances).map(([address, lpBalance]) => [ - getAddress(address), - parseFloat(formatUnits(vstaPerLp.mul(lpBalance), options.decimals)) - ]) - ); - - return result; -} diff --git a/src/strategies/vsta-pool-staking/schema.json b/src/strategies/vsta-pool-staking/schema.json deleted file mode 100644 index a2d73368c..000000000 --- a/src/strategies/vsta-pool-staking/schema.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "Strategy", - "type": "object", - "properties": { - "symbol": { - "type": "string", - "title": "symbol", - "examples": ["e.g. VSTA"], - "maxLength": 16 - }, - "decimals": { - "type": "number", - "title": "decimals", - "examples": ["e.g. 18"] - }, - "balancerVaultAddress": { - "type": "string", - "title": "balancerVaultAddress", - "examples": ["e.g. 0x2d94AA3e47d9D5024503Ca8491fcE9A2fB4DA198"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "poolAddress": { - "type": "string", - "title": "poolAddress", - "examples": ["e.g. 0x472D0B0DDFE0BC02C27928b8BcbD67E65D07d48a"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "poolId": { - "type": "string", - "title": "poolId", - "examples": [ - "e.g. 0xc61ff48f94d801c1ceface0289085197b5ec44f000020000000000000000004d" - ], - "pattern": "^0x[a-fA-F0-9]{64}$", - "minLength": 66, - "maxLength": 66 - }, - "farmAddress": { - "type": "string", - "title": "farmAddress", - "examples": ["e.g. 0x472D0B0DDFE0BC02C27928b8BcbD67E65D07d48a"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - }, - "vstaAddress": { - "type": "string", - "title": "vstaAddress", - "examples": ["e.g. 0x472D0B0DDFE0BC02C27928b8BcbD67E65D07d48a"], - "pattern": "^0x[a-fA-F0-9]{40}$", - "minLength": 42, - "maxLength": 42 - } - }, - "required": [ - "decimals", - "balancerVaultAddress", - "poolAddress", - "farmAddress", - "vstaAddress" - ], - "additionalProperties": false - } - } -} diff --git a/src/strategies/xkawa-farm/README.md b/src/strategies/xkawa-farm/README.md deleted file mode 100644 index 1975565f8..000000000 --- a/src/strategies/xkawa-farm/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# xkawa-farm - -Returns voting power based on xKawa token held and stacked in the xKawa Single-side farm, as well as associated pending xKawa reward \ No newline at end of file diff --git a/src/strategies/xkawa-farm/examples.json b/src/strategies/xkawa-farm/examples.json deleted file mode 100644 index bf8e8684b..000000000 --- a/src/strategies/xkawa-farm/examples.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "name": "xKAWA farm balance", - "strategy": { - "name": "xkawa-farm", - "params": { - "farm": "0xC68844Cd3BA9d3Ad88F2cC278213F64b8C0bCddf", - "token": "0xdC386452F9FFDa7F0d2940e5c60Dc0F9469b1097", - "LPxKawa": "0x876692166cAC59506Eac1DB7f01B120F5Da98A50", - "symbol": "xKAWA", - "decimals": 18 - } - }, - "network": "1", - "addresses": [ - "0x41a1b36fdef3df142d5116e52d6e72c02b904fda", - "0x7766ef005ec1b38a8472831e2f0631b12c811a5f" - ], - "snapshot": 13677274 - } -] diff --git a/src/strategies/xkawa-farm/index.ts b/src/strategies/xkawa-farm/index.ts deleted file mode 100644 index d580f2bfc..000000000 --- a/src/strategies/xkawa-farm/index.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { BigNumberish, BigNumber } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller } from '../../utils'; - -export const author = 'drgorillamd'; -export const version = '1.0.0'; - -const abi = [ - 'function userInfo(address,uint256) external view returns(uint256 amount,uint256,uint256,uint256)', - 'function balanceOf(address) external view returns(uint256)', - 'function getReserves() external view returns(uint112,uint112 reserve1,uint32)', - 'function totalSupply() external view returns(uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const multi = new Multicaller(network, provider, abi, { blockTag }); - - addresses.forEach((address) => { - multi.call(address + '-staked', options.farm, 'userInfo', [address, '1']); // .amount: uint - multi.call(address + '-balance', options.token, 'balanceOf', [address]); // uint - multi.call(address + '-LPstaked', options.farm, 'userInfo', [address, '3']); // .amount: uint - }); - - // xKawa is token1 - multi.call('reserves', options.LPxKawa, 'getReserves', []); // .reserve1: uint - multi.call('totalSupplyLP', options.LPxKawa, 'totalSupply', []); // uint - - const result: Record = await multi.execute(); - - return Object.fromEntries( - addresses.map((adr) => { - let bal = result[adr + '-staked']['amount']; - bal = bal.add(result[adr + '-balance']); - - bal = bal.add( - BigNumber.from(result[adr + '-LPstaked']['amount']) - .mul(result['reserves']['reserve1']) - .div(result['totalSupplyLP']) - ); - - return [adr, parseFloat(formatUnits(bal, options.decimals))]; - }) - ); -} diff --git a/src/strategies/xrook-balance-of-underlying-weighted/examples.json b/src/strategies/xrook-balance-of-underlying-weighted/examples.json deleted file mode 100644 index fb4e85a6d..000000000 --- a/src/strategies/xrook-balance-of-underlying-weighted/examples.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "xrook-balance-of-underlying-weighted", - "params": { - "symbol": "xROOK", - "address": "0x8aC32F0a635a0896a8428A9c31fBf1AB06ecf489", - "decimals": 18, - "weight": 1.2, - "underlyingTokenAddress": "0xfA5047c9c78B8877af97BDcb85Db743fD7313d4a", - "liquidityPoolAddress": "0x4F868C1aa37fCf307ab38D215382e88FCA6275E2" - } - }, - "network": "1", - "addresses": [ - "0x759a159D78342340EbACffB027c05910c093f430", - "0x2D21170928854F7Da6dC4b1C89dcb95BBc948338", - "0x722f531740937fc21A2FaC7648670C7f2872A1c7", - "0xA1ebb64d1f19D36A31221fFAd19747EF573Cd5A4", - "0x698EE32575dc58DDC0dd2e8B80d2EaB8Af1E94e1", - "0xBE6DcFDf4F1bf2ec0d3dEbD894a02a0765af6D69", - "0xFe4C4A27BeD5E9113480C84A177068F7421A1Eb7", - "0x0054D2575820cf60F5B6D350fb8d3d352BB3B6FD", - "0x4A9A3815060C3Bd08fb4d44C9e74513874771b0C", - "0x99BcEa6bB0403927fB3c038163478D5b42082Fd9", - "0x04b92e3b5De16Dc7e307ea4CEBc5d7dabaE1894F", - "0xe41eeB9ab53Ab208fD57A5E10dFC2FeE464Ca216", - "0x51F13C84b49B64ba6B1615e7D91b11066908BF3C", - "0x2ee8D80de1c389f1254e94bc44D2d1Bc391eD402", - "0x7453019b806919563EaC33870Fe5f8e5154fdF38", - "0x0B72513E73BB60375a4329FABF9BA16f861C0f12", - "0x9a82d59f46913913808bFE905F6157F967AAa28E" - ], - "snapshot": 15039737 - } -] diff --git a/src/strategies/xrook-balance-of-underlying-weighted/index.ts b/src/strategies/xrook-balance-of-underlying-weighted/index.ts deleted file mode 100644 index ccd613306..000000000 --- a/src/strategies/xrook-balance-of-underlying-weighted/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { call } from '../../utils'; -import { formatUnits } from '@ethersproject/units'; -import { strategy as erc20BalanceOfStrategy } from '../erc20-balance-of'; - -export const author = 'TudorSante'; -export const version = '1.0.0'; - -const erc20ABI = [ - 'function balanceOf(address account) external view returns (uint256)', - 'function totalSupply() external view returns (uint256)' -]; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -) { - const score = await erc20BalanceOfStrategy( - space, - network, - provider, - addresses, - options, - snapshot - ); - - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - - const xROOKTotalSupply = await call( - provider, - erc20ABI, - [options.address, 'totalSupply', []], - { blockTag } - ).then((res) => parseFloat(formatUnits(res, options.decimals))); - - const liquidityPoolBalance = await call( - provider, - erc20ABI, - [ - options.underlyingTokenAddress, - 'balanceOf', - [options.liquidityPoolAddress] - ], - { blockTag } - ).then((res) => parseFloat(formatUnits(res, options.decimals))); - - const underlyingValue = liquidityPoolBalance / xROOKTotalSupply; - - return Object.fromEntries( - Object.entries(score).map((res: any) => [ - res[0], - res[1] * options.weight * underlyingValue - ]) - ); -} diff --git a/src/strategies/zorro/README.md b/src/strategies/zorro/README.md deleted file mode 100644 index e333b4df7..000000000 --- a/src/strategies/zorro/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Zorro Proof of Personhood - -This strategy distributes voting power democratically using the Zorro Proof of Personhood protocol (https://zorro.xyz). - -- The voting power of an address is `0` if they have not been proven as a unique person within your Snapshot space. If they are proven unique, then their voting power is given by the `power` parameter (which defaults to `1`). - -- No matter how many Ethereum addresses someone creates, Zorro aims to make it so only one of them can receive a non-zero voting power from this strategy for a given `purposeIdentifier` (which defaults to your space's ENS name). - -## Example options parameters - -#### Minimal example - -```json -{ - "symbol": "VOTES" -} -``` - -In the above example, `purposeIdentifier` defaults to your space's ens name (e.g. `yourdao.eth`), and `power` defaults to `1`. - -#### options - -```json -{ - "symbol": "VOTES", - "purposeIdentifier": "YourDaoName", - "power": 1000 -} -``` - -## Customization - -If you want a proof-of-personhood strategy customized for your purposes, just email ted@suzman.net and I'll be happy to help. diff --git a/src/strategies/zorro/examples.json b/src/strategies/zorro/examples.json deleted file mode 100644 index 0b7d654c4..000000000 --- a/src/strategies/zorro/examples.json +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "name": "Example query", - "strategy": { - "name": "zorro", - "params": { - "purposeIdentifier": "roboteddy.eth", - "symbol": "VOTES", - "power": 1 - } - }, - "network": "1", - "addresses": [ - "0xa478c2975ab1ea89e8196811f51a7b7ade33eb11", - "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", - "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", - "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", - "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e", - "0x1d5E65a087eBc3d03a294412E46CE5D6882969f4", - "0x1f254336E5c46639A851b9CfC165697150a6c327", - "0x2ec3F80BeDA63Ede96BA20375032CDD3aAfb3030", - "0x4AcBcA6BE2f8D2540bBF4CA77E45dA0A4a095Fa2", - "0x4F3D348a6D09837Ae7961B1E0cEe2cc118cec777", - "0x6D7f23A509E212Ba7773EC1b2505d1A134f54fbe", - "0x07a1f6fc89223c5ebD4e4ddaE89Ac97629856A0f", - "0x33fA82CC7AB518Aae0f2Caee754E78d08A0cB190", - "0x8d07D225a769b7Af3A923481E1FdF49180e6A265" - ], - "snapshot": 13891032 - } -] diff --git a/src/strategies/zorro/index.ts b/src/strategies/zorro/index.ts deleted file mode 100644 index 35465e7b6..000000000 --- a/src/strategies/zorro/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import fetch from 'cross-fetch'; - -export const author = 'zorro-project'; -export const version = '0.1.0'; - -const API_URL = 'http://api.zorro.xyz'; - -export async function strategy( - space, - network, - provider, - addresses, - options, - snapshot -): Promise> { - const response = await fetch(API_URL + '/getVerifiedExternalAddresses', { - method: 'POST', - headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, - body: JSON.stringify({ - purposeIdentifier: options.purposeIdentifier || space, - externalAddresses: addresses, - snapshot - }) - }); - const { verifiedExternalAddresses } = await response.json(); - const lookup = Object.fromEntries( - verifiedExternalAddresses.map((addr) => [addr.toLowerCase(), true]) - ); - const power = options.power || 1; - return Object.fromEntries( - addresses.map((address) => [ - address, - lookup[address.toLowerCase()] ? power : 0 - ]) - ); -} diff --git a/src/strategies/zorro/schema.json b/src/strategies/zorro/schema.json deleted file mode 100644 index a887206d2..000000000 --- a/src/strategies/zorro/schema.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/Strategy", - "definitions": { - "Strategy": { - "title": "Strategy", - "type": "object", - "properties": { - "symbol": { - "type": "string", - "title": "Symbol", - "examples": ["e.g. VOTES"], - "maxLength": 16 - }, - "purposeIdentifier": { - "type": "string", - "title": "Purpose identifier (defaults to snapshot space ens)", - "examples": ["e.g. YourDaoName"] - }, - "power": { - "type": "number", - "title": "Voting power per verified person (defaults to 1)", - "examples": ["e.g. 1000"] - } - }, - "required": [], - "additionalProperties": false - } - } -} diff --git a/src/validations/passport-gated/README.md b/src/validations/passport-gated/README.md index 9e41bc9ca..fd7349598 100644 --- a/src/validations/passport-gated/README.md +++ b/src/validations/passport-gated/README.md @@ -30,7 +30,7 @@ Strategy schema & parameters are defined under [schema.json](./schema.json). In ## Code Explanation -The main function (validate()) first fetches the following paramaters: +The main function (validate()) first fetches the following parameters: * `stamps` (required): a list of Stamps that a passport should own. * `operator` (required): (and/or) whether a Passport should own all or at least one of the required stamps. @@ -38,8 +38,8 @@ The main function (validate()) first fetches the following paramaters: Then, it calls the following validation methods: -* `validateStamps`: it uses the API to fetch the current user's Passport stamps and verifies that each has valid issuance and isn't expired. Then, depending on the `operator`, it will iterate through the requred `stamps` and check that the user holds at least one verifiable credential that makes the passport eligible for that stamp. Finally, a Passport will be set as valid if it meets the criteria. -* `validatePassportScore`: if `scoreThreshold` is set to zero this function will be ommited. Otherwise when called, it uses the Scorer API to submit the passport for scoring and get the latest score. If the API response returns a payload with `status === 'DONE'` it will return the result of evaluating the scoring threshold criteria, otherwise the implementation will make periodic requests (up to `PASSPORT_SCORER_MAX_ATTEMPTS`) to the Scorer API until getting a `DONE` status. +* `validateStamps`: it uses the API to fetch the current user's Passport stamps and verifies that each has valid issuance and isn't expired. Then, depending on the `operator`, it will iterate through the required `stamps` and check that the user holds at least one verifiable credential that makes the passport eligible for that stamp. Finally, a Passport will be set as valid if it meets the criteria. +* `validatePassportScore`: if `scoreThreshold` is set to zero this function will be omitted. Otherwise when called, it uses the Scorer API to submit the passport for scoring and get the latest score. If the API response returns a payload with `status === 'DONE'` it will return the result of evaluating the scoring threshold criteria, otherwise the implementation will make periodic requests (up to `PASSPORT_SCORER_MAX_ATTEMPTS`) to the Scorer API until getting a `DONE` status. Finally, it checks the results of both eval functions and returns a boolean value indicating whether the user has a valid Passport. diff --git a/src/validations/passport-gated/examples.json b/src/validations/passport-gated/examples.json index d5347bd99..b89c9a402 100644 --- a/src/validations/passport-gated/examples.json +++ b/src/validations/passport-gated/examples.json @@ -1,4 +1,15 @@ [ + { + "name": "Example of a passport gated validation", + "author": "0x000000000000000000000000000000000000dead", + "space": "fabien.eth", + "network": "1", + "snapshot": "latest", + "params": { + "scoreThreshold": 0 + }, + "valid": true + }, { "name": "Example of a passport gated validation", "author": "0x24F15402C6Bb870554489b2fd2049A85d75B982f", @@ -6,6 +17,7 @@ "network": "1", "snapshot": "latest", "params": { + "scoreThreshold": 20, "stamps": ["Ens", "Github", "Snapshot"], "operator": "OR" }, @@ -18,9 +30,9 @@ "network": "1", "snapshot": "latest", "params": { + "scoreThreshold": 20, "stamps": ["Ens", "Github", "Snapshot"], - "operator": "OR", - "scoreThreshold": 20 + "operator": "OR" }, "valid": false } diff --git a/src/validations/passport-gated/index.ts b/src/validations/passport-gated/index.ts index a920582cf..6bad1f765 100644 --- a/src/validations/passport-gated/index.ts +++ b/src/validations/passport-gated/index.ts @@ -167,8 +167,12 @@ export default class extends Validation { async validate(currentAddress = this.author): Promise { const requiredStamps = this.params.stamps || []; const operator = this.params.operator; - const scoreThreshold = this.params.scoreThreshold || 0; - if (!operator) throw new Error('Operator is required'); + const scoreThreshold = this.params.scoreThreshold; + + if (scoreThreshold === undefined) + throw new Error('Score threshold is required'); + if (requiredStamps.length > 0 && (!operator || operator === 'NONE')) + throw new Error('Operator is required when selecting required stamps'); const provider = snapshot.utils.getProvider(this.network); const proposalTs = (await provider.getBlock(this.snapshot)).timestamp; diff --git a/src/validations/passport-gated/schema.json b/src/validations/passport-gated/schema.json index effaa435a..9b430e8aa 100644 --- a/src/validations/passport-gated/schema.json +++ b/src/validations/passport-gated/schema.json @@ -6,11 +6,39 @@ "title": "Gitcoin passport gated", "type": "object", "properties": { + "scoreThreshold": { + "type": "number", + "title": "Passport Score Threshold", + "description": "Minimum Passport score threshold (>=) to be eligible as a valid Passport", + "minimum": 0, + "maximum": 100, + "default": 0 + }, + "operator": { + "type": "string", + "title": "Approval operator", + "description": "Control how many stamps are required to become eligible.", + "anyOf": [ + { + "const": "NONE", + "title": "No operator" + }, + { + "const": "AND", + "title": "Require all stamps" + }, + { + "const": "OR", + "title": "Require at least one stamp" + } + ] + }, "stamps": { "type": "array", "title": "Stamps", + "description": "List of Eligible Passport Stamps (Optional).", "uniqueItems": true, - "minItems": 1, + "minItems": 0, "maxItems": 27, "items": { "type": "string", @@ -125,32 +153,9 @@ } ] } - }, - "operator": { - "type": "string", - "title": "Approval operator", - "description": "Control how many or which stamps are required to vote.", - "anyOf": [ - { - "const": "AND", - "title": "Require all stamps" - }, - { - "const": "OR", - "title": "Require at least one stamp" - } - ] - }, - "scoreThreshold": { - "type": "number", - "title": "Passport Score Threshold", - "description": "Minimum Passport score threshold (>) to be eligible as a valid Passport", - "minimum": 0, - "maximum": 100, - "default": 0 } }, - "required": ["stamps", "operator"], + "required": ["scoreThreshold"], "additionalProperties": false } } diff --git a/test/__snapshots__/delegation.test.ts.snap b/test/__snapshots__/delegation.test.ts.snap index 50cfedd58..662b921f5 100644 --- a/test/__snapshots__/delegation.test.ts.snap +++ b/test/__snapshots__/delegation.test.ts.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` getDelegations 1`] = ` -Object { - "in": Array [ +{ + "in": [ "0x073aBA73177668Ba1c6661A4CCddbF77dfC8809a", "0x0D996171E7883A286eF720030935f72D0Bac8219", "0x186E20ae3530520C9F3E6C46F2f5d1062b784761", diff --git a/test/__snapshots__/vp.test.ts.snap b/test/__snapshots__/vp.test.ts.snap index 80a296baa..973d41de0 100644 --- a/test/__snapshots__/vp.test.ts.snap +++ b/test/__snapshots__/vp.test.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[` getVp with delegation 1`] = ` -Object { +{ "vp": 10.077130335431244, - "vp_by_strategy": Array [ + "vp_by_strategy": [ 0, 10.028706352441185, 0, @@ -14,9 +14,9 @@ Object { `; exports[` getVp without delegation 1`] = ` -Object { +{ "vp": 21.55404462002206, - "vp_by_strategy": Array [ + "vp_by_strategy": [ 0, 9.998985610441185, 11.506635026590818, diff --git a/test/strategy.test.ts b/test/strategy.test.ts index 081d1de12..5e729838a 100644 --- a/test/strategy.test.ts +++ b/test/strategy.test.ts @@ -238,6 +238,23 @@ describe.each(examples)( ).toBe(true); } ); + (schema ? it : it.skip)( + 'Check schema (if available) all arrays in all levels should contain `items` property', + async () => { + const strategyParamsSchema = schema.definitions.Strategy.properties; + function checkArrayItems(schema: any) { + Object.keys(schema).forEach((key) => { + if (typeof schema[key] === 'object') { + checkArrayItems(schema[key]); + } else if (schema.type === 'array') { + expect(schema.items).toBeTruthy(); + } + }); + } + + checkArrayItems(strategyParamsSchema); + } + ); (schema ? it : it.skip)( 'Strategy should work even when strategy symbol is null', async () => { diff --git a/yarn.lock b/yarn.lock index 923d0890d..517448187 100644 --- a/yarn.lock +++ b/yarn.lock @@ -316,22 +316,7 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@ethersproject/abi@^5.0.12": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" - integrity sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w== - dependencies: - "@ethersproject/address" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/constants" "^5.5.0" - "@ethersproject/hash" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - -"@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.6.4": +"@ethersproject/abi@^5.0.12", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.6.4": version "5.6.4" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.6.4.tgz#f6e01b6ed391a505932698ecc0d9e7a99ee60362" integrity sha512-TTeZUlCeIHG6527/2goZA6gW5F8Emoc7MrZDC7hhP84aRGvW3TEdTnZR08Ls88YXM1m2SuK42Osw/jSi3uO8gg== @@ -346,19 +331,6 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.1" -"@ethersproject/abstract-provider@^5.5.0": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" - integrity sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg== - dependencies: - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/networks" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/transactions" "^5.5.0" - "@ethersproject/web" "^5.5.0" - "@ethersproject/abstract-provider@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.6.1.tgz#02ddce150785caf0c77fe036a0ebfcee61878c59" @@ -385,17 +357,6 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/web" "^5.7.0" -"@ethersproject/abstract-signer@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" - integrity sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA== - dependencies: - "@ethersproject/abstract-provider" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/abstract-signer@^5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.2.tgz#491f07fc2cbd5da258f46ec539664713950b0b33" @@ -418,29 +379,7 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@^5.0.2", "@ethersproject/address@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" - integrity sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw== - dependencies: - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/rlp" "^5.5.0" - -"@ethersproject/address@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.1.tgz#ab57818d9aefee919c5721d28cd31fd95eff413d" - integrity sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/rlp" "^5.6.1" - -"@ethersproject/address@^5.7.0": +"@ethersproject/address@^5.0.2", "@ethersproject/address@^5.6.1", "@ethersproject/address@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== @@ -451,13 +390,6 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/rlp" "^5.7.0" -"@ethersproject/base64@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" - integrity sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/base64@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.6.1.tgz#2c40d8a0310c9d1606c2c37ae3092634b41d87cb" @@ -507,21 +439,7 @@ "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" -"@ethersproject/bytes@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" - integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== - dependencies: - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/bytes@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.6.1.tgz#24f916e411f82a8a60412344bf4a813b917eefe7" - integrity sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g== - dependencies: - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/bytes@^5.7.0": +"@ethersproject/bytes@^5.5.0", "@ethersproject/bytes@^5.6.1", "@ethersproject/bytes@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== @@ -565,35 +483,7 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/transactions" "^5.6.2" -"@ethersproject/hash@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" - integrity sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg== - dependencies: - "@ethersproject/abstract-signer" "^5.5.0" - "@ethersproject/address" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - -"@ethersproject/hash@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.6.1.tgz#224572ea4de257f05b4abf8ae58b03a67e99b0f4" - integrity sha512-L1xAHurbaxG8VVul4ankNX5HgQ8PNCTrnVXEiFnE9xoRnaUcgfD12tZINtDinSllxPLCtGwguQxJ5E6keE84pA== - dependencies: - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" - -"@ethersproject/hash@^5.7.0": +"@ethersproject/hash@^5.6.1", "@ethersproject/hash@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== @@ -684,13 +574,6 @@ resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== -"@ethersproject/networks@^5.5.0": - version "5.5.2" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.2.tgz#784c8b1283cd2a931114ab428dae1bd00c07630b" - integrity sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ== - dependencies: - "@ethersproject/logger" "^5.5.0" - "@ethersproject/networks@^5.6.3": version "5.6.4" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.6.4.tgz#51296d8fec59e9627554f5a8a9c7791248c8dc07" @@ -713,13 +596,6 @@ "@ethersproject/bytes" "^5.6.1" "@ethersproject/sha2" "^5.6.1" -"@ethersproject/properties@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" - integrity sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA== - dependencies: - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.6.0.tgz#38904651713bc6bdd5bdd1b0a4287ecda920fa04" @@ -768,14 +644,6 @@ "@ethersproject/bytes" "^5.6.1" "@ethersproject/logger" "^5.6.0" -"@ethersproject/rlp@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.5.0.tgz#530f4f608f9ca9d4f89c24ab95db58ab56ab99a0" - integrity sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/rlp@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.6.1.tgz#df8311e6f9f24dcb03d59a2bac457a28a4fe2bd8" @@ -810,18 +678,6 @@ "@ethersproject/logger" "^5.6.0" hash.js "1.1.7" -"@ethersproject/signing-key@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.5.0.tgz#2aa37169ce7e01e3e80f2c14325f624c29cedbe0" - integrity sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - bn.js "^4.11.9" - elliptic "6.5.4" - hash.js "1.1.7" - "@ethersproject/signing-key@^5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.2.tgz#8a51b111e4d62e5a62aee1da1e088d12de0614a3" @@ -897,21 +753,6 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" - integrity sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA== - dependencies: - "@ethersproject/address" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/constants" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/rlp" "^5.5.0" - "@ethersproject/signing-key" "^5.5.0" - "@ethersproject/transactions@^5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.6.2.tgz#793a774c01ced9fe7073985bb95a4b4e57a6370b" @@ -942,16 +783,7 @@ "@ethersproject/rlp" "^5.7.0" "@ethersproject/signing-key" "^5.7.0" -"@ethersproject/units@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.6.1.tgz#ecc590d16d37c8f9ef4e89e2005bda7ddc6a4e6f" - integrity sha512-rEfSEvMQ7obcx3KWD5EWWx77gqv54K6BKiZzKxkQJqtpriVsICrktIQmKl8ReNToPeIYPnFHpXvKpi068YFZXw== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/units@^5.7.0": +"@ethersproject/units@^5.6.1", "@ethersproject/units@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== @@ -981,17 +813,6 @@ "@ethersproject/transactions" "^5.6.2" "@ethersproject/wordlists" "^5.6.1" -"@ethersproject/web@^5.5.0": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.1.tgz#cfcc4a074a6936c657878ac58917a61341681316" - integrity sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg== - dependencies: - "@ethersproject/base64" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - "@ethersproject/web@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.6.1.tgz#6e2bd3ebadd033e6fe57d072db2b69ad2c9bdf5d" @@ -1363,10 +1184,10 @@ dependencies: "@sinonjs/commons" "^2.0.0" -"@snapshot-labs/snapshot.js@^0.7.3": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@snapshot-labs/snapshot.js/-/snapshot.js-0.7.3.tgz#54984f135b78c7d3d7d26484d7275a0793d5bc9c" - integrity sha512-T+cjaJ9ZPLlKicPTxuN6FIQXUgADjvit/QFmwsOlGWxpMzAQ79k5rWm0ldex96JTPIFucUMgqHqWLA6UZwmmTQ== +"@snapshot-labs/snapshot.js@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@snapshot-labs/snapshot.js/-/snapshot.js-0.10.1.tgz#d783ee394d6e3ad3d3a6e91fea67411752d15180" + integrity sha512-PacD8HdsYZhb1Yifp6n+11Og+nZUvGhTosu+ejnEwhP6zQOFMg6gaIEsWGjoAMnjos0sgA/oIbWdPIzqJRTECw== dependencies: "@ensdomains/eth-ens-namehash" "^2.0.15" "@ethersproject/abi" "^5.6.4" @@ -1683,17 +1504,7 @@ ajv@^6.10.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0: - version "8.9.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" - integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ajv@^8.11.0: +ajv@^8.0.0, ajv@^8.11.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==