From 9cd6cf0efc2f5e4ac3a1a31528e1d307ae2c5b2f Mon Sep 17 00:00:00 2001 From: QYuQianchen <10935300+QYuQianchen@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:55:53 +0800 Subject: [PATCH 1/3] catch cross-chain block conversion error with fallback. Specify number types for aggregation --- .../hopr-stake-and-balance-qv/index.ts | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/strategies/hopr-stake-and-balance-qv/index.ts b/src/strategies/hopr-stake-and-balance-qv/index.ts index c8753b13f..29d0d927a 100644 --- a/src/strategies/hopr-stake-and-balance-qv/index.ts +++ b/src/strategies/hopr-stake-and-balance-qv/index.ts @@ -1,4 +1,4 @@ -import { formatUnits, parseUnits } from '@ethersproject/units'; +import { formatUnits } from '@ethersproject/units'; import { BigNumber } from '@ethersproject/bignumber'; import { multicall } from '../../utils'; import { @@ -57,7 +57,7 @@ function calculateScore( safeStakingBalance: number, exponent: number ) { - const total = + const total: number = parseFloat( formatUnits( gnosisBalance.add(BigNumber.from(mainnetBalance.toString())), @@ -110,6 +110,7 @@ export async function strategy( block.timestamp, options.fallbackGnosisBlock ); + // console.info(`Gnosis subgraph block number ${subgraphBlock}`); // trim addresses to sub of "QUERY_LIMIT" addresses. const addressSubsets: string[][] = trimArray(addresses); @@ -125,7 +126,7 @@ export async function strategy( // 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[] = []; + let 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 @@ -150,8 +151,7 @@ export async function strategy( stuidoDevSafeStakeSubgraphUrl, studioProdSafeStakeSubgraphUrl, subset, - subgraphBlock, - snapshot + subgraphBlock ) ) ); @@ -160,7 +160,7 @@ export async function strategy( resultSubset.forEach((safe) => { // 1. safe -> nodes safeNodes.set(safe.safeAddress, safe.nodes); - nodes.concat(safe.nodes); + nodes = nodes.concat(safe.nodes); if (safe.owners.length == 0) { // 2. safe -> factor safeFactor.set(safe.safeAddress, 0); @@ -202,8 +202,7 @@ export async function strategy( stuidoDevChannelsSubgraphUrl, studioProdChannelsSubgraphUrl, subset, - subgraphBlock, - snapshot + subgraphBlock ) ) ); @@ -220,10 +219,7 @@ export async function strategy( safeStakeInChannel.set(key, BigNumber.from('0')); } else { const stakesInNodes = nodesManagedBySafe.reduce( - (acc, cur) => - (acc = acc.add( - parseUnits(subgraphNodeStakeInChannels[cur] ?? '0', 18) - )), + (acc, cur) => (acc = acc.add(subgraphNodeStakeInChannels[cur])), BigNumber.from('0') ); safeStakeInChannel.set(key, stakesInNodes); @@ -261,8 +257,7 @@ export async function strategy( stuidoDevHoprOnGnosisSubgraphUrl, studioProdHoprOnGnosisSubgraphUrl, subset, - subgraphBlock, - snapshot + subgraphBlock ) ) ); @@ -274,11 +269,12 @@ export async function strategy( // 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) => { + const summedStakePerSafe: number[] = addresses.map((address) => { + const lowercasedAddr = address.toLowerCase(); // from the voting address, get all the safe addresses - const safes = ownerSafes.get(address) ?? []; + const safes = ownerSafes.get(lowercasedAddr) ?? []; if (safes.length == 0) { - return BigNumber.from('0'); + return 0; } else { return safes.reduce((acc, curSafe) => { // factor * (x/wxHOPR token balance + safe stake in channels) @@ -298,7 +294,7 @@ export async function strategy( formatUnits(curSafeTokenBalance.add(curSafeStakeInChannels), 18) ) ); - }); + }, 0); } }); From 74ff5023cc12bf05815669d27fb4e96e3512a90d Mon Sep 17 00:00:00 2001 From: QYuQianchen <10935300+QYuQianchen@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:56:29 +0800 Subject: [PATCH 2/3] make subgraph functions use explicit block number --- .../hopr-stake-and-balance-qv/utils.ts | 62 +++++++------------ 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/src/strategies/hopr-stake-and-balance-qv/utils.ts b/src/strategies/hopr-stake-and-balance-qv/utils.ts index c8a72cff6..17ae44214 100644 --- a/src/strategies/hopr-stake-and-balance-qv/utils.ts +++ b/src/strategies/hopr-stake-and-balance-qv/utils.ts @@ -131,13 +131,18 @@ export async function getGnosisBlockNumber( }; // query from subgraph - const data = await subgraphRequestsToVariousServices( - queryUrl, - null, - null, - query - ); - return !data ? fallbackBlockNumber : Number(data.blocks[0].number); + try { + const data = await subgraphRequestsToVariousServices( + queryUrl, + null, + null, + query + ); + return !data ? fallbackBlockNumber : Number(data.blocks[0].number); + } catch (error) { + // console.error(`cannot convert block number from subgraph due to ${error}`); + return fallbackBlockNumber; + } } /** @@ -148,8 +153,7 @@ export async function getGnosisBlockNumber( * @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 + * @param blockNumber Gnosis chain block number for the query * @returns a key-value object where the key is safe address the value is the total number of owners. */ export async function safeStakeSubgraphQuery( @@ -157,13 +161,13 @@ export async function safeStakeSubgraphQuery( stuidoDevSubgraphUrl: string | null, studioProdSubgraphUrl: string | null, addresses: string[], - blockNumber: number, - snapshot: number | string + blockNumber: number ): Promise { const query = { safes: { __args: { first: QUERY_LIMIT, + block: { number: blockNumber }, where: { owners_: { owner_in: addresses.map((adr) => adr.toLowerCase()) @@ -184,11 +188,6 @@ export async function safeStakeSubgraphQuery( } }; - if (snapshot !== 'latest') { - // @ts-ignore - query.safes.__args.block = { number: blockNumber }; - } - // query from subgraph const data = await subgraphRequestsToVariousServices( hostedSubgraphUrl, @@ -198,7 +197,7 @@ export async function safeStakeSubgraphQuery( ); // return parsed entries - if (!data || !data.safes || data.safe.length == 0) { + if (!data || !data.safes || data.safes.length == 0) { return []; } else { return data.safes.map((s) => { @@ -217,8 +216,7 @@ export async function safeStakeSubgraphQuery( * @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 + * @param blockNumber Gnosis chain block number for the query * @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( @@ -226,13 +224,13 @@ export async function hoprTotalOnGnosisSubgraphQuery( stuidoDevSubgraphUrl: string | null, studioProdSubgraphUrl: string | null, addresses: string[], - blockNumber: number, - snapshot: number | string + blockNumber: number ): Promise<{ [propName: string]: BigNumber }> { const query = { accounts: { __args: { first: QUERY_LIMIT, + block: { number: blockNumber }, where: { id_in: addresses.map((adr) => adr.toLowerCase()) } @@ -242,11 +240,6 @@ export async function hoprTotalOnGnosisSubgraphQuery( } }; - if (snapshot !== 'latest') { - // @ts-ignore - query.accounts.__args.block = { number: blockNumber }; - } - // query from subgraph const data = await subgraphRequestsToVariousServices( hostedSubgraphUrl, @@ -271,8 +264,7 @@ export async function hoprTotalOnGnosisSubgraphQuery( * @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 + * @param blockNumber Gnosis chain block number for the query * @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( @@ -280,13 +272,13 @@ export async function hoprNodeStakeOnChannelsSubgraphQuery( stuidoDevSubgraphUrl: string | null, studioProdSubgraphUrl: string | null, addresses: string[], - blockNumber: number, - snapshot: number | string + blockNumber: number ): Promise<{ [propName: string]: BigNumber }> { const query = { accounts: { __args: { first: QUERY_LIMIT, + block: { number: blockNumber }, where: { id_in: addresses.map((adr) => adr.toLowerCase()) } @@ -296,11 +288,6 @@ export async function hoprNodeStakeOnChannelsSubgraphQuery( } }; - if (snapshot !== 'latest') { - // @ts-ignore - query.accounts.__args.block = { number: blockNumber }; - } - // query from subgraph const data = await subgraphRequestsToVariousServices( hostedSubgraphUrl, @@ -312,10 +299,7 @@ export async function hoprNodeStakeOnChannelsSubgraphQuery( // 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) - ]); + : data.accounts.map((d) => [d.id, parseUnits(d.balance.toString(), 18)]); return Object.fromEntries(entries); } From f858d0f0936f4b30c8c361af6d0c4c9062b09355 Mon Sep 17 00:00:00 2001 From: QYuQianchen <10935300+QYuQianchen@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:57:09 +0800 Subject: [PATCH 3/3] Update safestake query id and latest version string in example file --- src/strategies/hopr-stake-and-balance-qv/examples.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/strategies/hopr-stake-and-balance-qv/examples.json b/src/strategies/hopr-stake-and-balance-qv/examples.json index c04b7e2c7..314908bb1 100644 --- a/src/strategies/hopr-stake-and-balance-qv/examples.json +++ b/src/strategies/hopr-stake-and-balance-qv/examples.json @@ -6,7 +6,7 @@ "params": { "tokenAddress": "0xf5581dfefd8fb0e4aec526be659cfab1f8c781da", "symbol": "HOPR", - "fallbackGnosisBlock": 27852687, + "fallbackGnosisBlock": 33114482, "subgraphStudioProdQueryApiKey": null, "subgraphStudioDevAccountId": null, "subgraphHostedAccountName": "hoprnet", @@ -14,13 +14,13 @@ "useChannelStake": true, "useHoprOnGnosis": true, "useHoprOnMainnet": true, - "subgraphStudioProdSafeStakeQueryId": "DrkbaCvNGVcNH1RghepLRy6NSHFi8Dmwp4T2LN3LqcjY", + "subgraphStudioProdSafeStakeQueryId": "FEQcaX9qfh31YL2K7rxRN5a3sr9rjMWkguJnby7StNRo", "subgraphStudioDevSafeStakeSubgraphName": "hopr-nodes-dufour", - "subgraphStudioDevSafeStakeVersion": "latest", + "subgraphStudioDevSafeStakeVersion": "version/latest", "subgraphHostedSafeStakeSubgraphName": null, "subgraphStudioProdChannelsQueryId": "Feg6Jero3aQzesVYuqk253NNLyNAZZppbDPKFYEGJ1Hj", "subgraphStudioDevChannelsSubgraphName": "hopr-channels", - "subgraphStudioDevChannelsVersion": "latest", + "subgraphStudioDevChannelsVersion": "version/latest", "subgraphHostedChannelsSubgraphName": null, "subgraphStudioProdHoprOnGnosisQueryId": "njToE7kpetd3P9sJdYQPSq6yQjBs7w9DahQpBj6WAoD", "subgraphStudioDevHoprOnGnosisSubgraphName": "hopr-on-gnosis", @@ -48,6 +48,6 @@ "0x3e1A12a6019ee26418F22B656926fE38F5e58C5f", "0x7A27A4D91231aCB3282b410Cc784517B417FA0DA" ], - "snapshot": 17220270 + "snapshot": 19584202 } ]