Skip to main content

Balance reporting of LSD validators

This tutorial allows anyone to do Balance Reporting in batches for all the LSd validators.

NOTE: Do not forget to change the provider network from goerli to mainnet. The code shared below shows balance reporting for goerli network

Create a .env file

In the project directory, create a .env file which contains following values:

  • INFURA_PROJECT_ID
  • INFURA_PROJECT_SECRET
  • PRIV_KEY
  • BEACON_NODE

Project dependencies

  • dotenv
  • ethers
  • graphql
  • graphql-request
  • Stakehouse SDK

To install the above dependencies run the following command

yarn add dotenv [email protected] graphql graphql-request @blockswaplab/stakehouse-sdk

ethers v6 and above causes issues with the SDK hence, it is recommended to install ethers v5. The SDK uses ethers v5.7.2

Balance reporting script

Create a .js file with any appropriate name, and copy the code shared below.

require('dotenv').config();
const { ethers } = require('ethers');
const { gql, request } = require('graphql-request');
const { StakehouseSDK } = require('@blockswaplab/stakehouse-sdk');

const INFURA_PROJECT_ID = process.env.INFURA_PROJECT_ID;
const INFURA_PROJECT_SECRET = process.env.INFURA_PROJECT_SECRET;
const PRIV_KEY = process.env.PRIV_KEY;
const BEACON_NODE = process.env.BEACON_NODE;

const queryAllBLSPublicKeys = async (sdk) => {

const sdkConstants = await sdk.constants;

const lookupQuery = gql`
query listOfKnots {
lsdvalidators {
id
}
}
`;

const response = await request(
sdkConstants.stakehouseUrls.LSD_SUBGRAPH_ENDPOINT,
lookupQuery
);

if (!response) {
throw new Error('Invalid response fetching KNOTs')
}

if(response.lsdvalidators === 'undefined' || response.lsdvalidators == null || response.lsdvalidators.length == 0) {
return;
}

return response.lsdvalidators;
};

const getValidatorData = async (sdk, blsPublicKeys) => {

const sdkConstants = await sdk.constants;

const lookupQuery = gql`
query knotDetials($id_in: [String!]!) {
knots(where: {
id_in: $id_in
}) {
id
stakeHouse
}
}
`;

const response = await request(
sdkConstants.stakehouseUrls.SUBGRAPH_ENDPOINTS,
lookupQuery,
{
id_in: blsPublicKeys
}
);

if (!response) {
throw new Error('Invalid response fetching KNOTs')
}

if(response.knots === 'undefined' || response.knots == null || response.knots.length == 0) {
return;
}

return response.knots;
};

const reportBalancePromise = (sdk, blsPublicKey, stakehouseAddress) => {

const res = sdk.reportBalance(
BEACON_NODE,
blsPublicKey,
stakehouseAddress
);

return res;
};

const main = async () => {

// change goerli to mainnet
const provider = new ethers.providers.InfuraProvider("goerli", {
projectId: INFURA_PROJECT_ID,
projectSecret: INFURA_PROJECT_SECRET
});

const signer = new ethers.Wallet(PRIV_KEY, provider);

const sdk = new StakehouseSDK(signer);

const listOfBlsPublicKeys = await queryAllBLSPublicKeys(sdk);

let blsPublicKeys = [];
for(let i=0; i<listOfBlsPublicKeys.length; ++i) {
blsPublicKeys.push(listOfBlsPublicKeys[i].id)
}

const validatorData = await getValidatorData(sdk, blsPublicKeys);
console.log(validatorData);

let promises = [];

for(let i=0; i<validatorData.length; ++i) {
const blsPublicKey = validatorData[i].id;
const stakehouseAddress = validatorData[i].stakeHouse;

promises.push(reportBalancePromise(sdk, blsPublicKey, stakehouseAddress));
}

await Promise.allSettled(promises).then(
async(result) => {
for(let i=0; i<result.length; ++i) {
console.log(validatorData[i].id, result[i]);
}
}
).catch(
(e) => {
console.log(e);
}
);
};

main();