diff --git a/src/strategies/moxie/examples.json b/src/strategies/moxie/examples.json index e6377288b..e5723a02c 100644 --- a/src/strategies/moxie/examples.json +++ b/src/strategies/moxie/examples.json @@ -5,7 +5,12 @@ "name": "moxie" }, "network": "8453", - "addresses": ["0xb59aa5bb9270d44be3fa9b6d67520a2d28cf80ab"], + "addresses": [ + "0x764e427020ad72624075c61260192c6e486d15a5", + "0xb59aa5bb9270d44be3fa9b6d67520a2d28cf80ab", + "0xcf03287A85298166522002c97aE4B1556fF026B3", + "0xcBFBcbFcA74955B8AB75Dec41F7b9eF36F329879" + ], "snapshot": 20631331 } ] diff --git a/src/strategies/moxie/index.ts b/src/strategies/moxie/index.ts index df81beac6..35ee37c48 100644 --- a/src/strategies/moxie/index.ts +++ b/src/strategies/moxie/index.ts @@ -1,23 +1,10 @@ -import { BigNumberish } from '@ethersproject/bignumber'; -import { formatUnits } from '@ethersproject/units'; -import { Multicaller, subgraphRequest } from '../../utils'; -import { getAddress } from '@ethersproject/address'; +import { customFetch } from '../../utils'; -export const author = 'Hrishikesh-Thakkar'; -export const version = '0.0.1'; +export const author = '0xsarvesh'; +export const version = '0.0.2'; -const abi = [ - 'function balanceOf(address account) external view returns (uint256)' -]; - -const QUERY_LIMIT = 1000; -const UNSTAKED_FAN_TOKEN_MULTIPLIER = 2; -const STAKED_FAN_TOKEN_MULTIPLIER = 3; -const MOXIE_LIQUIDITY_MULTIPLIER = 2; -const MOXIE_CONTRACT_ADDRESS = '0x8C9037D1Ef5c6D1f6816278C7AAF5491d24CD527'; -const MOXIE_DECIMALS = 18; -const PORTFOLIO_SUBGRAPH_PAGE_LIMIT = 2; -const LIQUIDITY_POOL_SUBGRAPH_PAGE_LIMIT = 1; +const MOXIE_ENDPOINT = 'https://api.moxie.xyz/protocol/address-votes'; +const MOXIE_API_KEY = process.env.MOXIE_API_KEY || ''; //Strategy to Compute Voting Power for MoxieDAO export async function strategy( @@ -28,216 +15,24 @@ export async function strategy( options, snapshot ) { - //Check if the addresses array has length not equal to 1 - if (addresses.length != 1) { - throw new Error('This strategy expects a single address'); - } - const MOXIE_API_KEY = process.env.MOXIE_API_KEY || ''; - const MOXIE_PROTOCOL_ID = '7zS29h4BDSujQq8R3TFF37JfpjtPQsRUpoC9p4vo4scx'; - const MOXIE_VESTING_ID = 'BuR6zAj2GSVZz6smGbJZkgQx8S6GUS881R493ZYZKSk3'; - const MOXIE_LIQUIDITY_ID = '2rv5XN3LDQiuc9BXFzUri7ZLnS6K1ZqNJzp8Zj8TqMhy'; - - //SETTING DEFAULT SUBGRAPH URLS - let MOXIE_PROTOCOL_SUBGRAPH_URL = - 'https://api.studio.thegraph.com/query/88457/moxie-protocol/version/latest'; - let MOXIE_VESTING_SUBGRAPH_URL = - 'https://api.studio.thegraph.com/query/88457/moxie-vesting/version/latest'; - let MOXIE_LIQUIDITY_POOL_SUBGRAPH_URL = - 'https://api.studio.thegraph.com/query/88457/moxie-liquidity/version/latest'; - - if (MOXIE_API_KEY !== '') { - MOXIE_PROTOCOL_SUBGRAPH_URL = `https://gateway.thegraph.com/api/${MOXIE_API_KEY}/subgraphs/id/${MOXIE_PROTOCOL_ID}`; - MOXIE_LIQUIDITY_POOL_SUBGRAPH_URL = `https://gateway.thegraph.com/api/${MOXIE_API_KEY}/subgraphs/id/${MOXIE_LIQUIDITY_ID}`; - MOXIE_VESTING_SUBGRAPH_URL = `https://gateway.thegraph.com/api/${MOXIE_API_KEY}/subgraphs/id/${MOXIE_VESTING_ID}`; - } - - //Check if the snapshot is for a specific block number or it's latest - const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; - const addressesMap = addresses.reduce((map, address) => { - map[getAddress(address)] = 0; - return map; - }, {}); - - const lowercaseAddresses = Object.keys(addressesMap).map((address) => - address.toLowerCase() - ); - - // Once we have the addresses we need to query the subgraphs to get the vesting contract addresses - const vestingSubgraphQuery = { - tokenLockWallets: { - __args: { - where: { - beneficiary_in: lowercaseAddresses - } - }, - id: true, - beneficiary: true - } - }; - //Adding block to the query if the snapshot is not latest - if (snapshot !== 'latest') { - vestingSubgraphQuery.tokenLockWallets.__args['block'] = { - number: snapshot - }; - } - - //Query the vesting subgraph to get the vesting contract addresses - const vestingContractResponse = await subgraphRequest( - MOXIE_VESTING_SUBGRAPH_URL, - vestingSubgraphQuery - ); - - // Generate a map of vesting contract addresses to beneficiaries - const addressToBeneficiaryMap = - vestingContractResponse.tokenLockWallets.reduce((map, wallet) => { - map[wallet.id.toLowerCase()] = wallet.beneficiary.toLowerCase(); - return map; - }, {}); - - // Add vesting contract addresses to the list of addresses to query - const allAddresses = [ - ...Object.keys(addressToBeneficiaryMap), - ...lowercaseAddresses - ]; - - // Initialise all the addresses with a score of 0 - const allAddressesScoreMap = allAddresses.reduce((map, address) => { - map[getAddress(address)] = 0; - return map; - }, {}); - - const protocolSubgraphQuery = { - portfolios: { - __args: { - where: { - user_in: allAddresses, - balance_gt: 0 - }, - first: QUERY_LIMIT, - skip: 0 - }, - unstakedBalance: true, - stakedBalance: true, - subjectToken: { - currentPriceInMoxie: true - }, - user: { - id: true - } - } - }; - - const liquidityPoolSubgraphQuery = { - userPools: { - __args: { - where: { - user_in: allAddresses - }, - first: QUERY_LIMIT, - skip: 0 - }, - totalLPAmount: true, - pool: { - totalSupply: true, - moxieReserve: true - }, - user: { - id: true + const response = await customFetch( + MOXIE_ENDPOINT, + { + method: 'POST', + body: JSON.stringify({ + block: snapshot, + addresses + }), + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'x-airstack-protocol': MOXIE_API_KEY } - } - }; - - //Adding block to the queries if the snapshot is not latest - if (snapshot !== 'latest') { - protocolSubgraphQuery.portfolios.__args['block'] = { number: snapshot }; - liquidityPoolSubgraphQuery.userPools.__args['block'] = { number: snapshot }; - } - - const fetchSubgraphData = async ( - subgraphUrl, - query, - processFunction, - key, - pageLimit - ) => { - let next_page = 0; - while (next_page < pageLimit) { - query[key].__args.skip = next_page * QUERY_LIMIT; - const response = await subgraphRequest(subgraphUrl, query); - const data = response[Object.keys(response)[0]]; - - processFunction(data); - - if (data.length < QUERY_LIMIT) break; - next_page++; - } - }; - - const processProtocolData = (portfolios) => { - portfolios.forEach((portfolio) => { - const userAddress = getAddress(portfolio.user.id); - allAddressesScoreMap[userAddress] += - parseFloat(formatUnits(portfolio.unstakedBalance, MOXIE_DECIMALS)) * - UNSTAKED_FAN_TOKEN_MULTIPLIER * - portfolio.subjectToken.currentPriceInMoxie + - parseFloat(formatUnits(portfolio.stakedBalance, MOXIE_DECIMALS)) * - STAKED_FAN_TOKEN_MULTIPLIER * - portfolio.subjectToken.currentPriceInMoxie; - }); - }; - - const processLiquidityPoolData = (userPools) => { - userPools.forEach((userPool) => { - const userAddress = getAddress(userPool.user.id); - allAddressesScoreMap[userAddress] += - (MOXIE_LIQUIDITY_MULTIPLIER * - parseFloat(formatUnits(userPool.totalLPAmount, MOXIE_DECIMALS)) * - parseFloat(formatUnits(userPool.pool.moxieReserve, MOXIE_DECIMALS))) / - parseFloat(formatUnits(userPool.pool.totalSupply, MOXIE_DECIMALS)); - }); - }; - - // RPC Call to get balance of Moxie at a block for users - const fetchBalances = async () => { - const multi = new Multicaller(network, provider, abi, { blockTag }); - allAddresses.forEach((address) => - multi.call(address, MOXIE_CONTRACT_ADDRESS, 'balanceOf', [address]) - ); - const result: Record = await multi.execute(); - Object.entries(result).forEach(([address, balance]) => { - const formattedBalance = parseFloat(formatUnits(balance, MOXIE_DECIMALS)); - allAddressesScoreMap[getAddress(address)] += formattedBalance; - }); - }; - - // Fetch data from both subgraphs in parallel - await Promise.all([ - fetchSubgraphData( - MOXIE_PROTOCOL_SUBGRAPH_URL, - protocolSubgraphQuery, - processProtocolData, - 'portfolios', - PORTFOLIO_SUBGRAPH_PAGE_LIMIT - ), - fetchSubgraphData( - MOXIE_LIQUIDITY_POOL_SUBGRAPH_URL, - liquidityPoolSubgraphQuery, - processLiquidityPoolData, - 'userPools', - LIQUIDITY_POOL_SUBGRAPH_PAGE_LIMIT - ), - fetchBalances() - ]); + }, + 60000 + ); - // Now we have the score for each address we need to ensure it is added to the beneficiary address if it exists - Object.keys(allAddressesScoreMap).forEach((address) => { - const beneficiary = addressToBeneficiaryMap[address.toLowerCase()]; - if (beneficiary) { - addressesMap[getAddress(beneficiary)] += allAddressesScoreMap[address]; - } else { - addressesMap[address] += allAddressesScoreMap[address]; - } - }); + const votes = await response.json(); - return addressesMap; + return votes.scores; }