├── lido-subgraph ├── tests-unit │ ├── .gitignore │ └── mockedFns.ts ├── snippets-nodejs │ ├── .gitignore │ ├── utils.js │ ├── utils │ │ ├── BigNumber.js │ │ ├── index.js │ │ ├── subgraphFetcher.js │ │ ├── Big.js │ │ └── ethCalls.js │ ├── queries │ │ ├── index.js │ │ ├── TotalReward.js │ │ ├── Submissions.js │ │ ├── TransferIn.js │ │ └── TransferOut.js │ ├── config.js │ ├── existingKeysCheck.js │ ├── largestDust.js │ ├── removeContracts.js │ ├── findFrequentStakers.js │ ├── reportSharesOfAddr.js │ ├── rewardBalances.js │ ├── allStEthHolders.js │ ├── monthTotalRewards.js │ ├── referrals.js │ ├── submitsForPeriod.js │ ├── findTreasurySharesMismatch.js │ ├── calculateShares.js │ ├── findTotalsMismatches.js │ ├── stethBalanceOverTime.js │ ├── yarn.lock │ └── rewardHistory.js ├── tests │ ├── package.json │ ├── utils │ │ ├── BigNumber.js │ │ ├── checkIfLimited.js │ │ ├── loadAddressShares.js │ │ ├── getTotals.js │ │ ├── getEntityCount.js │ │ ├── index.js │ │ ├── getStatus.js │ │ ├── subgraphFetch.js │ │ ├── getTestAddresses.js │ │ ├── calculateSharesCeiling.js │ │ ├── calculateShares.js │ │ ├── ethCalls.js │ │ └── calculateAddressBalance.js │ ├── isNotErrored.test.js │ ├── allOracleReportsExist.test.js │ ├── isSynced.test.js │ ├── isSyncedStrict.test.off.js │ ├── addressShares.test.off.alt.js │ ├── addressShares.test.alt.js │ ├── totalShares.test.js │ ├── totalPooledEth.test.js │ ├── config.js │ ├── globalSetup.js │ ├── reporter.js │ ├── allTransferEntitiesExist.test.off.js │ ├── addressShares.test.js │ ├── addressShares.test.off.js │ ├── rewards.test.off.js │ ├── graphBalance.test.off.alt.js │ ├── noTxNegativeValues.test.js │ ├── balances.test.js │ ├── graphBalance.test.off.js │ ├── noWrongDust.test.js │ ├── rewardsBalanceChanges.test.off.js │ └── smoke.test.js ├── matchstick.yaml ├── snippets-python │ ├── requirements.txt │ ├── README.md │ └── rewardBalances.py ├── Paw.paw ├── .prettierrc.json ├── tsconfig.json ├── renovate.json ├── jest.config.js ├── src │ ├── contracts.ts │ ├── constants.ts │ ├── utils.ts │ ├── Voting.ts │ ├── NodeOperatorsRegistry.ts │ ├── Easytrack.ts │ └── DepositSecurityModule.ts ├── .env.local.example ├── .env.mainnet.example ├── .env.testnet.example ├── LICENSE.txt ├── README.md ├── package.json └── abis │ └── Billing.json ├── topholders-subgraph ├── schema.graphql ├── tsconfig.json ├── networks.json ├── subgraph.yaml ├── package.json ├── src │ └── contract.ts └── tests │ ├── contract.test.ts │ └── contract-utils.ts ├── each_block_handler_example ├── networks.json ├── tsconfig.json ├── types │ └── schema.graphql ├── subgraph.yaml ├── package.json ├── tests │ ├── contract.test.ts │ └── contract-utils.ts └── src │ └── contract.ts ├── pepe-subgraph ├── tsconfig.json ├── networks.json ├── package.json ├── schema.graphql ├── subgraph.yaml ├── src │ └── contract.ts └── tests │ ├── contract.test.ts │ └── contract-utils.ts ├── friendtech-by-chainstack ├── tsconfig.json ├── networks.json ├── schema.graphql ├── package.json ├── subgraph.yaml ├── src │ └── friendtech-shares-v-1.ts ├── tests │ ├── friendtech-shares-v-1.test.ts │ └── friendtech-shares-v-1-utils.ts └── abis │ └── FriendtechSharesV1.json ├── tornadocash-subgraph ├── tsconfig.json ├── networks.json ├── schema.graphql ├── package.json ├── subgraph.yaml ├── src │ └── tornado-contract.ts └── tests │ ├── tornado-contract-utils.ts │ └── tornado-contract.test.ts ├── erc20_historical_total_supply ├── tsconfig.json ├── schema.graphql ├── networks.json ├── subgraph.yaml ├── package.json ├── src │ └── contract.ts └── tests │ ├── contract.test.ts │ └── contract-utils.ts ├── top_holders_updater_example ├── tsconfig.json ├── networks.json ├── schema.graphql ├── subgraph.yaml ├── package.json ├── tests │ ├── contract.test.ts │ └── contract-utils.ts └── src │ └── contract.ts ├── LICENSE ├── .gitignore └── README.md /lido-subgraph/tests-unit/.gitignore: -------------------------------------------------------------------------------- 1 | .bin 2 | .latest.json -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.json -------------------------------------------------------------------------------- /lido-subgraph/tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/utils.js: -------------------------------------------------------------------------------- 1 | export * from './utils/index.js' 2 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/utils/BigNumber.js: -------------------------------------------------------------------------------- 1 | export { BigNumber } from 'ethers' 2 | -------------------------------------------------------------------------------- /lido-subgraph/matchstick.yaml: -------------------------------------------------------------------------------- 1 | testsFolder: tests-unit 2 | manifestPath: subgraph.testnet.yaml 3 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-python/requirements.txt: -------------------------------------------------------------------------------- 1 | web3==5.28.0 2 | gql==3.0.0 3 | requests-toolbelt==0.9.1 -------------------------------------------------------------------------------- /lido-subgraph/Paw.paw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balakhonoff/awesome-subgraphs/HEAD/lido-subgraph/Paw.paw -------------------------------------------------------------------------------- /lido-subgraph/snippets-python/README.md: -------------------------------------------------------------------------------- 1 | ## Install Dependencies 2 | 3 | pip3 install -r requirements.txt 4 | -------------------------------------------------------------------------------- /topholders-subgraph/schema.graphql: -------------------------------------------------------------------------------- 1 | type Balance @entity { 2 | id: Bytes! 3 | value: BigInt! # uint256 4 | } 5 | -------------------------------------------------------------------------------- /lido-subgraph/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "jsxSingleQuote": true, 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /each_block_handler_example/networks.json: -------------------------------------------------------------------------------- 1 | {"avalanche": {"Contract": {"address": "0xdac17f958d2ee523a2206206994597c13d831ec7"}}} -------------------------------------------------------------------------------- /lido-subgraph/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /pepe-subgraph/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src", "tests"] 4 | } 5 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/BigNumber.js: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers' 2 | 3 | const BigNumber = ethers.BigNumber 4 | 5 | export { BigNumber } 6 | -------------------------------------------------------------------------------- /topholders-subgraph/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src", "tests"] 4 | } 5 | -------------------------------------------------------------------------------- /friendtech-by-chainstack/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src", "tests"] 4 | } 5 | -------------------------------------------------------------------------------- /tornadocash-subgraph/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src", "tests"] 4 | } 5 | -------------------------------------------------------------------------------- /each_block_handler_example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src", "tests"] 4 | } 5 | -------------------------------------------------------------------------------- /erc20_historical_total_supply/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src", "tests"] 4 | } 5 | -------------------------------------------------------------------------------- /top_holders_updater_example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src", "tests"] 4 | } 5 | -------------------------------------------------------------------------------- /erc20_historical_total_supply/schema.graphql: -------------------------------------------------------------------------------- 1 | type TotalSupply @entity(immutable: true) { 2 | id: Bytes! 3 | value: BigInt! 4 | blockNumber: BigInt! 5 | } 6 | -------------------------------------------------------------------------------- /each_block_handler_example/types/schema.graphql: -------------------------------------------------------------------------------- 1 | type Block @entity { 2 | id: ID! 3 | blockNumber: BigInt! 4 | timestamp: BigInt! 5 | parentHash: Bytes! 6 | } 7 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/utils/index.js: -------------------------------------------------------------------------------- 1 | export * from './Big.js' 2 | export * from './BigNumber.js' 3 | export * from './ethCalls.js' 4 | export * from './subgraphFetcher.js' 5 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/queries/index.js: -------------------------------------------------------------------------------- 1 | export * from './Submissions.js' 2 | export * from './TotalReward.js' 3 | export * from './TransferIn.js' 4 | export * from './TransferOut.js' 5 | -------------------------------------------------------------------------------- /pepe-subgraph/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "Contract": { 4 | "address": "0x6982508145454Ce325dDbE47a25d4ec3d2311933", 5 | "startBlock": 17046105 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /topholders-subgraph/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "Contract": { 4 | "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", 5 | "startBlock": 4634747 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /lido-subgraph/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["local>lidofinance/renovate-config"], 4 | "assignees": ["kolyasapphire"] 5 | } 6 | -------------------------------------------------------------------------------- /erc20_historical_total_supply/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "Contract": { 4 | "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", 5 | "startBlock": 1000000 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /top_holders_updater_example/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "Contract": { 4 | "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", 5 | "startBlock": 17612746 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /tornadocash-subgraph/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "TornadoContract": { 4 | "address": "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF", 5 | "startBlock": 17000000 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /friendtech-by-chainstack/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "base": { 3 | "FriendtechSharesV1": { 4 | "address": "0xCF205808Ed36593aa40a44F10c7f7C2F67d4A4d4", 5 | "startBlock": 2430439 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /top_holders_updater_example/schema.graphql: -------------------------------------------------------------------------------- 1 | type Balance @entity { 2 | id: Bytes! 3 | value: BigInt! # uint256 4 | blockNumber: BigInt! 5 | blockTimestamp: BigInt! 6 | transactionHash: Bytes! 7 | } 8 | -------------------------------------------------------------------------------- /lido-subgraph/tests/isNotErrored.test.js: -------------------------------------------------------------------------------- 1 | import { getIfIndexingErrored } from './utils/index.js' 2 | 3 | test('isNotErrored', async () => { 4 | const hasErrors = await getIfIndexingErrored() 5 | expect(hasErrors).toBe(false) 6 | }) 7 | -------------------------------------------------------------------------------- /lido-subgraph/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: {}, 3 | setupFiles: ['dotenv/config'], 4 | setupFilesAfterEnv: ['./tests/globalSetup.js'], 5 | reporters: ['./tests/reporter.js'], 6 | testMatch: ['**/tests/*.test.[jt]s'], 7 | } 8 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/utils/subgraphFetcher.js: -------------------------------------------------------------------------------- 1 | import { request, gql } from 'graphql-request' 2 | import { GRAPH, GRAPH_MONITORING } from '../config.js' 3 | 4 | export const subgraphFetch = async (query, vars = {}, monitoring = false) => 5 | await request(monitoring ? GRAPH_MONITORING : GRAPH, query, vars) 6 | 7 | export { gql } 8 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/config.js: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv' 2 | 3 | dotenv.config({ path: '../.env' }) 4 | 5 | export const ADDRESS = process.env.ADDRESS 6 | 7 | export const RPC = process.env.RPC 8 | export const GRAPH = process.env.GRAPH 9 | export const GRAPH_MONITORING = process.env.GRAPH_MONITORING 10 | export const LIDO_ADDRESS = process.env.LIDO_ADDRESS 11 | -------------------------------------------------------------------------------- /lido-subgraph/tests/allOracleReportsExist.test.js: -------------------------------------------------------------------------------- 1 | import { getOracleEventNumber, getEntityCount } from './utils/index.js' 2 | 3 | test('allOracleReportsExist', async () => { 4 | const ethNumber = await getOracleEventNumber('Completed') 5 | const subgraphNumber = await getEntityCount('oracleCompleteds') 6 | 7 | expect(subgraphNumber).toEqual(ethNumber) 8 | }, 30000) // very long on testnet 9 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/queries/TotalReward.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | 3 | export const totalRewardQuery = gql` 4 | { 5 | totalRewards(first: 1000) { 6 | totalPooledEtherBefore 7 | totalPooledEtherAfter 8 | totalSharesBefore 9 | totalSharesAfter 10 | 11 | block 12 | blockTime 13 | logIndex 14 | } 15 | } 16 | ` 17 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/utils/Big.js: -------------------------------------------------------------------------------- 1 | import Big from 'big.js' 2 | 3 | // Separate instance for displaying ETH 4 | const BigDecimal = Big() 5 | 6 | Big.DP = 0 7 | BigDecimal.DP = 18 8 | 9 | Big.RM = Big.roundDown 10 | BigDecimal.RM = Big.roundHalfUp 11 | 12 | Big.NE = -70000000 13 | BigDecimal.NE = -70000000 14 | Big.PE = 210000000 15 | BigDecimal.PE = 210000000 16 | 17 | export { Big, BigDecimal } 18 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/checkIfLimited.js: -------------------------------------------------------------------------------- 1 | import { gql, request } from 'graphql-request' 2 | import { GRAPH } from '../config.js' 3 | 4 | const query = gql` 5 | query { 6 | lidoTransfers(skip: 5001) { 7 | id 8 | } 9 | } 10 | ` 11 | 12 | export const checkIfLimited = async () => { 13 | try { 14 | await request(GRAPH, query) 15 | return false 16 | } catch { 17 | return true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/loadAddressShares.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { subgraphFetch } from './index.js' 3 | 4 | export const loadAddressShares = async (address) => { 5 | const query = gql` 6 | query ($block: Block_height) { 7 | shares(id: "${address}", block: $block) { 8 | shares 9 | } 10 | } 11 | ` 12 | 13 | return (await subgraphFetch(query)).shares.shares 14 | } 15 | -------------------------------------------------------------------------------- /lido-subgraph/src/contracts.ts: -------------------------------------------------------------------------------- 1 | import { Lido } from '../generated/Lido/Lido' 2 | import { NodeOperatorsRegistry } from '../generated/NodeOperatorsRegistry/NodeOperatorsRegistry' 3 | 4 | import { getAddress } from './constants' 5 | 6 | export const loadLidoContract = (): Lido => Lido.bind(getAddress('Lido')) 7 | 8 | export const loadNosContract = (): NodeOperatorsRegistry => 9 | NodeOperatorsRegistry.bind(getAddress('NodeOperatorsRegistry')) 10 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/getTotals.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { subgraphFetch } from './index.js' 3 | 4 | // We can fetch only 1000 entities in one request 5 | const totalsQuery = gql` 6 | query ($block: Block_height) { 7 | totals(id: "", block: $block) { 8 | totalPooledEther 9 | totalShares 10 | } 11 | } 12 | ` 13 | 14 | export const getTotals = async () => (await subgraphFetch(totalsQuery)).totals 15 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/existingKeysCheck.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql } from './utils.js' 2 | 3 | const pubKey = '0x123' 4 | 5 | const query = gql` 6 | { 7 | nodeOperatorSigningKeys(where: { pubkey: "${pubKey}" }) { 8 | id 9 | operatorId 10 | pubkey 11 | } 12 | } 13 | ` 14 | 15 | const keys = (await subgraphFetch(query)).nodeOperatorSigningKeys 16 | 17 | keys.length > 0 18 | ? console.log('Key already exists') 19 | : console.log("Key doesn't exist yet") 20 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/getEntityCount.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { subgraphFetch } from './index.js' 3 | 4 | const genQuery = (entityName) => gql` 5 | query ($first: Int, $skip: Int, $block: Block_height) { 6 | ${entityName}(first: $first, skip: $skip, block: $block, orderBy: block, orderDirection: desc) { 7 | id 8 | } 9 | } 10 | ` 11 | 12 | export const getEntityCount = async (entityName) => 13 | (await subgraphFetch(genQuery(entityName)))[entityName].length 14 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/index.js: -------------------------------------------------------------------------------- 1 | export * from './BigNumber.js' 2 | export * from './calculateAddressBalance.js' 3 | export * from './calculateShares.js' 4 | export * from './calculateSharesCeiling.js' 5 | export * from './ethCalls.js' 6 | export * from './getTestAddresses.js' 7 | export * from './getTotals.js' 8 | export * from './loadAddressShares.js' 9 | export * from './subgraphFetch.js' 10 | export * from './getStatus.js' 11 | export * from './getEntityCount.js' 12 | export * from './checkIfLimited.js' 13 | -------------------------------------------------------------------------------- /lido-subgraph/tests/isSynced.test.js: -------------------------------------------------------------------------------- 1 | import { jest } from '@jest/globals' 2 | import { ethCall, getLastIndexedBlock } from './utils/index.js' 3 | 4 | jest.setTimeout(20000) 5 | 6 | jest.retryTimes(3) 7 | 8 | test('isSyncedLoose', async () => { 9 | const currentBlock = parseInt((await ethCall('getBlock', 'latest')).number) 10 | const acceptedMinimum = currentBlock - 50 11 | 12 | const subgraphBlock = parseInt(await getLastIndexedBlock()) 13 | 14 | expect(subgraphBlock).toBeGreaterThanOrEqual(acceptedMinimum) 15 | }) 16 | -------------------------------------------------------------------------------- /lido-subgraph/tests/isSyncedStrict.test.off.js: -------------------------------------------------------------------------------- 1 | import { jest } from '@jest/globals' 2 | import { ethCall, getLastIndexedBlock } from './utils/index.js' 3 | 4 | jest.setTimeout(20000) 5 | 6 | jest.retryTimes(3) 7 | 8 | test('isSynced', async () => { 9 | const currentBlock = parseInt((await ethCall('getBlock', 'latest')).number) 10 | const acceptedMinimum = currentBlock - 2 11 | 12 | const subgraphBlock = parseInt(await getLastIndexedBlock()) 13 | 14 | expect(subgraphBlock).toBeGreaterThanOrEqual(acceptedMinimum) 15 | }) 16 | -------------------------------------------------------------------------------- /lido-subgraph/.env.local.example: -------------------------------------------------------------------------------- 1 | # Testing 2 | RPC=http://127.0.0.1:8545 3 | GRAPH=http://127.0.0.1:8000/subgraphs/name/lidofinance/lido 4 | LIDO_ADDRESS=0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 5 | NOP_ADDRESS=0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5 6 | ARAGON_ADDRESS=0x2e59A20f205bB85a89C53f1936454680651E618e 7 | EASYTRACK_ADDRESS=0xF0211b7660680B49De1A7E9f25C65660F0a13Fea 8 | DSM_ADDRESS=0xDb149235B6F40dC08810AA69869783Be101790e7 9 | 10 | # Deployment 11 | GRAPH_NODE=http://127.0.0.1:8020 12 | GRAPH_IPFS=http://127.0.0.1:5001 13 | -------------------------------------------------------------------------------- /lido-subgraph/tests/addressShares.test.off.alt.js: -------------------------------------------------------------------------------- 1 | import { getTestAddresses, getAddressShares, calculateShares } from './utils' 2 | 3 | // 1 addr 1 test version 4 | const ADDRESSES_TO_TEST = 100 5 | const addresses = await getTestAddresses(ADDRESSES_TO_TEST) 6 | 7 | test.each(addresses)('%j', async (address) => { 8 | const realShareAmount = (await getAddressShares(address)).toString() 9 | const subgraphShareAmount = (await calculateShares(address)).toString() 10 | 11 | expect(subgraphShareAmount).toEqual(realShareAmount) 12 | }) 13 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/largestDust.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql, Big } from './utils.js' 2 | 3 | const query = gql` 4 | { 5 | totalRewards(first: 1000) { 6 | dust 7 | } 8 | } 9 | ` 10 | 11 | const totalRewards = (await subgraphFetch(query)).totalRewards 12 | 13 | let largestDust = Big(0) 14 | 15 | for (let report of totalRewards) { 16 | const dust = Big(report.dust) 17 | 18 | if (dust.gt(largestDust)) { 19 | largestDust = dust 20 | } 21 | } 22 | 23 | console.log('Largest dust:', largestDust.toString()) 24 | -------------------------------------------------------------------------------- /lido-subgraph/.env.mainnet.example: -------------------------------------------------------------------------------- 1 | # Testing 2 | RPC=http://provider.com 3 | GRAPH=https://api.thegraph.com/subgraphs/name/lidofinance/lido 4 | LIDO_ADDRESS=0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 5 | NOP_ADDRESS=0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5 6 | ARAGON_ADDRESS=0x2e59A20f205bB85a89C53f1936454680651E618e 7 | EASYTRACK_ADDRESS=0xF0211b7660680B49De1A7E9f25C65660F0a13Fea 8 | DSM_ADDRESS=0xDb149235B6F40dC08810AA69869783Be101790e7 9 | 10 | # Deployment 11 | GRAPH_NODE=https://api.thegraph.com/deploy/ 12 | GRAPH_IPFS=https://api.thegraph.com/ipfs/ 13 | -------------------------------------------------------------------------------- /lido-subgraph/.env.testnet.example: -------------------------------------------------------------------------------- 1 | # Testing 2 | RPC=http://provider.com 3 | GRAPH=https://api.thegraph.com/subgraphs/name/lidofinance/lido-testnet 4 | LIDO_ADDRESS=0x1643E812aE58766192Cf7D2Cf9567dF2C37e9B7F 5 | NOP_ADDRESS=0x9D4AF1Ee19Dad8857db3a45B0374c81c8A1C6320 6 | ARAGON_ADDRESS=0xbc0B67b4553f4CF52a913DE9A6eD0057E2E758Db 7 | EASYTRACK_ADDRESS=0xAf072C8D368E4DD4A9d4fF6A76693887d6ae92Af 8 | DSM_ADDRESS=0xEd23AD3EA5Fb9d10e7371Caef1b141AD1C23A80c 9 | 10 | # Deployment 11 | GRAPH_NODE=https://api.thegraph.com/deploy/ 12 | GRAPH_IPFS=https://api.thegraph.com/ipfs/ 13 | -------------------------------------------------------------------------------- /lido-subgraph/tests/addressShares.test.alt.js: -------------------------------------------------------------------------------- 1 | import { 2 | getTestAddresses, 3 | getAddressShares, 4 | calculateShares, 5 | } from './utils/index.js' 6 | 7 | // 1 addr 1 test version 8 | const ADDRESSES_TO_TEST = 100 9 | const addresses = await getTestAddresses(ADDRESSES_TO_TEST) 10 | 11 | test.each(addresses)('%j', async (address) => { 12 | const realShareAmount = (await getAddressShares(address)).toString() 13 | const subgraphShareAmount = (await calculateShares(address)).toString() 14 | 15 | expect(subgraphShareAmount).toEqual(realShareAmount) 16 | }) 17 | -------------------------------------------------------------------------------- /lido-subgraph/tests/totalShares.test.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { lidoFuncCall, subgraphFetch } from './utils/index.js' 3 | 4 | const query = gql` 5 | query ($block: Block_height) { 6 | totals(id: "", block: $block) { 7 | totalShares 8 | } 9 | } 10 | ` 11 | 12 | test('totalShares', async () => { 13 | const realTotalShares = (await lidoFuncCall('getTotalShares')).toString() 14 | const subgraphTotalShares = (await subgraphFetch(query)).totals.totalShares 15 | 16 | expect(subgraphTotalShares).toEqual(realTotalShares) 17 | }) 18 | -------------------------------------------------------------------------------- /lido-subgraph/tests/totalPooledEth.test.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { lidoFuncCall, subgraphFetch } from './utils/index.js' 3 | 4 | const query = gql` 5 | query ($block: Block_height) { 6 | totals(id: "", block: $block) { 7 | totalPooledEther 8 | } 9 | } 10 | ` 11 | 12 | test('totalPooledEther', async () => { 13 | const realTotalShares = (await lidoFuncCall('getTotalPooledEther')).toString() 14 | const subgraphTotalShares = (await subgraphFetch(query)).totals 15 | .totalPooledEther 16 | 17 | expect(subgraphTotalShares).toEqual(realTotalShares) 18 | }) 19 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/getStatus.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { subgraphFetch } from './index.js' 3 | 4 | const statusQuery = gql` 5 | query { 6 | _meta { 7 | block { 8 | number 9 | hash 10 | } 11 | deployment 12 | hasIndexingErrors 13 | } 14 | } 15 | ` 16 | 17 | export const getStatus = async () => (await subgraphFetch(statusQuery))._meta 18 | 19 | export const getLastIndexedBlock = async () => (await getStatus()).block.number 20 | 21 | export const getIfIndexingErrored = async () => 22 | (await getStatus()).hasIndexingErrors 23 | -------------------------------------------------------------------------------- /tornadocash-subgraph/schema.graphql: -------------------------------------------------------------------------------- 1 | type Deposit @entity(immutable: true) { 2 | id: Bytes! 3 | from_: Bytes! 4 | value_: BigInt! 5 | commitment: Bytes! # bytes32 6 | leafIndex: BigInt! # uint32 7 | timestamp: BigInt! # uint256 8 | blockNumber: BigInt! 9 | blockTimestamp: BigInt! 10 | transactionHash: Bytes! 11 | } 12 | 13 | type Withdrawal @entity(immutable: true) { 14 | id: Bytes! 15 | to: Bytes! # address 16 | nullifierHash: Bytes! # bytes32 17 | relayer: Bytes! # address 18 | fee: BigInt! # uint256 19 | blockNumber: BigInt! 20 | blockTimestamp: BigInt! 21 | transactionHash: Bytes! 22 | } 23 | -------------------------------------------------------------------------------- /each_block_handler_example/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | schema: 3 | file: ./types/schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: Contract 7 | network: avalanche 8 | source: 9 | address: '0xdac17f958d2ee523a2206206994597c13d831ec7' 10 | abi: Contract 11 | startBlock: 32545225 12 | mapping: 13 | kind: ethereum/events 14 | apiVersion: 0.0.7 15 | language: wasm/assemblyscript 16 | entities: 17 | - Block 18 | abis: 19 | - name: Contract 20 | file: ./abis/Contract.json 21 | blockHandlers: 22 | - handler: handleBlock 23 | file: ./src/contract.ts 24 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/removeContracts.js: -------------------------------------------------------------------------------- 1 | import { ethCall } from './utils.js' 2 | 3 | import fs from 'fs' 4 | 5 | const withoutContracts = new Set() 6 | const withContracts = await fs.promises.readFile('withContracts.json') 7 | 8 | for (const adr of JSON.parse(withContracts)) { 9 | if ((await ethCall('getCode', adr)) === '0x') { 10 | withoutContracts.add(adr) 11 | } else { 12 | process.stdout.write('x') 13 | } 14 | } 15 | 16 | console.log(withoutContracts.size, 'non-contract addresses of stETH holders') 17 | 18 | await fs.promises.writeFile( 19 | 'nonContracts.json', 20 | JSON.stringify(Array.from(withoutContracts)) 21 | ) 22 | -------------------------------------------------------------------------------- /lido-subgraph/tests/config.js: -------------------------------------------------------------------------------- 1 | export const RPC = process.env.RPC 2 | export const GRAPH = process.env.GRAPH 3 | export const LIDO_ADDRESS = process.env.LIDO_ADDRESS 4 | export const NOP_ADDRESS = process.env.NOP_ADDRESS 5 | export const ARAGON_ADDRESS = process.env.ARAGON_ADDRESS 6 | export const EASYTRACK_ADDRESS = process.env.EASYTRACK_ADDRESS 7 | export const DSM_ADDRESS = process.env.DSM_ADDRESS 8 | 9 | export const getBlock = () => parseInt(process.env.BLOCK) 10 | export const getNetwork = () => process.env.NETWORK 11 | export const getIsMainnet = () => getNetwork() === 'mainnet' 12 | export const getIsLimited = () => process.env.LIMITED === 'true' 13 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/queries/Submissions.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | 3 | export const submissionsQuery = gql` 4 | query LidoSubmissions($address: String!) { 5 | lidoSubmissions(first: 1000, where: { sender: $address }) { 6 | sender 7 | amount 8 | 9 | shares 10 | sharesBefore 11 | sharesAfter 12 | 13 | totalPooledEtherBefore 14 | totalPooledEtherAfter 15 | totalSharesBefore 16 | totalSharesAfter 17 | 18 | balanceAfter 19 | 20 | block 21 | blockTime 22 | transactionHash 23 | transactionIndex 24 | logIndex 25 | transactionLogIndex 26 | } 27 | } 28 | ` 29 | -------------------------------------------------------------------------------- /lido-subgraph/tests/globalSetup.js: -------------------------------------------------------------------------------- 1 | import { GRAPH, RPC } from './config.js' 2 | import { 3 | getLastIndexedBlock, 4 | getRpcNetwork, 5 | checkIfLimited, 6 | } from './utils/index.js' 7 | 8 | if (GRAPH) { 9 | process.env.BLOCK = await getLastIndexedBlock() 10 | process.env.LIMITED = await checkIfLimited() 11 | } else { 12 | console.info( 13 | 'BLOCK and LIMITED env was not set as there is no GRAPH provided.' 14 | ) 15 | } 16 | 17 | if (RPC) { 18 | const networkName = (await getRpcNetwork()).name 19 | process.env.NETWORK = networkName === 'homestead' ? 'mainnet' : networkName 20 | } else { 21 | console.info('NETWORK env was not set as there is no RPC provided.') 22 | } 23 | -------------------------------------------------------------------------------- /friendtech-by-chainstack/schema.graphql: -------------------------------------------------------------------------------- 1 | type OwnershipTransferred @entity(immutable: true) { 2 | id: Bytes! 3 | previousOwner: Bytes! # address 4 | newOwner: Bytes! # address 5 | blockNumber: BigInt! 6 | blockTimestamp: BigInt! 7 | transactionHash: Bytes! 8 | } 9 | 10 | type Trade @entity(immutable: true) { 11 | id: Bytes! 12 | trader: Bytes! # address 13 | subject: Bytes! # address 14 | isBuy: Boolean! # bool 15 | shareAmount: BigInt! # uint256 16 | ethAmount: BigInt! # uint256 17 | protocolEthAmount: BigInt! # uint256 18 | subjectEthAmount: BigInt! # uint256 19 | supply: BigInt! # uint256 20 | blockNumber: BigInt! 21 | blockTimestamp: BigInt! 22 | transactionHash: Bytes! 23 | } 24 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/findFrequentStakers.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql } from './utils.js' 2 | 3 | const userTransfersQuery = gql` 4 | { 5 | lidoTransfers(first: 1000, skip: 1000) { 6 | from 7 | to 8 | } 9 | } 10 | ` 11 | 12 | const transfers = (await subgraphFetch(userTransfersQuery)).lidoTransfers 13 | 14 | const grouped = transfers.reduce((a, b) => { 15 | var i = a.findIndex((x) => x.to === b.to) 16 | return i === -1 ? a.push({ to: b.to, times: 1 }) : a[i].times++, a 17 | }, []) 18 | 19 | const sorted = grouped.sort((a, b) => b.times - a.times) 20 | 21 | const withAdequateAmount = sorted.filter((x) => x.times > 1 && x.times < 5) 22 | 23 | console.log(withAdequateAmount) 24 | -------------------------------------------------------------------------------- /pepe-subgraph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pepe", 3 | "license": "UNLICENSED", 4 | "scripts": { 5 | "codegen": "graph codegen", 6 | "build": "graph build", 7 | "deploy": "graph deploy --node https://api.thegraph.com/deploy/ pepe", 8 | "create-local": "graph create --node http://localhost:8020/ pepe", 9 | "remove-local": "graph remove --node http://localhost:8020/ pepe", 10 | "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 pepe", 11 | "test": "graph test" 12 | }, 13 | "dependencies": { 14 | "@graphprotocol/graph-cli": "0.49.0", 15 | "@graphprotocol/graph-ts": "0.29.1" 16 | }, 17 | "devDependencies": { "matchstick-as": "0.5.0" } 18 | } 19 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/reportSharesOfAddr.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql, lidoFuncCall } from './utils.js' 2 | import { ADDRESS } from './config.js' 3 | 4 | const query = gql` 5 | { 6 | oracleCompleteds(first: 1000, orderBy: block, orderDirection: asc) { 7 | block 8 | blockTime 9 | } 10 | } 11 | ` 12 | 13 | const oracleReports = (await subgraphFetch(query)).oracleCompleteds 14 | 15 | for (let report of oracleReports) { 16 | const balance = await lidoFuncCall('sharesOf', ADDRESS, { 17 | blockTag: parseInt(report.block), 18 | }) 19 | const humanTime = new Date(report.blockTime * 1000).toLocaleDateString( 20 | 'ru-RU' 21 | ) 22 | console.log(humanTime, balance.toString()) 23 | } 24 | -------------------------------------------------------------------------------- /topholders-subgraph/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: Contract 7 | network: mainnet 8 | source: 9 | address: "0xdac17f958d2ee523a2206206994597c13d831ec7" 10 | abi: Contract 11 | startBlock: 4634747 12 | mapping: 13 | kind: ethereum/events 14 | apiVersion: 0.0.7 15 | language: wasm/assemblyscript 16 | entities: 17 | - Transfer 18 | abis: 19 | - name: Contract 20 | file: ./abis/Contract.json 21 | eventHandlers: 22 | - event: Transfer(indexed address,indexed address,uint256) 23 | handler: handleTransfer 24 | file: ./src/contract.ts 25 | -------------------------------------------------------------------------------- /top_holders_updater_example/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: Contract 7 | network: mainnet 8 | source: 9 | address: "0xdac17f958d2ee523a2206206994597c13d831ec7" 10 | abi: Contract 11 | startBlock: 17619998 12 | mapping: 13 | kind: ethereum/events 14 | apiVersion: 0.0.7 15 | language: wasm/assemblyscript 16 | entities: 17 | - Balance 18 | abis: 19 | - name: Contract 20 | file: ./abis/Contract.json 21 | eventHandlers: 22 | - event: Transfer(indexed address,indexed address,uint256) 23 | handler: handleTransfer 24 | file: ./src/contract.ts 25 | -------------------------------------------------------------------------------- /erc20_historical_total_supply/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: Contract 7 | network: mainnet 8 | source: 9 | address: "0xdac17f958d2ee523a2206206994597c13d831ec7" 10 | abi: Contract 11 | startBlock: 4634747 12 | mapping: 13 | kind: ethereum/events 14 | apiVersion: 0.0.7 15 | language: wasm/assemblyscript 16 | entities: 17 | - TotalSupply 18 | abis: 19 | - name: Contract 20 | file: ./abis/Contract.json 21 | eventHandlers: 22 | - event: Transfer(indexed address,indexed address,uint256) 23 | handler: handleTransfer 24 | file: ./src/contract.ts 25 | -------------------------------------------------------------------------------- /topholders-subgraph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "topholders", 3 | "license": "UNLICENSED", 4 | "scripts": { 5 | "codegen": "graph codegen", 6 | "build": "graph build", 7 | "deploy": "graph deploy --node https://api.thegraph.com/deploy/ topholders", 8 | "create-local": "graph create --node http://localhost:8020/ topholders", 9 | "remove-local": "graph remove --node http://localhost:8020/ topholders", 10 | "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 topholders", 11 | "test": "graph test" 12 | }, 13 | "dependencies": { 14 | "@graphprotocol/graph-cli": "0.49.0", 15 | "@graphprotocol/graph-ts": "0.29.1" 16 | }, 17 | "devDependencies": { "matchstick-as": "0.5.0" } 18 | } 19 | -------------------------------------------------------------------------------- /lido-subgraph/tests/reporter.js: -------------------------------------------------------------------------------- 1 | import { VerboseReporter } from '@jest/reporters' 2 | 3 | /** 4 | This reporter will output console.log only when a test fails. 5 | For example, when shares test fails, we will see the address with wrong shares. 6 | Also very useful for tests debugging. 7 | **/ 8 | 9 | class Reporter extends VerboseReporter { 10 | constructor() { 11 | super(...arguments) 12 | } 13 | 14 | printTestFileHeader(_testPath, _config, result) { 15 | const console = result.console 16 | 17 | if (result.numFailingTests === 0 && !result.testExecError) { 18 | result.console = null 19 | } 20 | 21 | super.printTestFileHeader(...arguments) 22 | 23 | result.console = console 24 | } 25 | } 26 | 27 | export default Reporter 28 | -------------------------------------------------------------------------------- /pepe-subgraph/schema.graphql: -------------------------------------------------------------------------------- 1 | type Approval @entity(immutable: true) { 2 | id: Bytes! 3 | owner: Bytes! # address 4 | spender: Bytes! # address 5 | value: BigInt! # uint256 6 | blockNumber: BigInt! 7 | blockTimestamp: BigInt! 8 | transactionHash: Bytes! 9 | } 10 | 11 | type OwnershipTransferred @entity(immutable: true) { 12 | id: Bytes! 13 | previousOwner: Bytes! # address 14 | newOwner: Bytes! # address 15 | blockNumber: BigInt! 16 | blockTimestamp: BigInt! 17 | transactionHash: Bytes! 18 | } 19 | 20 | type Transfer @entity(immutable: true) { 21 | id: Bytes! 22 | from: Bytes! # address 23 | to: Bytes! # address 24 | value: BigInt! # uint256 25 | blockNumber: BigInt! 26 | blockTimestamp: BigInt! 27 | transactionHash: Bytes! 28 | } 29 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/queries/TransferIn.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | 3 | export const transferInQuery = gql` 4 | query LidoTransfersInbound($address: String!) { 5 | lidoTransfers(first: 1000, where: { to: $address }) { 6 | from 7 | to 8 | value 9 | 10 | shares 11 | sharesBeforeDecrease 12 | sharesAfterDecrease 13 | sharesBeforeIncrease 14 | sharesAfterIncrease 15 | 16 | totalPooledEther 17 | totalShares 18 | 19 | balanceAfterDecrease 20 | balanceAfterIncrease 21 | 22 | mintWithoutSubmission 23 | 24 | block 25 | blockTime 26 | transactionHash 27 | transactionIndex 28 | logIndex 29 | transactionLogIndex 30 | } 31 | } 32 | ` 33 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/queries/TransferOut.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | 3 | export const transferOutQuery = gql` 4 | query LidoTransfersInbound($address: String!) { 5 | lidoTransfers(first: 1000, where: { from: $address }) { 6 | from 7 | to 8 | value 9 | 10 | shares 11 | sharesBeforeDecrease 12 | sharesAfterDecrease 13 | sharesBeforeIncrease 14 | sharesAfterIncrease 15 | 16 | totalPooledEther 17 | totalShares 18 | 19 | balanceAfterDecrease 20 | balanceAfterIncrease 21 | 22 | mintWithoutSubmission 23 | 24 | block 25 | blockTime 26 | transactionHash 27 | transactionIndex 28 | logIndex 29 | transactionLogIndex 30 | } 31 | } 32 | ` 33 | -------------------------------------------------------------------------------- /tornadocash-subgraph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tornado_subgraph", 3 | "license": "UNLICENSED", 4 | "scripts": { 5 | "codegen": "graph codegen", 6 | "build": "graph build", 7 | "deploy": "graph deploy --node https://api.thegraph.com/deploy/ tornado_subgraph", 8 | "create-local": "graph create --node http://localhost:8020/ tornado_subgraph", 9 | "remove-local": "graph remove --node http://localhost:8020/ tornado_subgraph", 10 | "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 tornado_subgraph", 11 | "test": "graph test" 12 | }, 13 | "dependencies": { 14 | "@graphprotocol/graph-cli": "0.49.0", 15 | "@graphprotocol/graph-ts": "0.29.1" 16 | }, 17 | "devDependencies": { "matchstick-as": "0.5.0" } 18 | } 19 | -------------------------------------------------------------------------------- /each_block_handler_example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "each_block_subgraph", 3 | "license": "UNLICENSED", 4 | "scripts": { 5 | "codegen": "graph codegen", 6 | "build": "graph build", 7 | "deploy": "graph deploy --node https://api.thegraph.com/deploy/ each_block_subgraph", 8 | "create-local": "graph create --node http://localhost:8020/ each_block_subgraph", 9 | "remove-local": "graph remove --node http://localhost:8020/ each_block_subgraph", 10 | "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 each_block_subgraph", 11 | "test": "graph test" 12 | }, 13 | "dependencies": { 14 | "@graphprotocol/graph-cli": "0.49.0", 15 | "@graphprotocol/graph-ts": "0.29.1" 16 | }, 17 | "devDependencies": { "matchstick-as": "0.5.0" } 18 | } 19 | -------------------------------------------------------------------------------- /top_holders_updater_example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "top_holders_updater", 3 | "license": "UNLICENSED", 4 | "scripts": { 5 | "codegen": "graph codegen", 6 | "build": "graph build", 7 | "deploy": "graph deploy --node https://api.thegraph.com/deploy/ top_holders_updater", 8 | "create-local": "graph create --node http://localhost:8020/ top_holders_updater", 9 | "remove-local": "graph remove --node http://localhost:8020/ top_holders_updater", 10 | "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 top_holders_updater", 11 | "test": "graph test" 12 | }, 13 | "dependencies": { 14 | "@graphprotocol/graph-cli": "0.49.0", 15 | "@graphprotocol/graph-ts": "0.29.1" 16 | }, 17 | "devDependencies": { "matchstick-as": "0.5.0" } 18 | } 19 | -------------------------------------------------------------------------------- /friendtech-by-chainstack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "friendtech-by-chainstack", 3 | "license": "UNLICENSED", 4 | "scripts": { 5 | "codegen": "graph codegen", 6 | "build": "graph build", 7 | "deploy": "graph deploy --node https://api.studio.thegraph.com/deploy/ friendtech-by-chainstack", 8 | "create-local": "graph create --node http://localhost:8020/ friendtech-by-chainstack", 9 | "remove-local": "graph remove --node http://localhost:8020/ friendtech-by-chainstack", 10 | "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 friendtech-by-chainstack", 11 | "test": "graph test" 12 | }, 13 | "dependencies": { 14 | "@graphprotocol/graph-cli": "0.55.0", 15 | "@graphprotocol/graph-ts": "0.30.0" 16 | }, 17 | "devDependencies": { "matchstick-as": "0.5.0" } 18 | } 19 | -------------------------------------------------------------------------------- /erc20_historical_total_supply/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erc20_historical_total_supply", 3 | "license": "UNLICENSED", 4 | "scripts": { 5 | "codegen": "graph codegen", 6 | "build": "graph build", 7 | "deploy": "graph deploy --node https://api.thegraph.com/deploy/ erc20_historical_total_supply", 8 | "create-local": "graph create --node http://localhost:8020/ erc20_historical_total_supply", 9 | "remove-local": "graph remove --node http://localhost:8020/ erc20_historical_total_supply", 10 | "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 erc20_historical_total_supply", 11 | "test": "graph test" 12 | }, 13 | "dependencies": { 14 | "@graphprotocol/graph-cli": "0.55.0", 15 | "@graphprotocol/graph-ts": "0.30.0" 16 | }, 17 | "devDependencies": { "matchstick-as": "0.5.0" } 18 | } 19 | -------------------------------------------------------------------------------- /lido-subgraph/tests/allTransferEntitiesExist.test.off.js: -------------------------------------------------------------------------------- 1 | import { getLidoEventNumber, getEntityCount } from './utils/index.js' 2 | 3 | /** 4 | Timeout is for testnet - needs to be adjusted for mainnet or when transaction count increases. 5 | **/ 6 | 7 | const RPC_TIMEOUT = 60 * 1000 8 | 9 | test( 10 | 'allSubmissionsExist', 11 | async () => { 12 | const ethNumber = await getLidoEventNumber('Submitted') 13 | const subgraphNumber = await getEntityCount('lidoSubmissions') 14 | 15 | expect(subgraphNumber).toEqual(ethNumber) 16 | }, 17 | RPC_TIMEOUT 18 | ) 19 | 20 | test( 21 | 'allTransfersExist', 22 | async () => { 23 | const ethNumber = await getLidoEventNumber('Transfer') 24 | const subgraphNumber = await getEntityCount('lidoTransfers') 25 | 26 | expect(subgraphNumber).toEqual(ethNumber) 27 | }, 28 | RPC_TIMEOUT 29 | ) 30 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/rewardBalances.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql, Big, lidoFuncCall } from './utils.js' 2 | import { ADDRESS } from './config.js' 3 | 4 | const query = gql` 5 | { 6 | oracleCompleteds(first: 1000, orderBy: block, orderDirection: asc) { 7 | block 8 | blockTime 9 | } 10 | } 11 | ` 12 | 13 | const oracleReports = (await subgraphFetch(query)).oracleCompleteds 14 | 15 | // Make sure to use an archive node! 16 | for (let report of oracleReports) { 17 | const balanceBefore = await lidoFuncCall('balanceOf', ADDRESS, { 18 | blockTag: parseInt(report.block - 1), 19 | }) 20 | const balanceAfter = await lidoFuncCall('balanceOf', ADDRESS, { 21 | blockTag: parseInt(report.block), 22 | }) 23 | 24 | const reward = Big(balanceAfter).minus(balanceBefore) 25 | 26 | console.log(report.block, reward.toString()) 27 | } 28 | -------------------------------------------------------------------------------- /lido-subgraph/tests/addressShares.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | getTestAddresses, 3 | getAddressShares, 4 | loadAddressShares, 5 | } from './utils/index.js' 6 | 7 | const ADDRESSES_TO_TEST = 30 8 | const timePerAddress = 3 // seconds 9 | const timeout = ADDRESSES_TO_TEST * timePerAddress * 1000 // in ms 10 | 11 | test( 12 | 'shares of random addresses', 13 | async () => { 14 | const addresses = await getTestAddresses(ADDRESSES_TO_TEST) 15 | 16 | for (const address of addresses) { 17 | // Log will only be shown on test failure via a custom reporter 18 | console.log(address) 19 | 20 | const realShareAmount = (await getAddressShares(address)).toString() 21 | const subgraphShareAmount = (await loadAddressShares(address)).toString() 22 | 23 | expect(subgraphShareAmount).toEqual(realShareAmount) 24 | } 25 | }, 26 | timeout 27 | ) 28 | -------------------------------------------------------------------------------- /lido-subgraph/tests/addressShares.test.off.js: -------------------------------------------------------------------------------- 1 | import { 2 | getTestAddresses, 3 | getAddressShares, 4 | calculateShares, 5 | } from './utils/index.js' 6 | 7 | const ADDRESSES_TO_TEST = 100 8 | const timePerAddress = 0.5 // seconds 9 | const timeout = ADDRESSES_TO_TEST * timePerAddress * 1000 // in ms 10 | 11 | test( 12 | 'shares of 100 random addresses', 13 | async () => { 14 | const addresses = await getTestAddresses(ADDRESSES_TO_TEST) 15 | 16 | for (const address of addresses) { 17 | // Log will only be shown on test failure via a custom reporter 18 | console.log(address) 19 | 20 | const realShareAmount = (await getAddressShares(address)).toString() 21 | const subgraphShareAmount = (await calculateShares(address)).toString() 22 | 23 | expect(subgraphShareAmount).toEqual(realShareAmount) 24 | } 25 | }, 26 | timeout 27 | ) 28 | -------------------------------------------------------------------------------- /lido-subgraph/tests/rewards.test.off.js: -------------------------------------------------------------------------------- 1 | import { 2 | lidoFuncCall, 3 | getTestAddresses, 4 | calculateAddressBalance, 5 | } from './utils/index.js' 6 | 7 | const ADDRESSES_TO_TEST = 100 8 | const timePerAddress = 0.5 // seconds 9 | const timeout = ADDRESSES_TO_TEST * timePerAddress * 1000 // in ms 10 | 11 | /** 12 | NOTE: This test is experimental, should not work. 13 | **/ 14 | 15 | test( 16 | 'rewards', 17 | async () => { 18 | const addresses = await getTestAddresses(ADDRESSES_TO_TEST) 19 | 20 | for (const address of addresses) { 21 | console.log(address) 22 | 23 | const realBalance = (await lidoFuncCall('balanceOf', address)).toString() 24 | const subgraphBalance = ( 25 | await calculateAddressBalance(address) 26 | ).toString() 27 | 28 | expect(subgraphBalance).toEqual(realBalance) 29 | } 30 | }, 31 | timeout 32 | ) 33 | -------------------------------------------------------------------------------- /tornadocash-subgraph/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: TornadoContract 7 | network: mainnet 8 | source: 9 | address: "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF" 10 | abi: TornadoContract 11 | startBlock: 17000000 12 | mapping: 13 | kind: ethereum/events 14 | apiVersion: 0.0.7 15 | language: wasm/assemblyscript 16 | entities: 17 | - Deposit 18 | - Withdrawal 19 | abis: 20 | - name: TornadoContract 21 | file: ./abis/TornadoContract.json 22 | eventHandlers: 23 | - event: Deposit(indexed bytes32,uint32,uint256) 24 | handler: handleDeposit 25 | - event: Withdrawal(address,bytes32,indexed address,uint256) 26 | handler: handleWithdrawal 27 | file: ./src/tornado-contract.ts 28 | -------------------------------------------------------------------------------- /lido-subgraph/tests/graphBalance.test.off.alt.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import ethers from 'ethers' 3 | import { jest } from '@jest/globals' 4 | 5 | const BILLING_CONTRACT_ADDRESS = '0x10829DB618E6F520Fa3A01c75bC6dDf8722fA9fE' 6 | const LIDO_ADDRESS = process.env.THEGRAPH_BILLING_ADDRESS 7 | const THRESHOLD_ETH = 1 * 1000 // 1k GRT 8 | 9 | jest.setTimeout(10000) 10 | 11 | jest.retryTimes(3) 12 | 13 | test('The Graph balance check', async () => { 14 | const provider = new ethers.providers.JsonRpcProvider( 15 | 'https://polygon-rpc.com' 16 | ) 17 | const abi = JSON.parse(fs.readFileSync('abis/Billing.json')) 18 | const contract = new ethers.Contract(BILLING_CONTRACT_ADDRESS, abi, provider) 19 | const balanceWei = await contract.userBalances(LIDO_ADDRESS) 20 | const balanceEth = balanceWei.div(ethers.constants.WeiPerEther).toNumber() 21 | 22 | expect(balanceEth).toBeGreaterThan(THRESHOLD_ETH) 23 | }) 24 | -------------------------------------------------------------------------------- /topholders-subgraph/src/contract.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Transfer as TransferEvent 3 | } from "../generated/Contract/Contract" 4 | import { 5 | Balance 6 | } from "../generated/schema" 7 | import { BigInt } from '@graphprotocol/graph-ts' 8 | 9 | export function handleTransfer(event: TransferEvent): void { 10 | // Update 'from' balance 11 | let fromBalance = Balance.load(event.params.from) 12 | if (fromBalance == null) { 13 | fromBalance = new Balance(event.params.from) 14 | fromBalance.value = BigInt.fromI32(0) 15 | } 16 | fromBalance.value = fromBalance.value.minus(event.params.value) 17 | fromBalance.save() 18 | 19 | // Update 'to' balance 20 | let toBalance = Balance.load(event.params.to) 21 | if (toBalance == null) { 22 | toBalance = new Balance(event.params.to) 23 | toBalance.value = BigInt.fromI32(0) 24 | } 25 | toBalance.value = toBalance.value.plus(event.params.value) 26 | toBalance.save() 27 | 28 | } 29 | -------------------------------------------------------------------------------- /friendtech-by-chainstack/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: FriendtechSharesV1 7 | network: base 8 | source: 9 | address: "0xCF205808Ed36593aa40a44F10c7f7C2F67d4A4d4" 10 | abi: FriendtechSharesV1 11 | startBlock: 2430439 12 | mapping: 13 | kind: ethereum/events 14 | apiVersion: 0.0.7 15 | language: wasm/assemblyscript 16 | entities: 17 | - OwnershipTransferred 18 | - Trade 19 | abis: 20 | - name: FriendtechSharesV1 21 | file: ./abis/FriendtechSharesV1.json 22 | eventHandlers: 23 | - event: OwnershipTransferred(indexed address,indexed address) 24 | handler: handleOwnershipTransferred 25 | - event: Trade(address,address,bool,uint256,uint256,uint256,uint256,uint256) 26 | handler: handleTrade 27 | file: ./src/friendtech-shares-v-1.ts 28 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/utils/ethCalls.js: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers' 2 | import fs from 'fs' 3 | 4 | import { RPC, LIDO_ADDRESS } from '../config.js' 5 | 6 | const provider = new ethers.providers.JsonRpcProvider(RPC) 7 | 8 | const lidoAbi = JSON.parse(fs.readFileSync('../abis/Lido.json')) 9 | const lidoContract = new ethers.Contract(LIDO_ADDRESS, lidoAbi, provider) 10 | 11 | export const ethCall = async (func, ...args) => await provider[func](...args) 12 | 13 | export const lidoFuncCall = async (func, ...args) => 14 | await lidoContract[func](...args) 15 | 16 | export const getAddressShares = async (address, ...args) => 17 | await lidoFuncCall('sharesOf', address, ...args) 18 | 19 | export const getAddressBalance = async (address, ...args) => 20 | await lidoFuncCall('balanceOf', address, ...args) 21 | 22 | export const getBalanceFromShares = async (address, ...args) => 23 | await lidoFuncCall('getPooledEthByShares', address, ...args) 24 | -------------------------------------------------------------------------------- /erc20_historical_total_supply/src/contract.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Transfer} 3 | from "../generated/Contract/Contract" 4 | import { 5 | TotalSupply 6 | } from "../generated/schema" 7 | import { BigInt, Address } from "@graphprotocol/graph-ts" 8 | import { Contract } from "../generated/Contract/Contract" // Import the generated contract 9 | 10 | export function handleTransfer(event: Transfer): void { 11 | let zeroAddress = Address.fromString("0x0000000000000000000000000000000000000000") 12 | if (event.params.from.equals(zeroAddress) || event.params.to.equals(zeroAddress)) { 13 | 14 | let totalSupply = new TotalSupply(event.transaction.hash.concatI32(event.logIndex.toI32())) 15 | 16 | let contract = Contract.bind(event.address) 17 | let totalSupplyResult = contract.try_totalSupply() 18 | if (!totalSupplyResult.reverted) { 19 | totalSupply.value = totalSupplyResult.value 20 | totalSupply.blockNumber = event.block.number 21 | totalSupply.save() 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lido-subgraph/tests-unit/mockedFns.ts: -------------------------------------------------------------------------------- 1 | import { ethereum } from '@graphprotocol/graph-ts' 2 | import { BigInt, Address } from '@graphprotocol/graph-ts' 3 | import { createMockedFunction } from 'matchstick-as/assembly/index' 4 | 5 | export function createMockedRewardDistribution(input: string): void { 6 | let contractAddress = Address.fromString( 7 | // Mainnet here as our network check is not working in unit tests 8 | '0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5' 9 | ) 10 | 11 | createMockedFunction( 12 | contractAddress, 13 | 'getRewardsDistribution', 14 | 'getRewardsDistribution(uint256):(address[],uint256[])' 15 | ) 16 | .withArgs([ethereum.Value.fromUnsignedBigInt(BigInt.fromString(input))]) 17 | .returns([ethereum.Value.fromArray([]), ethereum.Value.fromArray([])]) 18 | } 19 | 20 | export function createMockedRewardDistributions(inputs: string[]): void { 21 | for (let i = 0; i < inputs.length; i++) { 22 | createMockedRewardDistribution(inputs[0]) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pepe-subgraph/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: Contract 7 | network: mainnet 8 | source: 9 | address: "0x6982508145454Ce325dDbE47a25d4ec3d2311933" 10 | abi: Contract 11 | startBlock: 17046105 12 | mapping: 13 | kind: ethereum/events 14 | apiVersion: 0.0.7 15 | language: wasm/assemblyscript 16 | entities: 17 | - Approval 18 | - OwnershipTransferred 19 | - Transfer 20 | abis: 21 | - name: Contract 22 | file: ./abis/Contract.json 23 | eventHandlers: 24 | - event: Approval(indexed address,indexed address,uint256) 25 | handler: handleApproval 26 | - event: OwnershipTransferred(indexed address,indexed address) 27 | handler: handleOwnershipTransferred 28 | - event: Transfer(indexed address,indexed address,uint256) 29 | handler: handleTransfer 30 | file: ./src/contract.ts 31 | -------------------------------------------------------------------------------- /lido-subgraph/tests/noTxNegativeValues.test.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { subgraphFetch } from './utils/index.js' 3 | 4 | const sharesQuery = gql` 5 | query ($first: Int, $skip: Int, $block: Block_height) { 6 | lidoTransfers( 7 | first: $first 8 | skip: $skip 9 | block: $block 10 | where: { sharesAfterDecrease_lt: 0 } 11 | ) { 12 | id 13 | } 14 | } 15 | ` 16 | 17 | const balanceQuery = gql` 18 | query ($first: Int, $skip: Int, $block: Block_height) { 19 | lidoTransfers( 20 | first: $first 21 | skip: $skip 22 | block: $block 23 | where: { balanceAfterDecrease_lt: 0 } 24 | ) { 25 | id 26 | } 27 | } 28 | ` 29 | 30 | test('there are no transactions going to minus', async () => { 31 | const sharesItems = (await subgraphFetch(sharesQuery)).lidoTransfers 32 | const balanceItems = (await subgraphFetch(balanceQuery)).lidoTransfers 33 | 34 | expect(sharesItems.length).toEqual(0) 35 | expect(balanceItems.length).toEqual(0) 36 | }, 50000) 37 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/allStEthHolders.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql } from './utils.js' 2 | import fs from 'fs' 3 | 4 | const genQuery = (skip) => gql` 5 | { 6 | lidoTransfers(first: 1000, skip: ${skip}) { 7 | from 8 | to 9 | } 10 | } 11 | ` 12 | 13 | const unique = new Set() 14 | 15 | let skip = 0 16 | let gotItems = 0 17 | 18 | // Make sure we are using self-hosted Graph nodes with no limits or this will fail 19 | while (gotItems === 0 || gotItems % 1000 === 0) { 20 | const items = (await subgraphFetch(genQuery(skip))).lidoTransfers 21 | 22 | skip += 1000 23 | gotItems += items.length 24 | 25 | for (const item of items) { 26 | unique.add(item.from) 27 | unique.add(item.to) 28 | } 29 | 30 | console.log('Fetched', gotItems) 31 | } 32 | 33 | const filtered = Array.from(unique).filter( 34 | (x) => x !== '0x0000000000000000000000000000000000000000' 35 | ) 36 | 37 | console.log('Found', filtered.length, 'unique addresses of stETH holders') 38 | 39 | await fs.promises.writeFile('withContracts.json', JSON.stringify(filtered)) 40 | -------------------------------------------------------------------------------- /lido-subgraph/tests/balances.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | lidoFuncCall, 3 | getTestAddresses, 4 | loadAddressShares, 5 | getTotals, 6 | BigNumber, 7 | } from './utils/index.js' 8 | 9 | const ADDRESSES_TO_TEST = 30 10 | const timePerAddress = 5 // seconds 11 | const timeout = ADDRESSES_TO_TEST * timePerAddress * 1000 // in ms 12 | 13 | test( 14 | 'balances', 15 | async () => { 16 | const addresses = await getTestAddresses(ADDRESSES_TO_TEST) 17 | 18 | for (const address of addresses) { 19 | // Log will only be shown on test failure via a custom reporter 20 | console.log(address) 21 | 22 | const { totalPooledEther, totalShares } = await getTotals(address) 23 | 24 | const shares = await loadAddressShares(address) 25 | 26 | const subgraphBalance = BigNumber.from(shares) 27 | .mul(totalPooledEther) 28 | .div(totalShares) 29 | .toString() 30 | const realBalance = (await lidoFuncCall('balanceOf', address)).toString() 31 | 32 | expect(subgraphBalance).toEqual(realBalance) 33 | } 34 | }, 35 | timeout 36 | ) 37 | -------------------------------------------------------------------------------- /lido-subgraph/tests/graphBalance.test.off.js: -------------------------------------------------------------------------------- 1 | import { gql, request } from 'graphql-request' 2 | import ethers from 'ethers' 3 | import { jest } from '@jest/globals' 4 | 5 | const LIDO_ADDRESS = process.env.THEGRAPH_BILLING_ADDRESS 6 | const THRESHOLD_ETH = 1 * 1000 // 1k GRT 7 | const BILLING_SUBGRAPH = 8 | 'https://api.thegraph.com/subgraphs/name/graphprotocol/billing' 9 | 10 | jest.setTimeout(10000) 11 | 12 | jest.retryTimes(3) 13 | 14 | const balanceQuery = gql` 15 | query billingAccount($id: ID!, $block: Block_height) { 16 | user(id: $id, block: $block) { 17 | id 18 | billingBalance 19 | polygonGRTBalance 20 | } 21 | } 22 | ` 23 | 24 | test('The Graph balance check', async () => { 25 | const res = await request(BILLING_SUBGRAPH, balanceQuery, { 26 | // Don't use checksummed addresses here as they are not checksummed in Subgraphs 27 | id: LIDO_ADDRESS.toLowerCase(), 28 | }) 29 | 30 | const rawBalance = ethers.BigNumber.from(res.user.billingBalance) 31 | const balance = rawBalance.div(ethers.constants.WeiPerEther).toNumber() 32 | 33 | expect(balance).toBeGreaterThan(THRESHOLD_ETH) 34 | }) 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kirill Balakhonov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lido-subgraph/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lido 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lido-subgraph/tests/noWrongDust.test.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { subgraphFetch } from './utils/index.js' 3 | 4 | const dustNullQuery = gql` 5 | query ($first: Int, $skip: Int, $block: Block_height) { 6 | totalRewards( 7 | first: $first 8 | skip: $skip 9 | block: $block 10 | where: { dust: null } 11 | ) { 12 | id 13 | block 14 | } 15 | } 16 | ` 17 | 18 | test('there are no rewards with null dust', async () => { 19 | const totalRewards = (await subgraphFetch(dustNullQuery)).totalRewards 20 | 21 | expect(totalRewards).toEqual([]) 22 | }) 23 | 24 | // Dust should be 0 when there are treasuryFees 25 | const simultaneousQuery = gql` 26 | query ($first: Int, $skip: Int, $block: Block_height) { 27 | totalRewards( 28 | first: $first 29 | skip: $skip 30 | block: $block 31 | where: { dust_not: 0, treasuryFee_not: 0 } 32 | ) { 33 | id 34 | block 35 | } 36 | } 37 | ` 38 | 39 | test('there are no txs with both dust and treasuryFee', async () => { 40 | const totalRewards = (await subgraphFetch(simultaneousQuery)).totalRewards 41 | 42 | expect(totalRewards).toEqual([]) 43 | }) 44 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/monthTotalRewards.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql, Big } from './utils.js' 2 | 3 | import { sub } from 'date-fns' 4 | 5 | const toHumanDate = (date) => 6 | date.toLocaleDateString('en-GB', { timeZone: 'UTC' }) 7 | 8 | const toHumanEthAmount = (value) => 9 | Big(value).div(Big('1e18')).toFixed(7).toString() 10 | 11 | const dayInMs = 1000 * 60 * 60 * 24 12 | 13 | const fullDays = Math.floor(new Date() / dayInMs) * dayInMs 14 | 15 | // 30 days ago range to last full day 16 | const monthStartEnd = { 17 | from: sub(new Date(fullDays), { days: 30 }), 18 | to: sub(new Date(fullDays), { seconds: 1 }), 19 | } 20 | 21 | // Converting JS dates to unix time 22 | const blockTimes = { 23 | from: Math.round(monthStartEnd.from / 1000), 24 | to: Math.round(monthStartEnd.to / 1000), 25 | } 26 | 27 | const monthRewardsQuery = gql` 28 | { 29 | totalRewards(first: 1000, where: { 30 | blockTime_gte: ${blockTimes.from}, 31 | blockTime_lte: ${blockTimes.to} }) { 32 | totalRewards 33 | } 34 | } 35 | ` 36 | 37 | const rewards = (await subgraphFetch(monthRewardsQuery)).totalRewards 38 | const sum = rewards.reduce( 39 | (acc, item) => acc.plus(Big(item.totalRewards)), 40 | Big(0) 41 | ) 42 | console.log( 43 | 'Rewarded our customers', 44 | toHumanEthAmount(sum), 45 | 'StETH in the last 30 days', 46 | toHumanDate(monthStartEnd.from), 47 | '-', 48 | toHumanDate(monthStartEnd.to) 49 | ) 50 | -------------------------------------------------------------------------------- /topholders-subgraph/tests/contract.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | describe, 4 | test, 5 | clearStore, 6 | beforeAll, 7 | afterAll 8 | } from "matchstick-as/assembly/index" 9 | import { BigInt, Address } from "@graphprotocol/graph-ts" 10 | import { Issue } from "../generated/schema" 11 | import { Issue as IssueEvent } from "../generated/Contract/Contract" 12 | import { handleIssue } from "../src/contract" 13 | import { createIssueEvent } from "./contract-utils" 14 | 15 | // Tests structure (matchstick-as >=0.5.0) 16 | // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 17 | 18 | describe("Describe entity assertions", () => { 19 | beforeAll(() => { 20 | let amount = BigInt.fromI32(234) 21 | let newIssueEvent = createIssueEvent(amount) 22 | handleIssue(newIssueEvent) 23 | }) 24 | 25 | afterAll(() => { 26 | clearStore() 27 | }) 28 | 29 | // For more test scenarios, see: 30 | // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test 31 | 32 | test("Issue created and stored", () => { 33 | assert.entityCount("Issue", 1) 34 | 35 | // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function 36 | assert.fieldEquals( 37 | "Issue", 38 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 39 | "amount", 40 | "234" 41 | ) 42 | 43 | // More assert options: 44 | // https://thegraph.com/docs/en/developer/matchstick/#asserts 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /top_holders_updater_example/tests/contract.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | describe, 4 | test, 5 | clearStore, 6 | beforeAll, 7 | afterAll 8 | } from "matchstick-as/assembly/index" 9 | import { BigInt, Address } from "@graphprotocol/graph-ts" 10 | import { Issue } from "../generated/schema" 11 | import { Issue as IssueEvent } from "../generated/Contract/Contract" 12 | import { handleIssue } from "../src/contract" 13 | import { createIssueEvent } from "./contract-utils" 14 | 15 | // Tests structure (matchstick-as >=0.5.0) 16 | // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 17 | 18 | describe("Describe entity assertions", () => { 19 | beforeAll(() => { 20 | let amount = BigInt.fromI32(234) 21 | let newIssueEvent = createIssueEvent(amount) 22 | handleIssue(newIssueEvent) 23 | }) 24 | 25 | afterAll(() => { 26 | clearStore() 27 | }) 28 | 29 | // For more test scenarios, see: 30 | // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test 31 | 32 | test("Issue created and stored", () => { 33 | assert.entityCount("Issue", 1) 34 | 35 | // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function 36 | assert.fieldEquals( 37 | "Issue", 38 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 39 | "amount", 40 | "234" 41 | ) 42 | 43 | // More assert options: 44 | // https://thegraph.com/docs/en/developer/matchstick/#asserts 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /erc20_historical_total_supply/tests/contract.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | describe, 4 | test, 5 | clearStore, 6 | beforeAll, 7 | afterAll 8 | } from "matchstick-as/assembly/index" 9 | import { BigInt, Address } from "@graphprotocol/graph-ts" 10 | import { Issue } from "../generated/schema" 11 | import { Issue as IssueEvent } from "../generated/Contract/Contract" 12 | import { handleIssue } from "../src/contract" 13 | import { createIssueEvent } from "./contract-utils" 14 | 15 | // Tests structure (matchstick-as >=0.5.0) 16 | // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 17 | 18 | describe("Describe entity assertions", () => { 19 | beforeAll(() => { 20 | let amount = BigInt.fromI32(234) 21 | let newIssueEvent = createIssueEvent(amount) 22 | handleIssue(newIssueEvent) 23 | }) 24 | 25 | afterAll(() => { 26 | clearStore() 27 | }) 28 | 29 | // For more test scenarios, see: 30 | // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test 31 | 32 | test("Issue created and stored", () => { 33 | assert.entityCount("Issue", 1) 34 | 35 | // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function 36 | assert.fieldEquals( 37 | "Issue", 38 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 39 | "amount", 40 | "234" 41 | ) 42 | 43 | // More assert options: 44 | // https://thegraph.com/docs/en/developer/matchstick/#asserts 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /each_block_handler_example/tests/contract.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | describe, 4 | test, 5 | clearStore, 6 | beforeAll, 7 | afterAll 8 | } from "matchstick-as/assembly/index" 9 | import { BigInt, Address } from "@graphprotocol/graph-ts" 10 | import { ExampleEntity } from "../generated/schema" 11 | import { Issue } from "../generated/Contract/Contract" 12 | import { handleIssue } from "../src/contract" 13 | import { createIssueEvent } from "./contract-utils" 14 | 15 | // Tests structure (matchstick-as >=0.5.0) 16 | // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 17 | 18 | describe("Describe entity assertions", () => { 19 | beforeAll(() => { 20 | let amount = BigInt.fromI32(234) 21 | let newIssueEvent = createIssueEvent(amount) 22 | handleIssue(newIssueEvent) 23 | }) 24 | 25 | afterAll(() => { 26 | clearStore() 27 | }) 28 | 29 | // For more test scenarios, see: 30 | // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test 31 | 32 | test("ExampleEntity created and stored", () => { 33 | assert.entityCount("ExampleEntity", 1) 34 | 35 | // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function 36 | assert.fieldEquals( 37 | "ExampleEntity", 38 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", 39 | "amount", 40 | "234" 41 | ) 42 | 43 | // More assert options: 44 | // https://thegraph.com/docs/en/developer/matchstick/#asserts 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/referrals.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql, Big } from './utils.js' 2 | import { ADDRESS } from './config.js' 3 | 4 | const generateQuery = (referral, skip) => gql` 5 | { 6 | lidoSubmissions( 7 | skip: ${skip} 8 | first: 1000 9 | where: { referral: "${referral}" } 10 | orderBy: blockTime 11 | orderDirection: desc 12 | ) { 13 | sender 14 | amount 15 | blockTime 16 | } 17 | } 18 | ` 19 | 20 | const fetchToLimits = async (referral) => { 21 | const subgraphLimit = 6000 22 | let skip = 0 23 | let gotItems = 0 24 | let results = [] 25 | 26 | // We do respect hosted Subgraph's limit here 27 | while ((gotItems === 0 || gotItems % 1000 === 0) && skip < subgraphLimit) { 28 | const items = (await subgraphFetch(generateQuery(referral, skip))) 29 | .lidoSubmissions 30 | 31 | skip += 1000 32 | gotItems += items.length 33 | 34 | results.push(...items) 35 | } 36 | 37 | return results 38 | } 39 | 40 | // This example is stats-only, doesn't take limits into account 41 | 42 | const submissions = await fetchToLimits(ADDRESS) 43 | 44 | const total = submissions.reduce((acc, item) => acc.plus(item.amount), Big(0)) 45 | 46 | const uniqueReferred = [...new Set(submissions.map((x) => x.sender))] 47 | 48 | console.log( 49 | ADDRESS, 50 | 'referred a total of', 51 | total.div('1e18').round(2).toNumber(), 52 | 'stETH' 53 | ) 54 | console.log(ADDRESS, 'referred', uniqueReferred.length, 'unique addresses') 55 | -------------------------------------------------------------------------------- /friendtech-by-chainstack/src/friendtech-shares-v-1.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OwnershipTransferred as OwnershipTransferredEvent, 3 | Trade as TradeEvent 4 | } from "../generated/FriendtechSharesV1/FriendtechSharesV1" 5 | import { OwnershipTransferred, Trade } from "../generated/schema" 6 | 7 | export function handleOwnershipTransferred( 8 | event: OwnershipTransferredEvent 9 | ): void { 10 | let entity = new OwnershipTransferred( 11 | event.transaction.hash.concatI32(event.logIndex.toI32()) 12 | ) 13 | entity.previousOwner = event.params.previousOwner 14 | entity.newOwner = event.params.newOwner 15 | 16 | entity.blockNumber = event.block.number 17 | entity.blockTimestamp = event.block.timestamp 18 | entity.transactionHash = event.transaction.hash 19 | 20 | entity.save() 21 | } 22 | 23 | export function handleTrade(event: TradeEvent): void { 24 | let entity = new Trade( 25 | event.transaction.hash.concatI32(event.logIndex.toI32()) 26 | ) 27 | entity.trader = event.params.trader 28 | entity.subject = event.params.subject 29 | entity.isBuy = event.params.isBuy 30 | entity.shareAmount = event.params.shareAmount 31 | entity.ethAmount = event.params.ethAmount 32 | entity.protocolEthAmount = event.params.protocolEthAmount 33 | entity.subjectEthAmount = event.params.subjectEthAmount 34 | entity.supply = event.params.supply 35 | 36 | entity.blockNumber = event.block.number 37 | entity.blockTimestamp = event.block.timestamp 38 | entity.transactionHash = event.transaction.hash 39 | 40 | entity.save() 41 | } 42 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-python/rewardBalances.py: -------------------------------------------------------------------------------- 1 | import json 2 | from datetime import datetime 3 | 4 | from web3 import Web3 5 | 6 | from gql import gql, Client 7 | from gql.transport.requests import RequestsHTTPTransport 8 | 9 | subgraph_url = "https://api.thegraph.com/subgraphs/name/lidofinance/lido" 10 | 11 | lido_address = "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84" 12 | lido_abi = json.load(open("../abis/Lido.json")) 13 | 14 | w3 = Web3(Web3.HTTPProvider("RPC_PROVIDER_HERE")) 15 | lido_contract = w3.eth.contract(address=lido_address, abi=lido_abi) 16 | 17 | address_to_check = Web3.toChecksumAddress("ADDRESS_TO_CHECK_HERE") 18 | balance_function = lido_contract.functions.balanceOf(address_to_check) 19 | 20 | 21 | graphql_transport = RequestsHTTPTransport( 22 | url=subgraph_url, 23 | use_json=True, 24 | headers={ 25 | "Content-type": "application/json", 26 | }, 27 | verify=False, 28 | retries=3, 29 | ) 30 | 31 | graphql_client = Client( 32 | transport=graphql_transport, 33 | fetch_schema_from_transport=True, 34 | ) 35 | 36 | query = gql( 37 | """ 38 | query { 39 | oracleCompleteds (first: 1000, orderBy: block, orderDirection: desc) { 40 | block 41 | blockTime 42 | } 43 | } 44 | """ 45 | ) 46 | 47 | reports = graphql_client.execute(query) 48 | 49 | for report in reports["oracleCompleteds"]: 50 | gwei_balance = balance_function.call(block_identifier=int(report["block"])) 51 | eth_balance = Web3.fromWei(gwei_balance, "ether") 52 | time = datetime.utcfromtimestamp(int(report["blockTime"])).strftime("%Y-%m-%d %H:%M:%S") 53 | 54 | print(time, eth_balance) 55 | -------------------------------------------------------------------------------- /tornadocash-subgraph/src/tornado-contract.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Deposit as DepositEvent, 3 | Withdrawal as WithdrawalEvent 4 | } from "../generated/TornadoContract/TornadoContract" 5 | import { Deposit, Withdrawal } from "../generated/schema" 6 | import { Address, BigInt } from "@graphprotocol/graph-ts" 7 | import { TornadoContract } from "../generated/TornadoContract/TornadoContract" 8 | 9 | export function handleDeposit(event: DepositEvent): void { 10 | let entity = new Deposit( 11 | event.transaction.hash.concatI32(event.logIndex.toI32()) 12 | ) 13 | 14 | entity.commitment = event.params.commitment 15 | entity.leafIndex = event.params.leafIndex 16 | entity.timestamp = event.params.timestamp 17 | 18 | entity.blockNumber = event.block.number 19 | entity.blockTimestamp = event.block.timestamp 20 | entity.transactionHash = event.transaction.hash 21 | 22 | // The address that triggered the event can be accessed via event.transaction.from 23 | entity.from_ = event.transaction.from 24 | 25 | // The value of the transaction in Wei can be accessed via event.transaction.value 26 | entity.value_ = event.transaction.value 27 | 28 | entity.save() 29 | } 30 | 31 | export function handleWithdrawal(event: WithdrawalEvent): void { 32 | let entity = new Withdrawal( 33 | event.transaction.hash.concatI32(event.logIndex.toI32()) 34 | ) 35 | entity.to = event.params.to 36 | entity.nullifierHash = event.params.nullifierHash 37 | entity.relayer = event.params.relayer 38 | entity.fee = event.params.fee 39 | 40 | entity.blockNumber = event.block.number 41 | entity.blockTimestamp = event.block.timestamp 42 | entity.transactionHash = event.transaction.hash 43 | 44 | entity.save() 45 | } 46 | -------------------------------------------------------------------------------- /pepe-subgraph/src/contract.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Approval as ApprovalEvent, 3 | OwnershipTransferred as OwnershipTransferredEvent, 4 | Transfer as TransferEvent 5 | } from "../generated/Contract/Contract" 6 | import { Approval, OwnershipTransferred, Transfer } from "../generated/schema" 7 | 8 | export function handleApproval(event: ApprovalEvent): void { 9 | let entity = new Approval( 10 | event.transaction.hash.concatI32(event.logIndex.toI32()) 11 | ) 12 | entity.owner = event.params.owner 13 | entity.spender = event.params.spender 14 | entity.value = event.params.value 15 | 16 | entity.blockNumber = event.block.number 17 | entity.blockTimestamp = event.block.timestamp 18 | entity.transactionHash = event.transaction.hash 19 | 20 | entity.save() 21 | } 22 | 23 | export function handleOwnershipTransferred( 24 | event: OwnershipTransferredEvent 25 | ): void { 26 | let entity = new OwnershipTransferred( 27 | event.transaction.hash.concatI32(event.logIndex.toI32()) 28 | ) 29 | entity.previousOwner = event.params.previousOwner 30 | entity.newOwner = event.params.newOwner 31 | 32 | entity.blockNumber = event.block.number 33 | entity.blockTimestamp = event.block.timestamp 34 | entity.transactionHash = event.transaction.hash 35 | 36 | entity.save() 37 | } 38 | 39 | export function handleTransfer(event: TransferEvent): void { 40 | let entity = new Transfer( 41 | event.transaction.hash.concatI32(event.logIndex.toI32()) 42 | ) 43 | entity.from = event.params.from 44 | entity.to = event.params.to 45 | entity.value = event.params.value 46 | 47 | entity.blockNumber = event.block.number 48 | entity.blockTimestamp = event.block.timestamp 49 | entity.transactionHash = event.transaction.hash 50 | 51 | entity.save() 52 | } 53 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/submitsForPeriod.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import { sub, getUnixTime, fromUnixTime, format, startOfDay } from 'date-fns' 3 | import { subgraphFetch, gql, Big, BigDecimal } from './utils.js' 4 | 5 | const MONTHS_BACK = 3 6 | 7 | const toEth = (number) => BigDecimal(number).div(1e18).round(2).toNumber() 8 | 9 | const formatDate = (unixTime) => format(fromUnixTime(unixTime), 'dd/MM/yyyy') 10 | 11 | const startRaw = sub(new Date(), { months: MONTHS_BACK }) 12 | 13 | const startDay = startOfDay(startRaw) 14 | 15 | // date-fns uses Date, which is local, so we compensate for the offset here 16 | const start = getUnixTime( 17 | new Date(startDay.valueOf() - startDay.getTimezoneOffset() * 60 * 1000) 18 | ) 19 | 20 | // Make sure we are using self-hosted Graph nodes with no limits or this will fail 21 | const query = gql` 22 | { 23 | lidoSubmissions(first: 100000, where: {blockTime_gte: ${start}}, orderBy: "blockTime") { 24 | sender 25 | amount 26 | block 27 | blockTime 28 | transactionHash 29 | } 30 | } 31 | ` 32 | 33 | const items = (await subgraphFetch(query)).lidoSubmissions 34 | 35 | console.log('Fetched', items.length, 'stakings for', MONTHS_BACK, 'months') 36 | 37 | const days = items.reduce((acc, item) => { 38 | const date = formatDate(item.blockTime) 39 | 40 | if (!acc.hasOwnProperty(date)) { 41 | acc[date] = { submits: [], total: Big(0) } 42 | } 43 | 44 | acc[date]['submits'].push(item) 45 | acc[date]['total'] = acc[date]['total'].plus(item.amount) 46 | 47 | return acc 48 | }, {}) 49 | 50 | Object.entries(days).forEach(([date, items]) => 51 | console.log( 52 | date, 53 | items.submits.length, 54 | 'submits for', 55 | toEth(items.total), 56 | 'ETH' 57 | ) 58 | ) 59 | 60 | await fs.promises.writeFile('submits.json', JSON.stringify(days)) 61 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/findTreasurySharesMismatch.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql, lidoFuncCall, Big } from './utils.js' 2 | 3 | const TREASURY_ADDRESS = '0x3e40d73eb977dc6a537af587d48316fee66e9c8c' 4 | 5 | const firstSubmitQuery = gql` 6 | query { 7 | lidoSubmissions(orderBy: block, orderDirection: asc, limit: 1) { 8 | block 9 | } 10 | } 11 | ` 12 | 13 | const lastIndexedQuery = gql` 14 | query { 15 | _meta { 16 | block { 17 | number 18 | } 19 | } 20 | } 21 | ` 22 | 23 | const startBlock = parseInt( 24 | (await subgraphFetch(firstSubmitQuery)).lidoSubmissions[0].block 25 | ) 26 | const endBlock = parseInt( 27 | (await subgraphFetch(lastIndexedQuery))._meta.block.number 28 | ) 29 | 30 | let min = startBlock 31 | let max = endBlock 32 | 33 | const genQuery = (block) => gql` 34 | query { 35 | shares(id: "${TREASURY_ADDRESS}", block: { number: ${block} }) { 36 | shares 37 | } 38 | } 39 | ` 40 | 41 | while (min <= max) { 42 | const mid = Math.floor((min + max) / 2) 43 | 44 | const ethShares = Big( 45 | await lidoFuncCall('sharesOf', TREASURY_ADDRESS, { 46 | blockTag: mid, 47 | }) 48 | ) 49 | 50 | const subgraphData = await subgraphFetch(genQuery(mid)) 51 | const subShares = Big(subgraphData.shares.shares) 52 | 53 | const isGood = ethShares.eq(subShares) 54 | 55 | if (isGood) { 56 | // Check doesn't fail yet, going up 57 | min = mid + 1 58 | } else { 59 | // Check already fails, need to go down 60 | max = mid 61 | } 62 | 63 | if (min === max && !isGood) { 64 | // Found it 65 | if (!ethShares.eq(subShares)) { 66 | console.log( 67 | 'Mismatch @', 68 | mid, 69 | ethShares.toString(), 70 | '<->', 71 | subShares.toString() 72 | ) 73 | } 74 | process.exit() 75 | } 76 | } 77 | 78 | console.log('No mismatches found!') 79 | -------------------------------------------------------------------------------- /tornadocash-subgraph/tests/tornado-contract-utils.ts: -------------------------------------------------------------------------------- 1 | import { newMockEvent } from "matchstick-as" 2 | import { ethereum, Bytes, BigInt, Address } from "@graphprotocol/graph-ts" 3 | import { 4 | Deposit, 5 | Withdrawal 6 | } from "../generated/TornadoContract/TornadoContract" 7 | 8 | export function createDepositEvent( 9 | commitment: Bytes, 10 | leafIndex: BigInt, 11 | timestamp: BigInt 12 | ): Deposit { 13 | let depositEvent = changetype(newMockEvent()) 14 | 15 | depositEvent.parameters = new Array() 16 | 17 | depositEvent.parameters.push( 18 | new ethereum.EventParam( 19 | "commitment", 20 | ethereum.Value.fromFixedBytes(commitment) 21 | ) 22 | ) 23 | depositEvent.parameters.push( 24 | new ethereum.EventParam( 25 | "leafIndex", 26 | ethereum.Value.fromUnsignedBigInt(leafIndex) 27 | ) 28 | ) 29 | depositEvent.parameters.push( 30 | new ethereum.EventParam( 31 | "timestamp", 32 | ethereum.Value.fromUnsignedBigInt(timestamp) 33 | ) 34 | ) 35 | 36 | return depositEvent 37 | } 38 | 39 | export function createWithdrawalEvent( 40 | to: Address, 41 | nullifierHash: Bytes, 42 | relayer: Address, 43 | fee: BigInt 44 | ): Withdrawal { 45 | let withdrawalEvent = changetype(newMockEvent()) 46 | 47 | withdrawalEvent.parameters = new Array() 48 | 49 | withdrawalEvent.parameters.push( 50 | new ethereum.EventParam("to", ethereum.Value.fromAddress(to)) 51 | ) 52 | withdrawalEvent.parameters.push( 53 | new ethereum.EventParam( 54 | "nullifierHash", 55 | ethereum.Value.fromFixedBytes(nullifierHash) 56 | ) 57 | ) 58 | withdrawalEvent.parameters.push( 59 | new ethereum.EventParam("relayer", ethereum.Value.fromAddress(relayer)) 60 | ) 61 | withdrawalEvent.parameters.push( 62 | new ethereum.EventParam("fee", ethereum.Value.fromUnsignedBigInt(fee)) 63 | ) 64 | 65 | return withdrawalEvent 66 | } 67 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/calculateShares.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql, Big } from './utils.js' 2 | import { ADDRESS } from './config.js' 3 | 4 | const submissionsQuery = gql` 5 | { 6 | lidoSubmissions(first: 1000, where: { sender: "${ADDRESS}" }) { 7 | amount 8 | shares 9 | block 10 | } 11 | } 12 | ` 13 | 14 | const transfersInboundQuery = gql` 15 | { 16 | lidoTransfers(first: 1000, where: { to: "${ADDRESS}" }) { 17 | shares 18 | to 19 | block 20 | } 21 | } 22 | ` 23 | 24 | const transfersOutboundQuery = gql` 25 | { 26 | lidoTransfers(first: 1000, where: { from: "${ADDRESS}" }) { 27 | shares 28 | to 29 | block 30 | } 31 | } 32 | ` 33 | 34 | const submissions = (await subgraphFetch(submissionsQuery)).lidoSubmissions 35 | const transfersInbound = (await subgraphFetch(transfersInboundQuery)) 36 | .lidoTransfers 37 | const transfersOutbound = (await subgraphFetch(transfersOutboundQuery)) 38 | .lidoTransfers 39 | 40 | const together = [ 41 | ...submissions.map((x) => ({ ...x, type: 'submission' })), 42 | ...transfersInbound.map((x) => ({ 43 | ...x, 44 | type: 'transfer', 45 | direction: 'inbound', 46 | })), 47 | ...transfersOutbound.map((x) => ({ 48 | ...x, 49 | type: 'transfer', 50 | direction: 'outbound', 51 | })), 52 | ].sort((a, b) => a.block - b.block) 53 | 54 | let shares = Big(0) 55 | 56 | for (const item of together) { 57 | const isStaking = item.type === 'submission' 58 | const isOut = !isStaking && item.direction === 'outbound' 59 | 60 | const currentShares = item.shares || 0 61 | 62 | shares = isOut ? shares.sub(currentShares) : shares.add(currentShares) 63 | 64 | console.log( 65 | isStaking ? 'Staking' : isOut ? 'Transfer Outbound' : 'Transfer Inbound', 66 | item.shares, 67 | '->', 68 | shares.toString(), 69 | item.block 70 | ) 71 | } 72 | 73 | console.log('Final', shares.toString()) 74 | -------------------------------------------------------------------------------- /tornadocash-subgraph/tests/tornado-contract.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | describe, 4 | test, 5 | clearStore, 6 | beforeAll, 7 | afterAll 8 | } from "matchstick-as/assembly/index" 9 | import { Bytes, BigInt, Address } from "@graphprotocol/graph-ts" 10 | import { Deposit } from "../generated/schema" 11 | import { Deposit as DepositEvent } from "../generated/TornadoContract/TornadoContract" 12 | import { handleDeposit } from "../src/tornado-contract" 13 | import { createDepositEvent } from "./tornado-contract-utils" 14 | 15 | // Tests structure (matchstick-as >=0.5.0) 16 | // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 17 | 18 | describe("Describe entity assertions", () => { 19 | beforeAll(() => { 20 | let commitment = Bytes.fromI32(1234567890) 21 | let leafIndex = BigInt.fromI32(234) 22 | let timestamp = BigInt.fromI32(234) 23 | let newDepositEvent = createDepositEvent(commitment, leafIndex, timestamp) 24 | handleDeposit(newDepositEvent) 25 | }) 26 | 27 | afterAll(() => { 28 | clearStore() 29 | }) 30 | 31 | // For more test scenarios, see: 32 | // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test 33 | 34 | test("Deposit created and stored", () => { 35 | assert.entityCount("Deposit", 1) 36 | 37 | // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function 38 | assert.fieldEquals( 39 | "Deposit", 40 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 41 | "commitment", 42 | "1234567890" 43 | ) 44 | assert.fieldEquals( 45 | "Deposit", 46 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 47 | "leafIndex", 48 | "234" 49 | ) 50 | assert.fieldEquals( 51 | "Deposit", 52 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 53 | "timestamp", 54 | "234" 55 | ) 56 | 57 | // More assert options: 58 | // https://thegraph.com/docs/en/developer/matchstick/#asserts 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/subgraphFetch.js: -------------------------------------------------------------------------------- 1 | import { request } from 'graphql-request' 2 | import { GRAPH, getBlock, getIsLimited } from '../config.js' 3 | 4 | const FETCH_STEP = 1000 5 | const SKIP_STEP = 1000 6 | const HOSTED_LIMIT = 6000 7 | 8 | // Add fixed block if not set already 9 | const mbAddFixedBlock = (vars) => { 10 | if (vars.block) { 11 | return query 12 | } 13 | 14 | const block = getBlock() 15 | return { ...vars, block: { number: block } } 16 | } 17 | 18 | // Warning: Assumes one-key objects 19 | const mergeObjects = (array) => 20 | array.reduce((acc, cur) => { 21 | const key = Object.keys(cur)[0] 22 | const exists = key === Object.keys(acc)[0] 23 | 24 | acc[key] = exists ? [...acc[key], ...cur[key]] : cur[key] 25 | 26 | return acc 27 | }, {}) 28 | 29 | export const subgraphFetch = async (query, vars = {}) => { 30 | let skip = 0 31 | let results = null 32 | 33 | do { 34 | const res = await request(GRAPH, query, { 35 | first: FETCH_STEP, 36 | skip: skip, 37 | ...mbAddFixedBlock(vars), 38 | }) 39 | 40 | // Super small delay not to DOS the indexing node 41 | await new Promise((resolve) => setTimeout(resolve, 10)) 42 | 43 | results = results ? mergeObjects([results, res]) : res 44 | 45 | // Breaking if there is exactly SKIP_STEP items and new results are now empty 46 | // Still adding an empty key to the result above 47 | const value = Object.values(res)[0] 48 | if (Array.isArray(value) && !value.length) { 49 | break 50 | } 51 | 52 | // Exit if we don't need to pull all available data 53 | if (vars?.first || vars?.skip) { 54 | break 55 | } 56 | 57 | skip += SKIP_STEP 58 | 59 | if (getIsLimited() && skip >= HOSTED_LIMIT) { 60 | break 61 | } 62 | } while ( 63 | // Items should exist at all 64 | Object.values(results)[0].length && 65 | // More items should exist 66 | Object.values(results)[0].length % SKIP_STEP === 0 67 | ) 68 | 69 | return results 70 | } 71 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/getTestAddresses.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch } from './index.js' 2 | import { getIsMainnet, getIsLimited } from '../config.js' 3 | 4 | import { gql } from 'graphql-request' 5 | 6 | // Enough for random selection, saves us from brute forcing entity amount 7 | const MAX_LIMITED = 5000 8 | const MAX_UNLIMITED = 200000 9 | 10 | const IMPORTANT_ADDRESSES = [ 11 | // Lido Treasury (Aragon Agent) 12 | '0x3e40d73eb977dc6a537af587d48316fee66e9c8c', 13 | // Lido Node Operator #0 14 | '0xdd4bc51496dc93a0c47008e820e0d80745476f22', 15 | // Lido Node Operator #1 16 | '0x8d689476eb446a1fb0065bffac32398ed7f89165', 17 | // Lido Node Operator #2 18 | '0x9a66fd7948a6834176fbb1c4127c61cb6d349561', 19 | // Curve stETH pool 20 | '0xdc24316b9ae028f1497c275eb9192a3ea0f67022', 21 | // 1inch LDO-stETH pool 22 | '0x1f629794b34ffb3b29ff206be5478a52678b47ae', 23 | ] 24 | 25 | const genQuery = (first, skip) => gql` 26 | query ($block: Block_height) { 27 | lidoTransfers(first: ${first}, skip: ${skip}, block: $block) { 28 | from 29 | to 30 | } 31 | } 32 | ` 33 | 34 | export const getTestAddresses = async (amount = 30, skipImportant = false) => { 35 | const max = getIsLimited() ? MAX_LIMITED : MAX_UNLIMITED 36 | const maxSkip = max - amount 37 | const randomSkip = Math.floor(Math.random() * maxSkip) 38 | 39 | const query = genQuery(amount, randomSkip) 40 | const transfers = (await subgraphFetch(query)).lidoTransfers 41 | 42 | const uniqueAddresses = transfers.reduce((acc, item) => { 43 | acc.add(item.from) 44 | acc.add(item.to) 45 | return acc 46 | }, new Set()) 47 | 48 | // Mint address 49 | uniqueAddresses.delete('0x0000000000000000000000000000000000000000') 50 | 51 | const shuffled = [...uniqueAddresses].sort(() => 0.5 - Math.random()) 52 | 53 | if (getIsMainnet() && !skipImportant) { 54 | // Make sure some important addresses get into our list: 55 | shuffled.unshift(...IMPORTANT_ADDRESSES) 56 | } 57 | 58 | const sliced = shuffled.slice(0, amount) 59 | 60 | return sliced 61 | } 62 | -------------------------------------------------------------------------------- /pepe-subgraph/tests/contract.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | describe, 4 | test, 5 | clearStore, 6 | beforeAll, 7 | afterAll 8 | } from "matchstick-as/assembly/index" 9 | import { Address, BigInt } from "@graphprotocol/graph-ts" 10 | import { Approval } from "../generated/schema" 11 | import { Approval as ApprovalEvent } from "../generated/Contract/Contract" 12 | import { handleApproval } from "../src/contract" 13 | import { createApprovalEvent } from "./contract-utils" 14 | 15 | // Tests structure (matchstick-as >=0.5.0) 16 | // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 17 | 18 | describe("Describe entity assertions", () => { 19 | beforeAll(() => { 20 | let owner = Address.fromString("0x0000000000000000000000000000000000000001") 21 | let spender = Address.fromString( 22 | "0x0000000000000000000000000000000000000001" 23 | ) 24 | let value = BigInt.fromI32(234) 25 | let newApprovalEvent = createApprovalEvent(owner, spender, value) 26 | handleApproval(newApprovalEvent) 27 | }) 28 | 29 | afterAll(() => { 30 | clearStore() 31 | }) 32 | 33 | // For more test scenarios, see: 34 | // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test 35 | 36 | test("Approval created and stored", () => { 37 | assert.entityCount("Approval", 1) 38 | 39 | // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function 40 | assert.fieldEquals( 41 | "Approval", 42 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 43 | "owner", 44 | "0x0000000000000000000000000000000000000001" 45 | ) 46 | assert.fieldEquals( 47 | "Approval", 48 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 49 | "spender", 50 | "0x0000000000000000000000000000000000000001" 51 | ) 52 | assert.fieldEquals( 53 | "Approval", 54 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 55 | "value", 56 | "234" 57 | ) 58 | 59 | // More assert options: 60 | // https://thegraph.com/docs/en/developer/matchstick/#asserts 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /friendtech-by-chainstack/tests/friendtech-shares-v-1.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | describe, 4 | test, 5 | clearStore, 6 | beforeAll, 7 | afterAll 8 | } from "matchstick-as/assembly/index" 9 | import { Address, BigInt } from "@graphprotocol/graph-ts" 10 | import { OwnershipTransferred } from "../generated/schema" 11 | import { OwnershipTransferred as OwnershipTransferredEvent } from "../generated/FriendtechSharesV1/FriendtechSharesV1" 12 | import { handleOwnershipTransferred } from "../src/friendtech-shares-v-1" 13 | import { createOwnershipTransferredEvent } from "./friendtech-shares-v-1-utils" 14 | 15 | // Tests structure (matchstick-as >=0.5.0) 16 | // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 17 | 18 | describe("Describe entity assertions", () => { 19 | beforeAll(() => { 20 | let previousOwner = Address.fromString( 21 | "0x0000000000000000000000000000000000000001" 22 | ) 23 | let newOwner = Address.fromString( 24 | "0x0000000000000000000000000000000000000001" 25 | ) 26 | let newOwnershipTransferredEvent = createOwnershipTransferredEvent( 27 | previousOwner, 28 | newOwner 29 | ) 30 | handleOwnershipTransferred(newOwnershipTransferredEvent) 31 | }) 32 | 33 | afterAll(() => { 34 | clearStore() 35 | }) 36 | 37 | // For more test scenarios, see: 38 | // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test 39 | 40 | test("OwnershipTransferred created and stored", () => { 41 | assert.entityCount("OwnershipTransferred", 1) 42 | 43 | // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function 44 | assert.fieldEquals( 45 | "OwnershipTransferred", 46 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 47 | "previousOwner", 48 | "0x0000000000000000000000000000000000000001" 49 | ) 50 | assert.fieldEquals( 51 | "OwnershipTransferred", 52 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 53 | "newOwner", 54 | "0x0000000000000000000000000000000000000001" 55 | ) 56 | 57 | // More assert options: 58 | // https://thegraph.com/docs/en/developer/matchstick/#asserts 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /lido-subgraph/README.md: -------------------------------------------------------------------------------- 1 | # Lido Subgraph 2 | 3 | Subgraph to index Lido contracts. 4 | 5 | ## Live Deployments 6 | 7 | ### The Graph Network 8 | 9 | Explorer page with playground: 10 | https://thegraph.com/explorer/subgraph?id=HXfMc1jPHfFQoccWd7VMv66km75FoxVHDMvsJj5vG5vf 11 | 12 | GraphQL API url (API key is needed): 13 | https://gateway.thegraph.com/api/[api-key]/subgraphs/id/HXfMc1jPHfFQoccWd7VMv66km75FoxVHDMvsJj5vG5vf 14 | 15 | ### The Graph Hosted 16 | 17 | #### Mainnet 18 | 19 | Explorer page with playground: 20 | https://thegraph.com/legacy-explorer/subgraph/lidofinance/lido 21 | 22 | GraphQL API url: 23 | https://api.thegraph.com/subgraphs/name/lidofinance/lido 24 | 25 | #### Testnet (Goerli) 26 | 27 | Explorer page with playground: 28 | https://thegraph.com/legacy-explorer/subgraph/lidofinance/lido-testnet 29 | 30 | GraphQL API url: 31 | https://api.thegraph.com/subgraphs/name/lidofinance/lido-testnet 32 | 33 | ## Contracts 34 | 35 | - Lido 36 | - Lido Oracle 37 | - Node Operator Registry 38 | - Voting 39 | - EasyTrack 40 | - Deposit Security Module 41 | 42 | ## Developing 43 | 44 | Install dependencies with `yarn` and run `yarn codegen`. Repeat `yarn codegen` after any schema changes or changes affecting generated files. 45 | 46 | ## Testing 47 | 48 | You can test any synced Lido deployment, simply fill an `.env` file and run: 49 | 50 | ``` 51 | yarn test 52 | ``` 53 | 54 | ## Deploying 55 | 56 | ### Locally 57 | 58 | First, set an `.env` file. You can check an example in `.env.local.example`. 59 | 60 | Run `create-local` first if Subgraph does not exist yet. 61 | Run `deploy-local` to deploy the Subgraph. 62 | 63 | ### The Graph Hosted 64 | 65 | Pushes to master branch will automatically get the Subgraph deployed to The Graph. 66 | 67 | ## Notes 68 | 69 | 1. Addresses are stored as-is, without conversion to checksum addresses. Keep that in mind when filtering entities by address. 70 | 2. Please note that it's now advised not to rely on this Subgraph's node operator keys for duplicate key checks. We've hit a technical limitation on withdrawal credentials changes when unused keys are cropped. We can't guarantee cropped keys will be deleted from this Subgraph correctly in the future. 71 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/findTotalsMismatches.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql, lidoFuncCall, Big } from './utils.js' 2 | 3 | const firstSubmitQuery = gql` 4 | query { 5 | lidoSubmissions(orderBy: block, orderDirection: asc, limit: 1) { 6 | block 7 | } 8 | } 9 | ` 10 | 11 | const lastIndexedQuery = gql` 12 | query { 13 | _meta { 14 | block { 15 | number 16 | } 17 | } 18 | } 19 | ` 20 | 21 | const genQuery = (block) => gql` 22 | query { 23 | totals(id: "", block: { number: ${block} }) { 24 | totalPooledEther 25 | totalShares 26 | } 27 | } 28 | ` 29 | 30 | const startBlock = parseInt( 31 | (await subgraphFetch(firstSubmitQuery)).lidoSubmissions[0].block 32 | ) 33 | const endBlock = parseInt( 34 | (await subgraphFetch(lastIndexedQuery))._meta.block.number 35 | ) 36 | 37 | let min = startBlock 38 | let max = endBlock 39 | 40 | while (min <= max) { 41 | const mid = Math.floor((min + max) / 2) 42 | 43 | const ethEther = Big( 44 | await lidoFuncCall('getTotalPooledEther', { 45 | blockTag: mid, 46 | }) 47 | ) 48 | const ethShares = Big( 49 | await lidoFuncCall('getTotalShares', { 50 | blockTag: mid, 51 | }) 52 | ) 53 | 54 | const subgraphData = await subgraphFetch(genQuery(mid)) 55 | const subEther = Big(subgraphData.totals.totalPooledEther) 56 | const subShares = Big(subgraphData.totals.totalShares) 57 | 58 | const isGood = ethEther.eq(subEther) && ethShares.eq(subShares) 59 | 60 | if (isGood) { 61 | // Check doesn't fail yet, going up 62 | min = mid + 1 63 | } else { 64 | // Check already fails, need to go down 65 | max = mid 66 | } 67 | 68 | if (min === max && !isGood) { 69 | // Found it 70 | if (!ethEther.eq(subEther)) { 71 | console.log( 72 | 'Ether mismatch @', 73 | mid, 74 | ethEther.toString(), 75 | '<->', 76 | subEther.toString() 77 | ) 78 | } 79 | if (!ethShares.eq(subShares)) { 80 | console.log( 81 | 'Shares mismatch @', 82 | mid, 83 | ethShares.toString(), 84 | '<->', 85 | subShares.toString() 86 | ) 87 | } 88 | process.exit() 89 | } 90 | } 91 | 92 | console.log('No mismatches found!') 93 | -------------------------------------------------------------------------------- /pepe-subgraph/tests/contract-utils.ts: -------------------------------------------------------------------------------- 1 | import { newMockEvent } from "matchstick-as" 2 | import { ethereum, Address, BigInt } from "@graphprotocol/graph-ts" 3 | import { 4 | Approval, 5 | OwnershipTransferred, 6 | Transfer 7 | } from "../generated/Contract/Contract" 8 | 9 | export function createApprovalEvent( 10 | owner: Address, 11 | spender: Address, 12 | value: BigInt 13 | ): Approval { 14 | let approvalEvent = changetype(newMockEvent()) 15 | 16 | approvalEvent.parameters = new Array() 17 | 18 | approvalEvent.parameters.push( 19 | new ethereum.EventParam("owner", ethereum.Value.fromAddress(owner)) 20 | ) 21 | approvalEvent.parameters.push( 22 | new ethereum.EventParam("spender", ethereum.Value.fromAddress(spender)) 23 | ) 24 | approvalEvent.parameters.push( 25 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 26 | ) 27 | 28 | return approvalEvent 29 | } 30 | 31 | export function createOwnershipTransferredEvent( 32 | previousOwner: Address, 33 | newOwner: Address 34 | ): OwnershipTransferred { 35 | let ownershipTransferredEvent = changetype( 36 | newMockEvent() 37 | ) 38 | 39 | ownershipTransferredEvent.parameters = new Array() 40 | 41 | ownershipTransferredEvent.parameters.push( 42 | new ethereum.EventParam( 43 | "previousOwner", 44 | ethereum.Value.fromAddress(previousOwner) 45 | ) 46 | ) 47 | ownershipTransferredEvent.parameters.push( 48 | new ethereum.EventParam("newOwner", ethereum.Value.fromAddress(newOwner)) 49 | ) 50 | 51 | return ownershipTransferredEvent 52 | } 53 | 54 | export function createTransferEvent( 55 | from: Address, 56 | to: Address, 57 | value: BigInt 58 | ): Transfer { 59 | let transferEvent = changetype(newMockEvent()) 60 | 61 | transferEvent.parameters = new Array() 62 | 63 | transferEvent.parameters.push( 64 | new ethereum.EventParam("from", ethereum.Value.fromAddress(from)) 65 | ) 66 | transferEvent.parameters.push( 67 | new ethereum.EventParam("to", ethereum.Value.fromAddress(to)) 68 | ) 69 | transferEvent.parameters.push( 70 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 71 | ) 72 | 73 | return transferEvent 74 | } 75 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/calculateSharesCeiling.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { subgraphFetch, BigNumber } from './index.js' 3 | 4 | export const calculateSharesCeiling = async (address) => { 5 | const submissionsQuery = gql` 6 | query ($first: Int, $skip: Int, $block: Block_height) { 7 | lidoSubmissions(first: $first, skip: $skip, block: $block, where: { sender: "${address}" }) { 8 | amount 9 | shares 10 | block 11 | } 12 | } 13 | ` 14 | 15 | const transfersInboundQuery = gql` 16 | query ($first: Int, $skip: Int, $block: Block_height) { 17 | lidoTransfers(first: $first, skip: $skip, block: $block, where: { to: "${address}" }) { 18 | shares 19 | to 20 | block 21 | } 22 | } 23 | ` 24 | 25 | const transfersOutboundQuery = gql` 26 | query ($first: Int, $skip: Int, $block: Block_height) { 27 | lidoTransfers(first: $first, skip: $skip, block: $block, where: { from: "${address}" }) { 28 | shares 29 | to 30 | block 31 | } 32 | } 33 | ` 34 | 35 | const submissions = (await subgraphFetch(submissionsQuery)).lidoSubmissions 36 | const transfersInbound = (await subgraphFetch(transfersInboundQuery)) 37 | .lidoTransfers 38 | const transfersOutbound = (await subgraphFetch(transfersOutboundQuery)) 39 | .lidoTransfers 40 | 41 | const together = [ 42 | ...submissions.map((x) => ({ ...x, type: 'submission' })), 43 | ...transfersInbound.map((x) => ({ 44 | ...x, 45 | type: 'transfer', 46 | direction: 'inbound', 47 | })), 48 | ...transfersOutbound.map((x) => ({ 49 | ...x, 50 | type: 'transfer', 51 | direction: 'outbound', 52 | })), 53 | ].sort((a, b) => a.block - b.block) 54 | 55 | let shares = BigNumber.from(0) 56 | let ceiling = BigNumber.from(0) 57 | 58 | for (const item of together) { 59 | const isStaking = item.type === 'submission' 60 | const isOut = !isStaking && item.direction === 'outbound' 61 | 62 | const txShares = item.shares || BigNumber.from(0) 63 | 64 | shares = isOut ? shares.sub(txShares) : shares.add(txShares) 65 | 66 | if (shares.gt(ceiling)) { 67 | ceiling = shares 68 | } 69 | } 70 | 71 | return ceiling 72 | } 73 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/calculateShares.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { subgraphFetch, BigNumber } from './index.js' 3 | 4 | export const calculateShares = async (address) => { 5 | const submissionsQuery = gql` 6 | query ($first: Int, $skip: Int, $block: Block_height) { 7 | lidoSubmissions( 8 | first: $first 9 | skip: $skip 10 | block: $block 11 | where: { sender: "${address}" } 12 | ) { 13 | amount 14 | shares 15 | block 16 | } 17 | } 18 | ` 19 | 20 | const transfersInboundQuery = gql` 21 | query ($first: Int, $skip: Int, $block: Block_height) { 22 | lidoTransfers( 23 | first: $first 24 | skip: $skip 25 | block: $block 26 | where: { to: "${address}" } 27 | ) { 28 | shares 29 | to 30 | block 31 | } 32 | } 33 | ` 34 | 35 | const transfersOutboundQuery = gql` 36 | query ($first: Int, $skip: Int, $block: Block_height) { 37 | lidoTransfers( 38 | first: $first 39 | skip: $skip 40 | block: $block 41 | where: { from: "${address}" } 42 | ) { 43 | shares 44 | to 45 | block 46 | } 47 | } 48 | ` 49 | const submissions = (await subgraphFetch(submissionsQuery)).lidoSubmissions 50 | const transfersInbound = (await subgraphFetch(transfersInboundQuery)) 51 | .lidoTransfers 52 | const transfersOutbound = (await subgraphFetch(transfersOutboundQuery)) 53 | .lidoTransfers 54 | 55 | const together = [ 56 | ...submissions.map((x) => ({ ...x, type: 'submission' })), 57 | ...transfersInbound.map((x) => ({ 58 | ...x, 59 | type: 'transfer', 60 | direction: 'inbound', 61 | })), 62 | ...transfersOutbound.map((x) => ({ 63 | ...x, 64 | type: 'transfer', 65 | direction: 'outbound', 66 | })), 67 | ].sort((a, b) => a.block - b.block) 68 | 69 | let shares = BigNumber.from(0) 70 | 71 | for (const item of together) { 72 | const isStaking = item.type === 'submission' 73 | const isOut = !isStaking && item.direction === 'outbound' 74 | 75 | const txShares = item.shares || BigNumber.from(0) 76 | 77 | shares = isOut ? shares.sub(txShares) : shares.add(txShares) 78 | } 79 | 80 | return shares 81 | } 82 | -------------------------------------------------------------------------------- /lido-subgraph/tests/rewardsBalanceChanges.test.off.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { 3 | getBalanceFromShares, 4 | subgraphFetch, 5 | BigNumber, 6 | } from './utils/index.js' 7 | 8 | const ADDRESSES_TO_TEST = 3 9 | const timePerAddress = 100 // seconds 10 | const timeout = ADDRESSES_TO_TEST * timePerAddress * 1000 // in ms 11 | 12 | /** 13 | NOTE: This test takes very long time per address. 14 | **/ 15 | 16 | // Example USD values from 03.09.2021 17 | const sharesChecks = [ 18 | '17253336101171480', // 70usd 19 | '453884371982397608502', // 2mil usd 20 | '22253111414175281724765', // 90mil usd 21 | ].map((x) => BigNumber.from(x)) 22 | 23 | const ratioQuery = gql` 24 | query ($first: Int, $skip: Int, $block: Block_height) { 25 | totalRewards( 26 | first: $first 27 | skip: $skip 28 | block: $block 29 | orderBy: block 30 | orderDirection: asc 31 | ) { 32 | totalPooledEtherBefore 33 | totalPooledEtherAfter 34 | totalSharesBefore 35 | totalSharesAfter 36 | 37 | block 38 | } 39 | } 40 | ` 41 | 42 | test( 43 | 'rewards balance changes', 44 | async () => { 45 | const reports = (await subgraphFetch(ratioQuery)).totalRewards 46 | 47 | for (const shares of sharesChecks) { 48 | console.log('Checking shares', shares.toString()) 49 | 50 | for (let report of reports) { 51 | const balanceBeforeSubgraph = shares 52 | .mul(report.totalPooledEtherBefore) 53 | .div(report.totalSharesBefore) 54 | 55 | const balanceAfterSubgraph = shares 56 | .mul(report.totalPooledEtherAfter) 57 | .div(report.totalSharesAfter) 58 | 59 | const rewardsSubgraph = balanceAfterSubgraph 60 | .sub(balanceBeforeSubgraph) 61 | .toString() 62 | 63 | const balanceBeforeReal = await getBalanceFromShares(shares, { 64 | blockTag: parseInt(report.block) - 1, 65 | }) 66 | const balanceAfterReal = await getBalanceFromShares(shares, { 67 | blockTag: parseInt(report.block), 68 | }) 69 | 70 | const rewardsReal = BigNumber.from(balanceAfterReal) 71 | .sub(balanceBeforeReal) 72 | .toString() 73 | 74 | expect(rewardsSubgraph).toEqual(rewardsReal) 75 | } 76 | } 77 | }, 78 | timeout 79 | ) 80 | -------------------------------------------------------------------------------- /friendtech-by-chainstack/tests/friendtech-shares-v-1-utils.ts: -------------------------------------------------------------------------------- 1 | import { newMockEvent } from "matchstick-as" 2 | import { ethereum, Address, BigInt } from "@graphprotocol/graph-ts" 3 | import { 4 | OwnershipTransferred, 5 | Trade 6 | } from "../generated/FriendtechSharesV1/FriendtechSharesV1" 7 | 8 | export function createOwnershipTransferredEvent( 9 | previousOwner: Address, 10 | newOwner: Address 11 | ): OwnershipTransferred { 12 | let ownershipTransferredEvent = changetype( 13 | newMockEvent() 14 | ) 15 | 16 | ownershipTransferredEvent.parameters = new Array() 17 | 18 | ownershipTransferredEvent.parameters.push( 19 | new ethereum.EventParam( 20 | "previousOwner", 21 | ethereum.Value.fromAddress(previousOwner) 22 | ) 23 | ) 24 | ownershipTransferredEvent.parameters.push( 25 | new ethereum.EventParam("newOwner", ethereum.Value.fromAddress(newOwner)) 26 | ) 27 | 28 | return ownershipTransferredEvent 29 | } 30 | 31 | export function createTradeEvent( 32 | trader: Address, 33 | subject: Address, 34 | isBuy: boolean, 35 | shareAmount: BigInt, 36 | ethAmount: BigInt, 37 | protocolEthAmount: BigInt, 38 | subjectEthAmount: BigInt, 39 | supply: BigInt 40 | ): Trade { 41 | let tradeEvent = changetype(newMockEvent()) 42 | 43 | tradeEvent.parameters = new Array() 44 | 45 | tradeEvent.parameters.push( 46 | new ethereum.EventParam("trader", ethereum.Value.fromAddress(trader)) 47 | ) 48 | tradeEvent.parameters.push( 49 | new ethereum.EventParam("subject", ethereum.Value.fromAddress(subject)) 50 | ) 51 | tradeEvent.parameters.push( 52 | new ethereum.EventParam("isBuy", ethereum.Value.fromBoolean(isBuy)) 53 | ) 54 | tradeEvent.parameters.push( 55 | new ethereum.EventParam( 56 | "shareAmount", 57 | ethereum.Value.fromUnsignedBigInt(shareAmount) 58 | ) 59 | ) 60 | tradeEvent.parameters.push( 61 | new ethereum.EventParam( 62 | "ethAmount", 63 | ethereum.Value.fromUnsignedBigInt(ethAmount) 64 | ) 65 | ) 66 | tradeEvent.parameters.push( 67 | new ethereum.EventParam( 68 | "protocolEthAmount", 69 | ethereum.Value.fromUnsignedBigInt(protocolEthAmount) 70 | ) 71 | ) 72 | tradeEvent.parameters.push( 73 | new ethereum.EventParam( 74 | "subjectEthAmount", 75 | ethereum.Value.fromUnsignedBigInt(subjectEthAmount) 76 | ) 77 | ) 78 | tradeEvent.parameters.push( 79 | new ethereum.EventParam("supply", ethereum.Value.fromUnsignedBigInt(supply)) 80 | ) 81 | 82 | return tradeEvent 83 | } 84 | -------------------------------------------------------------------------------- /lido-subgraph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lido-subgraph", 3 | "license": "MIT", 4 | "scripts": { 5 | "codegen": "graph codegen subgraph.mainnet.yaml", 6 | "codegen-testnet": "graph codegen subgraph.testnet.yaml", 7 | "build": "graph build subgraph.mainnet.yaml", 8 | "build-testnet": "graph build subgraph.testnet.yaml", 9 | "test": "yarn test:errors && yarn test:sync && yarn test:withoutStatus", 10 | "test:hosted": "yarn test:errors && yarn test:sync && yarn test:withoutStatus --testPathIgnorePatterns addressShares balances", 11 | "test:withoutStatus": "NODE_OPTIONS=--experimental-vm-modules jest --testPathIgnorePatterns isSynced isErrored", 12 | "test:sync": "NODE_OPTIONS=--experimental-vm-modules jest tests/isSynced.test.js", 13 | "test:errors": "NODE_OPTIONS=--experimental-vm-modules jest tests/isNotErrored.test.js", 14 | "test:totals": "yarn test:sync && NODE_OPTIONS=--experimental-vm-modules jest tests/totalPooledEth.test.js tests/totalShares.test.js", 15 | "test:dust": "NODE_OPTIONS=--experimental-vm-modules jest tests/noWrongDust.test.js", 16 | "test:graphBalance": "NODE_OPTIONS=--experimental-vm-modules yarn jest --testMatch='**/tests/graphBalance.test.off.js'", 17 | "test:smoke": "NODE_OPTIONS=--experimental-vm-modules yarn jest --testMatch='**/tests/smoke.test.js'", 18 | "unit-test": "graph test", 19 | "deploy": "graph deploy --product hosted-service --node https://api.thegraph.com/deploy/ --ipfs https://api.thegraph.com/ipfs/ lidofinance/lido subgraph.mainnet.yaml", 20 | "deploy-testnet": "graph deploy --product hosted-service --node https://api.thegraph.com/deploy/ --ipfs https://api.thegraph.com/ipfs/ lidofinance/lido-testnet subgraph.testnet.yaml", 21 | "create-local": "env-cmd --use-shell graph create --node '$GRAPH_NODE' lidofinance/lido", 22 | "remove-local": "env-cmd --use-shell graph remove --node '$GRAPH_NODE' lidofinance/lido", 23 | "deploy-local": "env-cmd --use-shell graph deploy --node '$GRAPH_NODE' --ipfs '$GRAPH_IPFS' lidofinance/lido subgraph.mainnet.yaml", 24 | "create-local-testnet": "env-cmd --use-shell graph create --node '$GRAPH_NODE' lidofinance/lido-testnet", 25 | "remove-local-testnet": "env-cmd --use-shell graph remove --node '$GRAPH_NODE' lidofinance/lido-testnet", 26 | "deploy-local-testnet": "env-cmd --use-shell graph deploy --node '$GRAPH_NODE' --ipfs '$GRAPH_IPFS' lidofinance/lido-testnet subgraph.testnet.yaml" 27 | }, 28 | "dependencies": { 29 | "@graphprotocol/graph-cli": "0.35.0", 30 | "@graphprotocol/graph-ts": "0.29.0" 31 | }, 32 | "devDependencies": { 33 | "@jest/types": "29.3.1", 34 | "dotenv": "16.0.3", 35 | "env-cmd": "10.1.0", 36 | "ethers": "5.7.2", 37 | "graphql": "16.6.0", 38 | "graphql-request": "5.0.0", 39 | "jest": "29.3.1", 40 | "matchstick-as": "0.5.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lido-subgraph/src/constants.ts: -------------------------------------------------------------------------------- 1 | import { BigInt, Address, TypedMap } from '@graphprotocol/graph-ts' 2 | import { dataSource } from '@graphprotocol/graph-ts' 3 | 4 | import { Settings } from '../generated/schema' 5 | 6 | const network = dataSource.network() 7 | const isMainnet = network == 'mainnet' 8 | 9 | /** 10 | Units 11 | **/ 12 | 13 | export const ZERO = BigInt.fromI32(0) 14 | export const ONE = BigInt.fromI32(1) 15 | 16 | export const CALCULATION_UNIT = BigInt.fromI32(10000) 17 | 18 | // 1 ETH in WEI 19 | export const ETHER = BigInt.fromString('1000000000000000000') 20 | 21 | /** 22 | Deposits 23 | **/ 24 | 25 | export const DEPOSIT_SIZE = BigInt.fromI32(32) 26 | export const DEPOSIT_AMOUNT = DEPOSIT_SIZE.times(ETHER) // in Wei 27 | 28 | /** 29 | Oracle 30 | **/ 31 | 32 | // Buffer of oracle runs if we underestimated the number 33 | export const ORACLE_RUNS_BUFFER = BigInt.fromI32(50) 34 | 35 | export const MAINNET_FIRST_ORACLE_REPORT = BigInt.fromI32(1610016625) // block 11607098 36 | export const TESTNET_FIRST_ORACLE_REPORT = BigInt.fromI32(1617282681) // block 4543056 37 | 38 | // Oracle report period is dependent on network (eg much often on testnet) 39 | export const MAINNET_ORACLE_PERIOD = BigInt.fromI32(86400) // 1 day 40 | export const TESTNET_ORACLE_PERIOD = BigInt.fromI32(3840) // 10 epochs by ~6.4 minutes 41 | 42 | export const getFirstOracleReport = (): BigInt => 43 | isMainnet ? MAINNET_FIRST_ORACLE_REPORT : TESTNET_FIRST_ORACLE_REPORT 44 | 45 | export const getOraclePeriod = (): BigInt => 46 | isMainnet ? MAINNET_ORACLE_PERIOD : TESTNET_ORACLE_PERIOD 47 | 48 | /** 49 | Addresses 50 | **/ 51 | 52 | export const ZERO_ADDRESS = Address.fromString( 53 | '0x0000000000000000000000000000000000000000' 54 | ) 55 | 56 | const LIDO_ADDRESSES = new TypedMap() 57 | LIDO_ADDRESSES.set('mainnet', '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84') 58 | LIDO_ADDRESSES.set('goerli', '0x1643E812aE58766192Cf7D2Cf9567dF2C37e9B7F') 59 | 60 | const NOS_ADDRESSES = new TypedMap() 61 | NOS_ADDRESSES.set('mainnet', '0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5') 62 | NOS_ADDRESSES.set('goerli', '0x9D4AF1Ee19Dad8857db3a45B0374c81c8A1C6320') 63 | 64 | const TREASURY_ADDRESSES = new TypedMap() 65 | TREASURY_ADDRESSES.set('mainnet', '0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c') 66 | TREASURY_ADDRESSES.set('goerli', '0x4333218072D5d7008546737786663c38B4D561A4') 67 | 68 | // We presume here that initially insurance fund was the treasury 69 | const getInsuranceFund = (): string => 70 | Settings.load('') 71 | ? Settings.load('')!.insuranceFund.toHex() 72 | : TREASURY_ADDRESSES.get(network)! 73 | 74 | export const getAddress = (contract: string): Address => 75 | Address.fromString( 76 | (contract == 'Lido' 77 | ? LIDO_ADDRESSES.get(network) 78 | : contract == 'NodeOperatorsRegistry' 79 | ? NOS_ADDRESSES.get(network) 80 | : contract == 'Insurance Fund' 81 | ? getInsuranceFund() 82 | : contract == 'Treasury' 83 | ? TREASURY_ADDRESSES.get(network) 84 | : null)! 85 | ) 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Reference/ 2 | .vscode/ 3 | 4 | versions.ts 5 | deploymentJsonContext.json 6 | **/configurations/configure.ts 7 | generated/ 8 | build/ 9 | 10 | **/.DS_Store 11 | 12 | ############################ 13 | ##### Default Template ##### 14 | ############################ 15 | 16 | # Logs 17 | logs 18 | *.log 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | lerna-debug.log* 23 | .pnpm-debug.log* 24 | 25 | # Diagnostic reports (https://nodejs.org/api/report.html) 26 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 27 | 28 | # Runtime data 29 | pids 30 | *.pid 31 | *.seed 32 | *.pid.lock 33 | 34 | # Directory for instrumented libs generated by jscoverage/JSCover 35 | lib-cov 36 | 37 | # Coverage directory used by tools like istanbul 38 | coverage 39 | *.lcov 40 | 41 | # nyc test coverage 42 | .nyc_output 43 | 44 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 45 | .grunt 46 | 47 | # Bower dependency directory (https://bower.io/) 48 | bower_components 49 | 50 | # node-waf configuration 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | build/Release 55 | 56 | # Dependency directories 57 | node_modules/ 58 | jspm_packages/ 59 | 60 | # Text generating from running deployment scripts 61 | results.txt 62 | 63 | # Snowpack dependency directory (https://snowpack.dev/) 64 | web_modules/ 65 | 66 | # TypeScript cache 67 | *.tsbuildinfo 68 | 69 | # Optional npm cache directory 70 | .npm 71 | 72 | # Optional eslint cache 73 | .eslintcache 74 | 75 | # Optional stylelint cache 76 | .stylelintcache 77 | 78 | # Microbundle cache 79 | .rpt2_cache/ 80 | .rts2_cache_cjs/ 81 | .rts2_cache_es/ 82 | .rts2_cache_umd/ 83 | 84 | # Optional REPL history 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | *.tgz 89 | 90 | # Yarn Integrity file 91 | .yarn-integrity 92 | 93 | # dotenv environment variable files 94 | .env 95 | .env.development.local 96 | .env.test.local 97 | .env.production.local 98 | .env.local 99 | 100 | # parcel-bundler cache (https://parceljs.org/) 101 | .cache 102 | .parcel-cache 103 | 104 | # Next.js build output 105 | .next 106 | out 107 | 108 | # Nuxt.js build / generate output 109 | .nuxt 110 | dist 111 | 112 | # Gatsby files 113 | .cache/ 114 | # Comment in the public line in if your project uses Gatsby and not Next.js 115 | # https://nextjs.org/blog/next-9-1#public-directory-support 116 | # public 117 | 118 | # vuepress build output 119 | .vuepress/dist 120 | 121 | # vuepress v2.x temp and cache directory 122 | .temp 123 | .cache 124 | 125 | # Docusaurus cache and generated files 126 | .docusaurus 127 | 128 | # Serverless directories 129 | .serverless/ 130 | 131 | # FuseBox cache 132 | .fusebox/ 133 | 134 | # DynamoDB Local files 135 | .dynamodb/ 136 | 137 | # TernJS port file 138 | .tern-port 139 | 140 | # Stores VSCode versions used for testing VSCode extensions 141 | .vscode-test 142 | 143 | # yarn v2 144 | .yarn/cache 145 | .yarn/unplugged 146 | .yarn/build-state.yml 147 | .yarn/install-state.gz 148 | .pnp.* 149 | 150 | # WebStorm cache 151 | .idea 152 | 153 | # Matchstick generated file 154 | .bin 155 | -------------------------------------------------------------------------------- /lido-subgraph/tests/smoke.test.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { getBlock } from './config' 3 | import { 4 | getAragonEvents, 5 | getDSMEvents, 6 | getEasyTrackEvents, 7 | getLidoEvents, 8 | getLidoOracleEvents, 9 | getNopRegistryEvents, 10 | subgraphFetch 11 | } from './utils' 12 | 13 | const SECS_PER_BLOCK = 12 14 | const BLOCKS_RANGE_3HOURS = Math.floor((3 * 60 * 60) / SECS_PER_BLOCK) 15 | const BLOCKS_RANGE_1MONTH = Math.floor((30 * 24 * 60 * 60) / SECS_PER_BLOCK) 16 | 17 | test('LidoTransfer', async () => { 18 | const startBlock = getBlock() - BLOCKS_RANGE_3HOURS 19 | const someTransfers = await getLidoEvents('Transfer', startBlock) 20 | const event = someTransfers.pop() 21 | expect(event).toBeDefined() 22 | 23 | const id = event.transactionHash + '-' + event.logIndex 24 | const query = gql` 25 | query($id: ID!) { 26 | lidoTransfer(id: $id) { 27 | block 28 | } 29 | } 30 | ` 31 | const response = await subgraphFetch(query, { id }) 32 | 33 | expect(response?.lidoTransfer?.block).toEqual(String(event.blockNumber)) 34 | }) 35 | 36 | test('OracleMember', async () => { 37 | const events = await getLidoOracleEvents('MemberAdded') 38 | const event = events.pop() 39 | 40 | const id = event.args.member 41 | const query = gql` 42 | query($id: ID!) { 43 | oracleMember(id: $id) { 44 | member 45 | } 46 | } 47 | ` 48 | const response = await subgraphFetch(query, { id }) 49 | 50 | expect(response?.oracleMember?.member).toEqual(id.toLowerCase()) 51 | }) 52 | 53 | test('NodeOperator', async () => { 54 | const events = await getNopRegistryEvents('NodeOperatorAdded') 55 | const event = events.pop() 56 | 57 | const id = event.args.id.toString() 58 | 59 | const query = gql` 60 | query($id: ID!) { 61 | nodeOperator(id: $id) { 62 | name 63 | } 64 | } 65 | ` 66 | const response = await subgraphFetch(query, { id }) 67 | 68 | expect(response?.nodeOperator?.name).toEqual(event.args.name) 69 | }) 70 | 71 | test('AragonVoting', async () => { 72 | const startBlock = getBlock() - BLOCKS_RANGE_1MONTH 73 | const events = await getAragonEvents('StartVote', startBlock) 74 | const event = events.pop() 75 | expect(event).toBeDefined() 76 | 77 | const id = event.args.voteId.toString() 78 | 79 | const query = gql` 80 | query($id: ID!) { 81 | voting(id: $id) { 82 | creator 83 | } 84 | } 85 | ` 86 | const response = await subgraphFetch(query, { id }) 87 | 88 | expect(response?.voting?.creator).toEqual(event.args.creator.toLowerCase()) 89 | }) 90 | 91 | test('EasyTrack', async () => { 92 | const startBlock = getBlock() - BLOCKS_RANGE_1MONTH 93 | const events = await getEasyTrackEvents('MotionCreated', startBlock) 94 | const event = events.pop() 95 | expect(event).toBeDefined() 96 | 97 | const id = event.args._motionId.toString() 98 | 99 | const query = gql` 100 | query($id: ID!) { 101 | motion(id: $id) { 102 | creator 103 | } 104 | } 105 | ` 106 | const response = await subgraphFetch(query, { id }) 107 | 108 | expect(response?.motion?.creator).toEqual(event.args._creator.toLowerCase()) 109 | }) 110 | 111 | test('DepositSecurityModule Guardian', async () => { 112 | const events = await getDSMEvents('GuardianAdded') 113 | const event = events.pop() 114 | 115 | const id = event.args.guardian.toLowerCase() 116 | 117 | const query = gql` 118 | query($id: ID!) { 119 | guardian(id: $id) { 120 | block 121 | } 122 | } 123 | ` 124 | const response = await subgraphFetch(query, { id }) 125 | 126 | expect(response?.guardian?.block).toEqual(String(event.blockNumber)) 127 | }) 128 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/stethBalanceOverTime.js: -------------------------------------------------------------------------------- 1 | import { subgraphFetch, gql, getBalanceFromShares, BigNumber } from './utils.js' 2 | 3 | const rpcMode = false 4 | 5 | const initialPeriodEnabled = true 6 | const firstTxBlock = 11480180 7 | 8 | const tailingPeriodEnabled = true 9 | 10 | const stepBlocks = 100 11 | 12 | const genTotalsQuery = (block) => gql` 13 | { 14 | totals(id: "", block: { number: ${block} }) { 15 | totalPooledEther 16 | totalShares 17 | } 18 | } 19 | ` 20 | 21 | const ratioQuery = gql` 22 | { 23 | totalRewards(first: 10000, orderBy: block, orderDirection: asc) { 24 | block 25 | } 26 | } 27 | ` 28 | 29 | const lastSubmitQuery = gql` 30 | { 31 | lidoSubmissions(first: 1, orderBy: block, orderDirection: desc) { 32 | block 33 | } 34 | } 35 | ` 36 | 37 | // Example USD values from 03.09.2021 38 | const sharesChecks = [ 39 | '17253336101171480', // 70usd 40 | '453884371982397608502', // 2mil usd 41 | '22253111414175281724765', // 90mil usd 42 | ].map((x) => BigNumber.from(x)) 43 | 44 | for (const shares of sharesChecks) { 45 | console.log('Checking shares:', shares.toString()) 46 | 47 | const reports = (await subgraphFetch(ratioQuery)).totalRewards 48 | 49 | const lastBlock = (await subgraphFetch(lastSubmitQuery)).lidoSubmissions.block 50 | 51 | const periods = [] 52 | 53 | for (let i = 0; i < reports.length - 1; i++) { 54 | periods.push({ 55 | start: parseInt(reports[i].block), 56 | end: parseInt(reports[i + 1].block), 57 | }) 58 | } 59 | 60 | if (initialPeriodEnabled) { 61 | // Period before first rewards 62 | periods.unshift({ 63 | start: firstTxBlock, 64 | end: periods[0].start, 65 | }) 66 | } 67 | 68 | if (tailingPeriodEnabled) { 69 | // Period after last rewards 70 | periods.push({ 71 | start: periods.at(-1).end, 72 | end: lastBlock, 73 | }) 74 | } 75 | 76 | let fluctuationsNumber = 0 77 | let largestFluctuation = BigNumber.from(0) 78 | let totalOfFluctuations = BigNumber.from(0) 79 | 80 | for (const period of periods) { 81 | let firstBalance = null 82 | let last = null 83 | 84 | for (let block = period.start; block < period.end; block += stepBlocks) { 85 | let balance = null 86 | 87 | if (rpcMode) { 88 | balance = await getBalanceFromShares(shares, { blockTag: block }) 89 | } else { 90 | const totals = (await subgraphFetch(genTotalsQuery(block))).totals 91 | balance = shares.mul(totals.totalPooledEther).div(totals.totalShares) 92 | } 93 | 94 | // Save initial balance for the period 95 | if (block === period.start) { 96 | firstBalance = balance 97 | } 98 | 99 | if (last && !balance.eq(last)) { 100 | const fluctuation = balance.sub(last).abs() 101 | 102 | totalOfFluctuations = totalOfFluctuations.add(fluctuation) 103 | 104 | if (fluctuation.gt(largestFluctuation)) { 105 | largestFluctuation = fluctuation 106 | } 107 | 108 | fluctuationsNumber++ 109 | } 110 | 111 | last = balance 112 | } 113 | if (firstBalance) { 114 | const periodDifference = last.sub(firstBalance) 115 | if (periodDifference.gt(0)) { 116 | console.log( 117 | 'Start-End balance difference detected in period', 118 | period.start, 119 | '-', 120 | period.end, 121 | periodDifference.toString() 122 | ) 123 | } 124 | } 125 | } 126 | console.log('Fluctuations:', fluctuationsNumber) 127 | console.log('Largest Fluctuation:', largestFluctuation.toNumber()) 128 | console.log('Total Of Fluctuations:', totalOfFluctuations.toNumber()) 129 | } 130 | -------------------------------------------------------------------------------- /lido-subgraph/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { store, crypto } from '@graphprotocol/graph-ts' 2 | import { BigInt, Address, ByteArray } from '@graphprotocol/graph-ts' 3 | 4 | import { 5 | ORACLE_RUNS_BUFFER, 6 | getOraclePeriod, 7 | getFirstOracleReport, 8 | ZERO, 9 | ONE, 10 | } from './constants' 11 | 12 | export function guessOracleRunsTotal(currentblockTime: BigInt): BigInt { 13 | // We know when first Oracle report happened 14 | // We can find out for how long Oracle report have been happening 15 | // Knowing how often they happen, we can estimate how many reports there have been 16 | 17 | let currentFullDaysSinceEpoch = currentblockTime 18 | let oracleFirstDaySinceEpoch = getFirstOracleReport() 19 | 20 | let runningTime = currentFullDaysSinceEpoch.minus(oracleFirstDaySinceEpoch) 21 | 22 | // TODO: Can we improve this? 23 | // Writing this would round the number to zero: 24 | // let probableId = runningTime.div(getOraclePeriod()) 25 | // For it's best to overestimate than underestimate: 26 | let probableId = BigInt.fromI64( 27 | Math.ceil(runningTime.toI64() / getOraclePeriod().toI64()) 28 | ) 29 | 30 | // If estimation is requested before first report, number would be negative 31 | if (probableId.le(ZERO)) { 32 | return ZERO 33 | } 34 | 35 | // Our estimation is not 100% accurate - it needs a safety buffer 36 | // We will try to load this estimate and if it fails try n-1 each time until we load an entity successfully or reach zero 37 | return probableId.plus(ORACLE_RUNS_BUFFER) 38 | } 39 | 40 | export function lastIncrementalId(entityName: string, i: BigInt): string { 41 | // Wrong id, doesn't exist yet. Make sure it doesn't load. 42 | if (i.equals(ZERO)) { 43 | // 0 id doesn't exist (id start from 1), but 44 | // But allows us to still do newId = lastIncrementalId() + 1 45 | return ZERO.toString() 46 | } 47 | 48 | // Try to load entity with this id 49 | let entity = store.get(entityName, i.toString()) 50 | 51 | if (entity) { 52 | // It exists, return id 53 | return i.toString() 54 | } else { 55 | // It doesn't exist, trying id - 1 56 | return lastIncrementalId(entityName, i.minus(ONE)) 57 | } 58 | } 59 | 60 | export function nextIncrementalId(entityName: string, i: BigInt): string { 61 | if (i.equals(ZERO)) { 62 | // No entities, start from 1 63 | return ONE.toString() 64 | } 65 | 66 | // Try to load entity with this id 67 | let entity = store.get(entityName, i.toString()) 68 | 69 | if (entity) { 70 | let nextItem = i.plus(ONE) 71 | return nextItem.toString() 72 | } else { 73 | return nextIncrementalId(entityName, i.minus(ONE)) 74 | } 75 | } 76 | 77 | /** 78 | Temporary solution until conversion is implemented in Address: 79 | https://github.com/graphprotocol/support/issues/40 80 | **/ 81 | 82 | export function toChecksumAddress(address: Address): string { 83 | let lowerCaseAddress = address.toHex().slice(2) 84 | // note that this is actually a hash of the string representation of the hex without the "0x" 85 | let hash = crypto 86 | .keccak256(ByteArray.fromUTF8(address.toHex().slice(2))) 87 | .toHex() 88 | .slice(2) 89 | let result = '0x' 90 | 91 | for (let i = 0; i < lowerCaseAddress.length; i++) { 92 | if (parseInt(hash.charAt(i), 16) >= 8) { 93 | result += toUpper(lowerCaseAddress.charAt(i)) 94 | } else { 95 | result += lowerCaseAddress.charAt(i) 96 | } 97 | } 98 | 99 | return result 100 | } 101 | 102 | // because there is no String.toUpper() in assemblyscript 103 | function toUpper(str: string): string { 104 | let result = '' 105 | for (let i = 0; i < str.length; i++) { 106 | let charCode = str.charCodeAt(i) 107 | // only operate on lowercase 'a' through lower case 'z' 108 | if (charCode >= 97 && charCode <= 122) { 109 | result += String.fromCharCode(charCode - 32) 110 | } else { 111 | result += str.charAt(i) 112 | } 113 | } 114 | return result 115 | } 116 | -------------------------------------------------------------------------------- /each_block_handler_example/src/contract.ts: -------------------------------------------------------------------------------- 1 | import { ethereum } from '@graphprotocol/graph-ts' 2 | import { Block } from '../generated/schema' 3 | 4 | export function handleBlock(block: ethereum.Block): void { 5 | let blockEntity = Block.load('last') 6 | if (blockEntity == null) blockEntity = new Block('last') 7 | blockEntity.blockNumber = block.number 8 | blockEntity.timestamp = block.timestamp 9 | blockEntity.parentHash = block.parentHash 10 | blockEntity.save() 11 | 12 | // let id = block.hash.toHex() 13 | // let blockEntity = new Block(id) 14 | // blockEntity.blockNumber = block.number 15 | // blockEntity.timestamp = block.timestamp 16 | // blockEntity.parentHash = block.parentHash 17 | // blockEntity.save() 18 | } 19 | 20 | // 21 | // 22 | // export function handleIssue(event: Issue): void { 23 | // // Entities can be loaded from the store using a string ID; this ID 24 | // // needs to be unique across all entities of the same type 25 | // let entity = ExampleEntity.load(event.transaction.from) 26 | // 27 | // // Entities only exist after they have been saved to the store; 28 | // // `null` checks allow to create entities on demand 29 | // if (!entity) { 30 | // entity = new ExampleEntity(event.transaction.from) 31 | // 32 | // // Entity fields can be set using simple assignments 33 | // entity.count = BigInt.fromI32(0) 34 | // } 35 | // 36 | // // BigInt and BigDecimal math are supported 37 | // entity.count = entity.count + BigInt.fromI32(1) 38 | // 39 | // // Entity fields can be set based on event parameters 40 | // entity.amount = event.params.amount 41 | // 42 | // // Entities can be written to the store with `.save()` 43 | // entity.save() 44 | // 45 | // // Note: If a handler doesn't require existing field values, it is faster 46 | // // _not_ to load the entity from the store. Instead, create it fresh with 47 | // // `new Entity(...)`, set the fields that should be updated and save the 48 | // // entity back to the store. Fields that were not set or unset remain 49 | // // unchanged, allowing for partial updates to be applied. 50 | // 51 | // // It is also possible to access smart contracts from mappings. For 52 | // // example, the contract that has emitted the event can be connected to 53 | // // with: 54 | // // 55 | // // let contract = Contract.bind(event.address) 56 | // // 57 | // // The following functions can then be called on this contract to access 58 | // // state variables and other data: 59 | // // 60 | // // - contract.name(...) 61 | // // - contract.deprecated(...) 62 | // // - contract.totalSupply(...) 63 | // // - contract.upgradedAddress(...) 64 | // // - contract.balances(...) 65 | // // - contract.decimals(...) 66 | // // - contract.maximumFee(...) 67 | // // - contract._totalSupply(...) 68 | // // - contract.getBlackListStatus(...) 69 | // // - contract.allowed(...) 70 | // // - contract.paused(...) 71 | // // - contract.balanceOf(...) 72 | // // - contract.getOwner(...) 73 | // // - contract.owner(...) 74 | // // - contract.symbol(...) 75 | // // - contract.allowance(...) 76 | // // - contract.basisPointsRate(...) 77 | // // - contract.isBlackListed(...) 78 | // // - contract.MAX_UINT(...) 79 | // } 80 | // 81 | // export function handleRedeem(event: Redeem): void {} 82 | // 83 | // export function handleDeprecate(event: Deprecate): void {} 84 | // 85 | // export function handleParams(event: Params): void {} 86 | // 87 | // export function handleDestroyedBlackFunds(event: DestroyedBlackFunds): void {} 88 | // 89 | // export function handleAddedBlackList(event: AddedBlackList): void {} 90 | // 91 | // export function handleRemovedBlackList(event: RemovedBlackList): void {} 92 | // 93 | // export function handleApproval(event: Approval): void {} 94 | // 95 | // export function handleTransfer(event: Transfer): void {} 96 | // 97 | // export function handlePause(event: Pause): void {} 98 | // 99 | // export function handleUnpause(event: Unpause): void {} 100 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/ethCalls.js: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers' 2 | import fs from 'fs' 3 | 4 | import { 5 | ARAGON_ADDRESS, 6 | DSM_ADDRESS, 7 | EASYTRACK_ADDRESS, 8 | LIDO_ADDRESS, 9 | NOP_ADDRESS, 10 | RPC, 11 | getBlock 12 | } from '../config.js' 13 | 14 | const provider = new ethers.providers.JsonRpcProvider(RPC) 15 | 16 | const lidoAbi = JSON.parse(fs.readFileSync('abis/Lido.json')) 17 | const lidoContract = new ethers.Contract(LIDO_ADDRESS, lidoAbi, provider) 18 | 19 | const oracleAddress = await lidoContract.getOracle() 20 | const oracleAbi = JSON.parse(fs.readFileSync('abis/LidoOracle.json')) 21 | const oracleContract = new ethers.Contract(oracleAddress, oracleAbi, provider) 22 | 23 | const nopRegistryAbi = JSON.parse( 24 | fs.readFileSync('abis/NodeOperatorsRegistry.json') 25 | ) 26 | const nopRegistryContract = new ethers.Contract( 27 | NOP_ADDRESS, 28 | nopRegistryAbi, 29 | provider 30 | ) 31 | 32 | const aragonAbi = JSON.parse(fs.readFileSync('abis/Voting.json')) 33 | const aragonContract = new ethers.Contract(ARAGON_ADDRESS, aragonAbi, provider) 34 | 35 | const easyTrackAbi = JSON.parse(fs.readFileSync('abis/Easytrack.json')) 36 | const easyTrackContract = new ethers.Contract( 37 | EASYTRACK_ADDRESS, 38 | easyTrackAbi, 39 | provider 40 | ) 41 | 42 | const dsmAbi = JSON.parse(fs.readFileSync('abis/DepositSecurityModule.json')) 43 | const dsmContract = new ethers.Contract(DSM_ADDRESS, dsmAbi, provider) 44 | 45 | const mbAddBlock = async args => { 46 | const blockIsOverriden = args.find(x => x.blockTag) 47 | 48 | if (blockIsOverriden) { 49 | return args 50 | } 51 | 52 | const block = getBlock() 53 | 54 | args.push({ blockTag: block }) 55 | 56 | return args 57 | } 58 | 59 | export const ethCall = async (func, ...initialArgs) => 60 | await provider[func](...(await mbAddBlock(initialArgs))) 61 | 62 | export const lidoFuncCall = async (func, ...initialArgs) => 63 | await lidoContract[func](...(await mbAddBlock(initialArgs))) 64 | 65 | export const oracleFuncCall = async (func, ...initialArgs) => 66 | await oracleContract[func](...(await mbAddBlock(initialArgs))) 67 | 68 | export const getAddressShares = async (address, ...args) => 69 | await lidoFuncCall('sharesOf', address, ...args) 70 | 71 | export const getAddressBalance = async (address, ...args) => 72 | await lidoFuncCall('balanceOf', address, ...args) 73 | 74 | export const getBalanceFromShares = async (address, ...args) => 75 | await lidoFuncCall('getPooledEthByShares', address, ...args) 76 | 77 | export const getEvents = async (contract, eventName, startBlock, endBlock) => { 78 | const filter = contract.filters[eventName]() 79 | return await contract.queryFilter( 80 | filter, 81 | startBlock ?? 0, 82 | endBlock ?? getBlock() 83 | ) 84 | } 85 | 86 | export const getLidoEvents = async (eventName, startBlock) => { 87 | return await getEvents(lidoContract, eventName, startBlock) 88 | } 89 | 90 | export const getLidoEventNumber = async eventName => { 91 | return await getLidoEvents(eventName).length 92 | } 93 | 94 | export const getLidoOracleEvents = async (eventName, startBlock) => { 95 | return await getEvents(oracleContract, eventName, startBlock) 96 | } 97 | 98 | export const getOracleEventNumber = async (eventName, startBlock) => { 99 | return (await getLidoOracleEvents(eventName, startBlock)).length 100 | } 101 | 102 | export const getNopRegistryEvents = async (eventName, startBlock) => { 103 | return await getEvents(nopRegistryContract, eventName, startBlock) 104 | } 105 | 106 | export const getAragonEvents = async (eventName, startBlock) => { 107 | return await getEvents(aragonContract, eventName, startBlock) 108 | } 109 | 110 | export const getEasyTrackEvents = async (eventName, startBlock) => { 111 | return await getEvents(easyTrackContract, eventName, startBlock) 112 | } 113 | 114 | export const getDSMEvents = async (eventName, startBlock) => { 115 | return await getEvents(dsmContract, eventName, startBlock) 116 | } 117 | 118 | export const getRpcNetwork = async () => await provider.getNetwork() 119 | -------------------------------------------------------------------------------- /lido-subgraph/src/Voting.ts: -------------------------------------------------------------------------------- 1 | import { BigInt, Address } from '@graphprotocol/graph-ts' 2 | import { 3 | StartVote, 4 | CastVote, 5 | CastObjection, 6 | ExecuteVote, 7 | ChangeSupportRequired, 8 | ChangeMinQuorum, 9 | ChangeVoteTime, 10 | ChangeObjectionPhaseTime, 11 | } from '../generated/Voting/Voting' 12 | import { 13 | Voting, 14 | Vote, 15 | VotingObjection, 16 | ChangedSupportRequired, 17 | ChangedMinQuorum, 18 | ChangedVoteTime, 19 | ChangedObjectionPhaseTime, 20 | Shares, 21 | } from '../generated/schema' 22 | import { Totals } from '../generated/schema' 23 | 24 | export function handleStartVote(event: StartVote): void { 25 | let entity = new Voting(event.params.voteId.toString()) 26 | 27 | entity.index = event.params.voteId.toI32() 28 | entity.creator = event.params.creator 29 | entity.metadata = event.params.metadata 30 | entity.executed = false 31 | 32 | entity.save() 33 | } 34 | 35 | export function handleCastVote(event: CastVote): void { 36 | let entity = new Vote( 37 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 38 | ) 39 | 40 | entity.voting = event.params.voteId.toString() 41 | entity.voter = event.params.voter 42 | entity.supports = event.params.supports 43 | entity.stake = event.params.stake 44 | 45 | entity.save() 46 | } 47 | 48 | export function handleCastObjection(event: CastObjection): void { 49 | let entity = new VotingObjection( 50 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 51 | ) 52 | 53 | entity.voting = event.params.voteId.toString() 54 | entity.voter = event.params.voter 55 | entity.stake = event.params.stake 56 | 57 | entity.save() 58 | } 59 | 60 | export function handleExecuteVote(event: ExecuteVote): void { 61 | let entity = Voting.load(event.params.voteId.toString()) 62 | 63 | if (entity == null) { 64 | entity = new Voting(event.params.voteId.toString()) 65 | } 66 | 67 | entity.executed = true 68 | 69 | /** 70 | Accounting for calling burnShares() on Mainnet as we didn't yet have a proper event. 71 | This one-off operation allows us not to enable tracing. 72 | **/ 73 | if ( 74 | event.transaction.hash.toHexString() == 75 | '0x55eb29bda8d96a9a92295c358edbcef087d09f24bd684e6b4e88b166c99ea6a7' 76 | ) { 77 | let accToBurn = Address.fromString( 78 | '0x3e40d73eb977dc6a537af587d48316fee66e9c8c' 79 | ) 80 | let sharesToSubtract = BigInt.fromString('32145684728326685744') 81 | 82 | let shares = Shares.load(accToBurn)! 83 | shares.shares = shares.shares.minus(sharesToSubtract) 84 | shares.save() 85 | 86 | let totals = Totals.load('')! 87 | totals.totalShares = totals.totalShares.minus(sharesToSubtract) 88 | totals.save() 89 | } 90 | 91 | entity.save() 92 | } 93 | 94 | // Global settings 95 | 96 | export function handleChangeSupportRequired( 97 | event: ChangeSupportRequired 98 | ): void { 99 | let entity = new ChangedSupportRequired( 100 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 101 | ) 102 | 103 | entity.supportRequiredPct = event.params.supportRequiredPct 104 | 105 | entity.save() 106 | } 107 | 108 | export function handleChangeMinQuorum(event: ChangeMinQuorum): void { 109 | let entity = new ChangedMinQuorum( 110 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 111 | ) 112 | 113 | entity.minAcceptQuorumPct = event.params.minAcceptQuorumPct 114 | 115 | entity.save() 116 | } 117 | 118 | export function handleChangeVoteTime(event: ChangeVoteTime): void { 119 | let entity = new ChangedVoteTime( 120 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 121 | ) 122 | 123 | entity.voteTime = event.params.voteTime 124 | 125 | entity.save() 126 | } 127 | 128 | export function handleChangeObjectionPhaseTime( 129 | event: ChangeObjectionPhaseTime 130 | ): void { 131 | let entity = new ChangedObjectionPhaseTime( 132 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 133 | ) 134 | 135 | entity.objectionPhaseTime = event.params.objectionPhaseTime 136 | 137 | entity.save() 138 | } 139 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/big.js@6.1.3": 6 | version "6.1.3" 7 | resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-6.1.3.tgz#c008dec4dae24c7a338ebb4521c46e9609020807" 8 | integrity sha512-fHh2h1cFlvGP0kFCqoAsnuQoM0n3xHB6HxgZvELt7dji+BtK/j938MRL0nG5AA45EgibuFcPjgLlkqfUPCyoKw== 9 | 10 | asynckit@^0.4.0: 11 | version "0.4.0" 12 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 13 | integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= 14 | 15 | big.js@6.1.1: 16 | version "6.1.1" 17 | resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.1.1.tgz#63b35b19dc9775c94991ee5db7694880655d5537" 18 | integrity sha512-1vObw81a8ylZO5ePrtMay0n018TcftpTA5HFKDaSuiUDBo8biRBtjIobw60OpwuvrGk+FsxKamqN4cnmj/eXdg== 19 | 20 | combined-stream@^1.0.8: 21 | version "1.0.8" 22 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 23 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 24 | dependencies: 25 | delayed-stream "~1.0.0" 26 | 27 | cross-fetch@^3.0.6: 28 | version "3.1.4" 29 | resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" 30 | integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== 31 | dependencies: 32 | node-fetch "2.6.1" 33 | 34 | date-fns@2.28.0: 35 | version "2.28.0" 36 | resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" 37 | integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== 38 | 39 | delayed-stream@~1.0.0: 40 | version "1.0.0" 41 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 42 | integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= 43 | 44 | extract-files@^9.0.0: 45 | version "9.0.0" 46 | resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" 47 | integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== 48 | 49 | form-data@^3.0.0: 50 | version "3.0.1" 51 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" 52 | integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== 53 | dependencies: 54 | asynckit "^0.4.0" 55 | combined-stream "^1.0.8" 56 | mime-types "^2.1.12" 57 | 58 | graphql-request@4.0.0: 59 | version "4.0.0" 60 | resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-4.0.0.tgz#5e4361d33df1a95ccd7ad23a8ebb6bbca9d5622f" 61 | integrity sha512-cdqQLCXlBGkaLdkLYRl4LtkwaZU6TfpE7/tnUQFl3wXfUPWN74Ov+Q61VuIh+AltS789YfGB6whghmCmeXLvTw== 62 | dependencies: 63 | cross-fetch "^3.0.6" 64 | extract-files "^9.0.0" 65 | form-data "^3.0.0" 66 | 67 | graphql@16.3.0: 68 | version "16.3.0" 69 | resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05" 70 | integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A== 71 | 72 | mime-db@1.47.0: 73 | version "1.47.0" 74 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" 75 | integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== 76 | 77 | mime-types@^2.1.12: 78 | version "2.1.30" 79 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" 80 | integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== 81 | dependencies: 82 | mime-db "1.47.0" 83 | 84 | node-fetch@2.6.1: 85 | version "2.6.1" 86 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" 87 | integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== 88 | -------------------------------------------------------------------------------- /lido-subgraph/tests/utils/calculateAddressBalance.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'graphql-request' 2 | import { subgraphFetch, BigNumber } from './index.js' 3 | 4 | export const calculateAddressBalance = async (address) => { 5 | const submissionsQuery = gql` 6 | query ($first: Int, $skip: Int, $block: Block_height) { 7 | lidoSubmissions(first: $first, skip: $skip, block: $block, where: {sender: "${address}"}) { 8 | amount 9 | shares 10 | 11 | block 12 | transactionIndex 13 | } 14 | } 15 | ` 16 | 17 | const transfersInboundQuery = gql` 18 | query ($first: Int, $skip: Int, $block: Block_height) { 19 | lidoTransfers (first: $first, skip: $skip, block: $block, where: {to: "${address}"}) { 20 | value 21 | shares 22 | to 23 | 24 | mintWithoutSubmission 25 | 26 | block 27 | transactionIndex 28 | } 29 | } 30 | ` 31 | 32 | const transfersOutboundQuery = gql` 33 | query ($first: Int, $skip: Int, $block: Block_height) { 34 | lidoTransfers (first: $first, skip: $skip, block: $block, where: {from: "${address}"}) { 35 | value 36 | shares 37 | to 38 | 39 | block 40 | transactionIndex 41 | } 42 | } 43 | ` 44 | 45 | const ratioQuery = gql` 46 | query ($first: Int, $skip: Int, $block: Block_height) { 47 | totalRewards( 48 | first: $first 49 | skip: $skip 50 | block: $block 51 | orderBy: block 52 | orderDirection: asc 53 | ) { 54 | id 55 | 56 | totalRewards 57 | 58 | totalPooledEtherBefore 59 | totalPooledEtherAfter 60 | totalSharesBefore 61 | totalSharesAfter 62 | 63 | block 64 | transactionIndex 65 | } 66 | } 67 | ` 68 | 69 | const submissions = (await subgraphFetch(submissionsQuery)).lidoSubmissions 70 | const transfersInbound = (await subgraphFetch(transfersInboundQuery)) 71 | .lidoTransfers 72 | const transfersOutbound = (await subgraphFetch(transfersOutboundQuery)) 73 | .lidoTransfers 74 | 75 | const sortTxs = (a, b) => 76 | a.block - b.block || 77 | a.transactionIndex - b.transactionIndex || 78 | a.value - b.value 79 | 80 | const transactions = [ 81 | ...submissions.map((x) => ({ ...x, type: 'submission' })), 82 | ...transfersInbound.map((x) => ({ 83 | ...x, 84 | type: 'transfer', 85 | direction: 'inbound', 86 | })), 87 | ...transfersOutbound.map((x) => ({ 88 | ...x, 89 | type: 'transfer', 90 | direction: 'outbound', 91 | })), 92 | ].sort(sortTxs) 93 | 94 | const reports = (await subgraphFetch(ratioQuery)).totalRewards 95 | 96 | // Adding rewards to each day of oracle reports 97 | for (let report of reports) { 98 | // Find all transfers before this blocktime 99 | const usefulTransfers = transactions.filter((transfer) => 100 | transfer.block !== report.block 101 | ? parseInt(transfer.block) < parseInt(report.block) 102 | : parseInt(transfer.transactionIndex) < 103 | parseInt(report.transactionIndex) 104 | ) 105 | 106 | // Sum of all stakes before this moment 107 | const sumOfShares = usefulTransfers.reduce((aсс, item) => { 108 | // Can be null for transfers from 0x0 (minting) 109 | const shares = item.shares || 0 110 | 111 | return item.direction !== 'outbound' ? aсс.add(shares) : aсс.sub(shares) 112 | }, BigNumber.from(0)) 113 | 114 | const balanceBefore = sumOfShares 115 | .mul(report.totalPooledEtherBefore) 116 | .div(report.totalSharesBefore) 117 | 118 | const balanceAfter = sumOfShares 119 | .mul(report.totalPooledEtherAfter) 120 | .div(report.totalSharesAfter) 121 | 122 | const rewards = balanceAfter.sub(balanceBefore) 123 | 124 | report.balanceBefore = balanceBefore 125 | report.balanceAfter = balanceAfter 126 | report.rewards = rewards 127 | } 128 | 129 | // Calculating balances 130 | const together = [ 131 | ...transactions, 132 | ...reports.map((x) => ({ ...x, type: 'reward' })), 133 | ].sort(sortTxs) 134 | 135 | const balance = together.reduce((acc, item) => { 136 | const amount = item.value || item.rewards || 0 137 | 138 | return item.direction !== 'outbound' ? acc.add(amount) : acc.sub(amount) 139 | }, BigNumber.from(0)) 140 | 141 | return balance 142 | } 143 | -------------------------------------------------------------------------------- /lido-subgraph/snippets-nodejs/rewardHistory.js: -------------------------------------------------------------------------------- 1 | // Big number lib 2 | import { Big, BigDecimal } from './utils/index.js' 3 | // Date utilities 4 | import { fromUnixTime, format } from 'date-fns' 5 | // Simple GraphQL requester 6 | import { subgraphFetch } from './utils/index.js' 7 | 8 | // GraphQL Queries 9 | import { 10 | submissionsQuery, 11 | totalRewardQuery, 12 | transferInQuery, 13 | transferOutQuery, 14 | } from './queries/index.js' 15 | 16 | // Address to track 17 | const ADDRESS = '' 18 | 19 | // Helpers to display data in easily readable form 20 | const weiToHumanReadable = (wei) => BigDecimal(wei).div('1e18').toString() 21 | const dateToHumanReadable = (date) => format(fromUnixTime(date), 'dd.MM.yyyy') 22 | 23 | // Sort by block, logIndex and out first and then in 24 | const sortTxs = (a, b) => 25 | a.block - b.block || 26 | a.logIndex - b.logIndex || 27 | (a.direction === 'In' ? 1 : -1) 28 | 29 | const queryVars = { 30 | address: ADDRESS, 31 | } 32 | 33 | // Subgraph Requests: staking, oracle reports, transfers in and out 34 | const submissions = (await subgraphFetch(submissionsQuery, queryVars)) 35 | .lidoSubmissions 36 | const reports = (await subgraphFetch(totalRewardQuery)).totalRewards 37 | const transfersIn = (await subgraphFetch(transferInQuery, queryVars)) 38 | .lidoTransfers 39 | const transfersOut = (await subgraphFetch(transferOutQuery, queryVars)) 40 | .lidoTransfers 41 | 42 | // Joining transfers in and out 43 | const transfers = [ 44 | ...submissions.map((x) => ({ ...x, type: 'Staking' })), 45 | ...transfersIn.map((x) => ({ 46 | ...x, 47 | type: 'Transfer', 48 | direction: 'In', 49 | value: Big(x.value), 50 | })), 51 | ...transfersOut.map((x) => ({ 52 | ...x, 53 | type: 'Transfer', 54 | direction: 'Out', 55 | value: Big(x.value), 56 | })), 57 | ].sort(sortTxs) 58 | 59 | // Picking which balance direction we need 60 | for (let transfer of transfers) { 61 | transfer.balance = 62 | transfer.type === 'Staking' 63 | ? Big(transfer.balanceAfter) 64 | : transfer.direction === 'In' 65 | ? Big(transfer.balanceAfterIncrease || 0) 66 | : Big(transfer.balanceAfterDecrease || 0) 67 | } 68 | 69 | for (let report of reports) { 70 | report.type = 'Reward' 71 | 72 | // Find all transfers before rewards 73 | const usefulTransfers = transfers.filter((transfer) => 74 | transfer.block !== report.block 75 | ? parseInt(transfer.block) < parseInt(report.block) 76 | : parseInt(transfer.logIndex) < parseInt(report.logIndex) 77 | ) 78 | 79 | // Sum of all shares before this moment 80 | const shares = usefulTransfers.reduce((aсс, item) => { 81 | // Can be null for transfers from 0x0 (minting) 82 | const shares = Big(item.shares || 0) 83 | 84 | return item.direction !== 'Out' ? aсс.plus(shares) : aсс.minus(shares) 85 | }, Big(0)) 86 | 87 | report.shares = shares 88 | 89 | // Early exit if no shares 90 | if (shares.eq(0)) { 91 | report.balance = Big(0) 92 | report.rewards = Big(0) 93 | continue 94 | } 95 | 96 | const balanceBefore = shares 97 | .times(report.totalPooledEtherBefore) 98 | .div(report.totalSharesBefore) 99 | 100 | const balanceAfter = shares 101 | .times(report.totalPooledEtherAfter) 102 | .div(report.totalSharesAfter) 103 | 104 | const rewards = balanceAfter.sub(balanceBefore) 105 | 106 | report.balance = balanceAfter 107 | report.rewards = rewards 108 | } 109 | 110 | // Hiding unnecessary output 111 | const reportsWithShares = reports.filter((day) => day.shares.gt(0)) 112 | const hiddenTransfersOnSubmit = transfers.filter( 113 | (x, ix, array) => 114 | !( 115 | x.type === 'Transfer' && 116 | x.direction === 'In' && 117 | x.from === '0x0000000000000000000000000000000000000000' && 118 | array[ix - 1]?.type === 'Staking' 119 | ) 120 | ) 121 | 122 | // Joining and sorting 123 | const merged = [...hiddenTransfersOnSubmit, ...reportsWithShares].sort(sortTxs) 124 | 125 | const withChange = merged.map((x) => ({ 126 | ...x, 127 | change: x.amount || x.value || x.rewards, 128 | })) 129 | 130 | for (const x of withChange) { 131 | console.log( 132 | dateToHumanReadable(x.blockTime), 133 | x.block, 134 | `${x.type} ${x.type === 'Transfer' ? x.direction : ''}`, 135 | weiToHumanReadable(x.change), 136 | weiToHumanReadable(x.balance) 137 | ) 138 | } 139 | -------------------------------------------------------------------------------- /top_holders_updater_example/src/contract.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contract, Transfer as TransferEvent 3 | } from "../generated/Contract/Contract" 4 | import { 5 | Balance 6 | } from "../generated/schema" 7 | import { Address, BigInt, log, Bytes } from '@graphprotocol/graph-ts'; 8 | 9 | export function handleTransfer(event: TransferEvent): void { 10 | 11 | let entity = Balance.load(Bytes.fromHexString("0xF977814e90dA44bFA03b6295A0616a897441aceC")); 12 | if (entity == null) { 13 | createBalance("0xF977814e90dA44bFA03b6295A0616a897441aceC", event) 14 | createBalance("0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503", event) 15 | createBalance("0xA7A93fd0a276fc1C0197a5B5623eD117786eeD06", event) 16 | createBalance("0x5a52E96BAcdaBb82fd05763E25335261B270Efcb", event) 17 | createBalance("0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf", event) 18 | createBalance("0xD6216fC19DB775Df9774a6E33526131dA7D19a2c", event) 19 | createBalance("0xcEe284F754E854890e311e3280b767F80797180d", event) 20 | createBalance("0x5754284f345afc66a98fbB0a0Afe71e0F007B949", event) 21 | createBalance("0x28C6c06298d514Db089934071355E5743bf21d60", event) 22 | createBalance("0x3CC936b795A188F0e246cBB2D74C5Bd190aeCF18", event) 23 | createBalance("0xc5451b523d5FFfe1351337a221688a62806ad91a", event) 24 | createBalance("0x6Fb624B48d9299674022a23d92515e76Ba880113", event) 25 | createBalance("0x461249076B88189f8AC9418De28B365859E46BfD", event) 26 | createBalance("0xc708A1c712bA26DC618f972ad7A187F76C8596Fd", event) 27 | createBalance("0x69a722f0B5Da3aF02b4a205D6F0c285F4ed8F396", event) 28 | createBalance("0x42436286A9c8d63AAfC2eEbBCA193064d68068f2", event) 29 | createBalance("0xCbA38020cd7B6F51Df6AFaf507685aDd148F6ab6", event) 30 | createBalance("0x89e51fA8CA5D66cd220bAed62ED01e8951aa7c40", event) 31 | createBalance("0xB9711550ec6Dc977f26B73809A2D6791c0F0E9C8", event) 32 | createBalance("0x65A0947BA5175359Bb457D3b34491eDf4cBF7997", event) 33 | createBalance("0xe9172Daf64b05B26eb18f07aC8d6D723aCB48f99", event) 34 | createBalance("0xf59869753f41Db720127Ceb8DbB8afAF89030De4", event) 35 | createBalance("0x4D19C0a5357bC48be0017095d3C871D9aFC3F21d", event) 36 | createBalance("0x5C52cC7c96bDE8594e5B77D5b76d042CB5FaE5f2", event) 37 | createBalance("0x0D0707963952f2fBA59dD06f2b425ace40b492Fe", event) 38 | 39 | } 40 | 41 | updateBalanceFrom(event) 42 | updateBalanceTo(event) 43 | 44 | } 45 | 46 | export function updateBalanceFrom(event: TransferEvent): void { 47 | let entityId = event.params.from; 48 | let entity = Balance.load(entityId); 49 | if (entity == null) { 50 | entity = new Balance(entityId); 51 | } 52 | let contract = Contract.bind(event.address); 53 | 54 | // entity.value = contract.balanceOf(event.params.from); 55 | 56 | 57 | let balanceResult = contract.try_balanceOf(event.params.from); 58 | if (!balanceResult.reverted) { 59 | entity.value = balanceResult.value; 60 | entity.blockNumber = event.block.number; 61 | entity.blockTimestamp = event.block.timestamp; 62 | entity.transactionHash = event.transaction.hash; 63 | 64 | entity.save(); 65 | } 66 | 67 | } 68 | export function updateBalanceTo(event: TransferEvent): void { 69 | let entityId = event.params.to; 70 | let entity = Balance.load(entityId); 71 | if (entity == null) { 72 | entity = new Balance(entityId); 73 | } 74 | let contract = Contract.bind(event.address); 75 | 76 | // entity.value = contract.balanceOf(event.params.to); 77 | 78 | let balanceResult = contract.try_balanceOf(event.params.to); 79 | if (!balanceResult.reverted) { 80 | entity.value = balanceResult.value; 81 | entity.blockNumber = event.block.number; 82 | entity.blockTimestamp = event.block.timestamp; 83 | entity.transactionHash = event.transaction.hash; 84 | entity.save(); 85 | } 86 | 87 | } 88 | 89 | 90 | export function createBalance(address: string, event: TransferEvent): void { 91 | let entity = new Balance(Bytes.fromHexString(address)); 92 | let contract = Contract.bind(event.address); 93 | 94 | entity.value = contract.balanceOf(Address.fromString(address)) 95 | // entity.value = contract.balanceOf(Address.fromString("0x40ec5b33f54e0e8a33a975908c5ba1c14e5bbbdf")) 96 | entity.blockNumber = BigInt.fromI32(0); // Set the desired block number 97 | entity.blockTimestamp = BigInt.fromI32(0); // Set the desired block timestamp 98 | entity.transactionHash = Bytes.fromHexString("0x"); // Set the desired transaction hash 99 | 100 | entity.save(); 101 | } 102 | -------------------------------------------------------------------------------- /lido-subgraph/src/NodeOperatorsRegistry.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NodeOperatorAdded, 3 | NodeOperatorActiveSet, 4 | NodeOperatorNameSet, 5 | NodeOperatorRewardAddressSet, 6 | NodeOperatorStakingLimitSet, 7 | NodeOperatorTotalStoppedValidatorsReported, 8 | SigningKeyAdded, 9 | SigningKeyRemoved, 10 | NodeOperatorTotalKeysTrimmed, 11 | KeysOpIndexSet, 12 | } from '../generated/NodeOperatorsRegistry/NodeOperatorsRegistry' 13 | import { 14 | NodeOperatorSigningKey, 15 | NodeOperator, 16 | NodeOperatorTotalKeysTrim, 17 | handleKeysOpIndexChange, 18 | } from '../generated/schema' 19 | 20 | export function handleSigningKeyAdded(event: SigningKeyAdded): void { 21 | let entity = new NodeOperatorSigningKey(event.params.pubkey) 22 | 23 | entity.operatorId = event.params.operatorId 24 | entity.pubkey = event.params.pubkey 25 | entity.removed = false 26 | 27 | entity.operator = event.params.operatorId.toString() 28 | 29 | entity.save() 30 | } 31 | 32 | export function handleSigningKeyRemoved(event: SigningKeyRemoved): void { 33 | let entity = NodeOperatorSigningKey.load(event.params.pubkey) 34 | 35 | if (entity == null) { 36 | entity = new NodeOperatorSigningKey(event.params.pubkey) 37 | entity.pubkey = event.params.pubkey 38 | } 39 | 40 | entity.removed = true 41 | entity.save() 42 | } 43 | 44 | export function handleNodeOperatorAdded(event: NodeOperatorAdded): void { 45 | let entity = new NodeOperator(event.params.id.toString()) 46 | 47 | entity.name = event.params.name 48 | entity.rewardAddress = event.params.rewardAddress 49 | entity.stakingLimit = event.params.stakingLimit 50 | entity.active = true 51 | 52 | entity.save() 53 | } 54 | 55 | export function handleNodeOperatorActiveSet( 56 | event: NodeOperatorActiveSet 57 | ): void { 58 | let entity = NodeOperator.load(event.params.id.toString()) 59 | 60 | if (entity == null) { 61 | entity = new NodeOperator(event.params.id.toString()) 62 | } 63 | 64 | entity.active = event.params.active 65 | 66 | entity.save() 67 | } 68 | 69 | export function handleNodeOperatorNameSet(event: NodeOperatorNameSet): void { 70 | let entity = NodeOperator.load(event.params.id.toString()) 71 | 72 | if (entity == null) { 73 | entity = new NodeOperator(event.params.id.toString()) 74 | } 75 | 76 | entity.name = event.params.name 77 | 78 | entity.save() 79 | } 80 | 81 | export function handleNodeOperatorRewardAddressSet( 82 | event: NodeOperatorRewardAddressSet 83 | ): void { 84 | let entity = NodeOperator.load(event.params.id.toString()) 85 | 86 | if (entity == null) { 87 | entity = new NodeOperator(event.params.id.toString()) 88 | } 89 | 90 | entity.rewardAddress = event.params.rewardAddress 91 | 92 | entity.save() 93 | } 94 | 95 | export function handleNodeOperatorStakingLimitSet( 96 | event: NodeOperatorStakingLimitSet 97 | ): void { 98 | let entity = NodeOperator.load(event.params.id.toString()) 99 | 100 | if (entity == null) { 101 | entity = new NodeOperator(event.params.id.toString()) 102 | } 103 | 104 | entity.stakingLimit = event.params.stakingLimit 105 | 106 | entity.save() 107 | } 108 | 109 | export function handleNodeOperatorTotalStoppedValidatorsReported( 110 | event: NodeOperatorTotalStoppedValidatorsReported 111 | ): void { 112 | let entity = NodeOperator.load(event.params.id.toString()) 113 | 114 | if (entity == null) { 115 | entity = new NodeOperator(event.params.id.toString()) 116 | } 117 | 118 | entity.totalStoppedValidators = event.params.totalStopped 119 | 120 | entity.save() 121 | } 122 | 123 | export function handleNodeOperatorTotalKeysTrimmed( 124 | event: NodeOperatorTotalKeysTrimmed 125 | ): void { 126 | let entity = new NodeOperatorTotalKeysTrim( 127 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 128 | ) 129 | 130 | entity.operatorId = event.params.id 131 | entity.totalKeysTrimmed = event.params.totalKeysTrimmed 132 | 133 | entity.operator = event.params.id.toString() 134 | 135 | entity.block = event.block.number 136 | entity.blockTime = event.block.timestamp 137 | 138 | entity.save() 139 | } 140 | 141 | export function handleKeysOpIndexSet(event: KeysOpIndexSet): void { 142 | let entity = new handleKeysOpIndexChange( 143 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 144 | ) 145 | 146 | entity.index = event.params.keysOpIndex 147 | 148 | entity.block = event.block.number 149 | entity.blockTime = event.block.timestamp 150 | 151 | entity.save() 152 | } 153 | -------------------------------------------------------------------------------- /topholders-subgraph/tests/contract-utils.ts: -------------------------------------------------------------------------------- 1 | import { newMockEvent } from "matchstick-as" 2 | import { ethereum, BigInt, Address } from "@graphprotocol/graph-ts" 3 | import { 4 | Issue, 5 | Redeem, 6 | Deprecate, 7 | Params, 8 | DestroyedBlackFunds, 9 | AddedBlackList, 10 | RemovedBlackList, 11 | Approval, 12 | Transfer, 13 | Pause, 14 | Unpause 15 | } from "../generated/Contract/Contract" 16 | 17 | export function createIssueEvent(amount: BigInt): Issue { 18 | let issueEvent = changetype(newMockEvent()) 19 | 20 | issueEvent.parameters = new Array() 21 | 22 | issueEvent.parameters.push( 23 | new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)) 24 | ) 25 | 26 | return issueEvent 27 | } 28 | 29 | export function createRedeemEvent(amount: BigInt): Redeem { 30 | let redeemEvent = changetype(newMockEvent()) 31 | 32 | redeemEvent.parameters = new Array() 33 | 34 | redeemEvent.parameters.push( 35 | new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)) 36 | ) 37 | 38 | return redeemEvent 39 | } 40 | 41 | export function createDeprecateEvent(newAddress: Address): Deprecate { 42 | let deprecateEvent = changetype(newMockEvent()) 43 | 44 | deprecateEvent.parameters = new Array() 45 | 46 | deprecateEvent.parameters.push( 47 | new ethereum.EventParam( 48 | "newAddress", 49 | ethereum.Value.fromAddress(newAddress) 50 | ) 51 | ) 52 | 53 | return deprecateEvent 54 | } 55 | 56 | export function createParamsEvent( 57 | feeBasisPoints: BigInt, 58 | maxFee: BigInt 59 | ): Params { 60 | let paramsEvent = changetype(newMockEvent()) 61 | 62 | paramsEvent.parameters = new Array() 63 | 64 | paramsEvent.parameters.push( 65 | new ethereum.EventParam( 66 | "feeBasisPoints", 67 | ethereum.Value.fromUnsignedBigInt(feeBasisPoints) 68 | ) 69 | ) 70 | paramsEvent.parameters.push( 71 | new ethereum.EventParam("maxFee", ethereum.Value.fromUnsignedBigInt(maxFee)) 72 | ) 73 | 74 | return paramsEvent 75 | } 76 | 77 | export function createDestroyedBlackFundsEvent( 78 | _blackListedUser: Address, 79 | _balance: BigInt 80 | ): DestroyedBlackFunds { 81 | let destroyedBlackFundsEvent = changetype(newMockEvent()) 82 | 83 | destroyedBlackFundsEvent.parameters = new Array() 84 | 85 | destroyedBlackFundsEvent.parameters.push( 86 | new ethereum.EventParam( 87 | "_blackListedUser", 88 | ethereum.Value.fromAddress(_blackListedUser) 89 | ) 90 | ) 91 | destroyedBlackFundsEvent.parameters.push( 92 | new ethereum.EventParam( 93 | "_balance", 94 | ethereum.Value.fromUnsignedBigInt(_balance) 95 | ) 96 | ) 97 | 98 | return destroyedBlackFundsEvent 99 | } 100 | 101 | export function createAddedBlackListEvent(_user: Address): AddedBlackList { 102 | let addedBlackListEvent = changetype(newMockEvent()) 103 | 104 | addedBlackListEvent.parameters = new Array() 105 | 106 | addedBlackListEvent.parameters.push( 107 | new ethereum.EventParam("_user", ethereum.Value.fromAddress(_user)) 108 | ) 109 | 110 | return addedBlackListEvent 111 | } 112 | 113 | export function createRemovedBlackListEvent(_user: Address): RemovedBlackList { 114 | let removedBlackListEvent = changetype(newMockEvent()) 115 | 116 | removedBlackListEvent.parameters = new Array() 117 | 118 | removedBlackListEvent.parameters.push( 119 | new ethereum.EventParam("_user", ethereum.Value.fromAddress(_user)) 120 | ) 121 | 122 | return removedBlackListEvent 123 | } 124 | 125 | export function createApprovalEvent( 126 | owner: Address, 127 | spender: Address, 128 | value: BigInt 129 | ): Approval { 130 | let approvalEvent = changetype(newMockEvent()) 131 | 132 | approvalEvent.parameters = new Array() 133 | 134 | approvalEvent.parameters.push( 135 | new ethereum.EventParam("owner", ethereum.Value.fromAddress(owner)) 136 | ) 137 | approvalEvent.parameters.push( 138 | new ethereum.EventParam("spender", ethereum.Value.fromAddress(spender)) 139 | ) 140 | approvalEvent.parameters.push( 141 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 142 | ) 143 | 144 | return approvalEvent 145 | } 146 | 147 | export function createTransferEvent( 148 | from: Address, 149 | to: Address, 150 | value: BigInt 151 | ): Transfer { 152 | let transferEvent = changetype(newMockEvent()) 153 | 154 | transferEvent.parameters = new Array() 155 | 156 | transferEvent.parameters.push( 157 | new ethereum.EventParam("from", ethereum.Value.fromAddress(from)) 158 | ) 159 | transferEvent.parameters.push( 160 | new ethereum.EventParam("to", ethereum.Value.fromAddress(to)) 161 | ) 162 | transferEvent.parameters.push( 163 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 164 | ) 165 | 166 | return transferEvent 167 | } 168 | 169 | export function createPauseEvent(): Pause { 170 | let pauseEvent = changetype(newMockEvent()) 171 | 172 | pauseEvent.parameters = new Array() 173 | 174 | return pauseEvent 175 | } 176 | 177 | export function createUnpauseEvent(): Unpause { 178 | let unpauseEvent = changetype(newMockEvent()) 179 | 180 | unpauseEvent.parameters = new Array() 181 | 182 | return unpauseEvent 183 | } 184 | -------------------------------------------------------------------------------- /each_block_handler_example/tests/contract-utils.ts: -------------------------------------------------------------------------------- 1 | import { newMockEvent } from "matchstick-as" 2 | import { ethereum, BigInt, Address } from "@graphprotocol/graph-ts" 3 | import { 4 | Issue, 5 | Redeem, 6 | Deprecate, 7 | Params, 8 | DestroyedBlackFunds, 9 | AddedBlackList, 10 | RemovedBlackList, 11 | Approval, 12 | Transfer, 13 | Pause, 14 | Unpause 15 | } from "../generated/Contract/Contract" 16 | 17 | export function createIssueEvent(amount: BigInt): Issue { 18 | let issueEvent = changetype(newMockEvent()) 19 | 20 | issueEvent.parameters = new Array() 21 | 22 | issueEvent.parameters.push( 23 | new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)) 24 | ) 25 | 26 | return issueEvent 27 | } 28 | 29 | export function createRedeemEvent(amount: BigInt): Redeem { 30 | let redeemEvent = changetype(newMockEvent()) 31 | 32 | redeemEvent.parameters = new Array() 33 | 34 | redeemEvent.parameters.push( 35 | new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)) 36 | ) 37 | 38 | return redeemEvent 39 | } 40 | 41 | export function createDeprecateEvent(newAddress: Address): Deprecate { 42 | let deprecateEvent = changetype(newMockEvent()) 43 | 44 | deprecateEvent.parameters = new Array() 45 | 46 | deprecateEvent.parameters.push( 47 | new ethereum.EventParam( 48 | "newAddress", 49 | ethereum.Value.fromAddress(newAddress) 50 | ) 51 | ) 52 | 53 | return deprecateEvent 54 | } 55 | 56 | export function createParamsEvent( 57 | feeBasisPoints: BigInt, 58 | maxFee: BigInt 59 | ): Params { 60 | let paramsEvent = changetype(newMockEvent()) 61 | 62 | paramsEvent.parameters = new Array() 63 | 64 | paramsEvent.parameters.push( 65 | new ethereum.EventParam( 66 | "feeBasisPoints", 67 | ethereum.Value.fromUnsignedBigInt(feeBasisPoints) 68 | ) 69 | ) 70 | paramsEvent.parameters.push( 71 | new ethereum.EventParam("maxFee", ethereum.Value.fromUnsignedBigInt(maxFee)) 72 | ) 73 | 74 | return paramsEvent 75 | } 76 | 77 | export function createDestroyedBlackFundsEvent( 78 | _blackListedUser: Address, 79 | _balance: BigInt 80 | ): DestroyedBlackFunds { 81 | let destroyedBlackFundsEvent = changetype(newMockEvent()) 82 | 83 | destroyedBlackFundsEvent.parameters = new Array() 84 | 85 | destroyedBlackFundsEvent.parameters.push( 86 | new ethereum.EventParam( 87 | "_blackListedUser", 88 | ethereum.Value.fromAddress(_blackListedUser) 89 | ) 90 | ) 91 | destroyedBlackFundsEvent.parameters.push( 92 | new ethereum.EventParam( 93 | "_balance", 94 | ethereum.Value.fromUnsignedBigInt(_balance) 95 | ) 96 | ) 97 | 98 | return destroyedBlackFundsEvent 99 | } 100 | 101 | export function createAddedBlackListEvent(_user: Address): AddedBlackList { 102 | let addedBlackListEvent = changetype(newMockEvent()) 103 | 104 | addedBlackListEvent.parameters = new Array() 105 | 106 | addedBlackListEvent.parameters.push( 107 | new ethereum.EventParam("_user", ethereum.Value.fromAddress(_user)) 108 | ) 109 | 110 | return addedBlackListEvent 111 | } 112 | 113 | export function createRemovedBlackListEvent(_user: Address): RemovedBlackList { 114 | let removedBlackListEvent = changetype(newMockEvent()) 115 | 116 | removedBlackListEvent.parameters = new Array() 117 | 118 | removedBlackListEvent.parameters.push( 119 | new ethereum.EventParam("_user", ethereum.Value.fromAddress(_user)) 120 | ) 121 | 122 | return removedBlackListEvent 123 | } 124 | 125 | export function createApprovalEvent( 126 | owner: Address, 127 | spender: Address, 128 | value: BigInt 129 | ): Approval { 130 | let approvalEvent = changetype(newMockEvent()) 131 | 132 | approvalEvent.parameters = new Array() 133 | 134 | approvalEvent.parameters.push( 135 | new ethereum.EventParam("owner", ethereum.Value.fromAddress(owner)) 136 | ) 137 | approvalEvent.parameters.push( 138 | new ethereum.EventParam("spender", ethereum.Value.fromAddress(spender)) 139 | ) 140 | approvalEvent.parameters.push( 141 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 142 | ) 143 | 144 | return approvalEvent 145 | } 146 | 147 | export function createTransferEvent( 148 | from: Address, 149 | to: Address, 150 | value: BigInt 151 | ): Transfer { 152 | let transferEvent = changetype(newMockEvent()) 153 | 154 | transferEvent.parameters = new Array() 155 | 156 | transferEvent.parameters.push( 157 | new ethereum.EventParam("from", ethereum.Value.fromAddress(from)) 158 | ) 159 | transferEvent.parameters.push( 160 | new ethereum.EventParam("to", ethereum.Value.fromAddress(to)) 161 | ) 162 | transferEvent.parameters.push( 163 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 164 | ) 165 | 166 | return transferEvent 167 | } 168 | 169 | export function createPauseEvent(): Pause { 170 | let pauseEvent = changetype(newMockEvent()) 171 | 172 | pauseEvent.parameters = new Array() 173 | 174 | return pauseEvent 175 | } 176 | 177 | export function createUnpauseEvent(): Unpause { 178 | let unpauseEvent = changetype(newMockEvent()) 179 | 180 | unpauseEvent.parameters = new Array() 181 | 182 | return unpauseEvent 183 | } 184 | -------------------------------------------------------------------------------- /erc20_historical_total_supply/tests/contract-utils.ts: -------------------------------------------------------------------------------- 1 | import { newMockEvent } from "matchstick-as" 2 | import { ethereum, BigInt, Address } from "@graphprotocol/graph-ts" 3 | import { 4 | Issue, 5 | Redeem, 6 | Deprecate, 7 | Params, 8 | DestroyedBlackFunds, 9 | AddedBlackList, 10 | RemovedBlackList, 11 | Approval, 12 | Transfer, 13 | Pause, 14 | Unpause 15 | } from "../generated/Contract/Contract" 16 | 17 | export function createIssueEvent(amount: BigInt): Issue { 18 | let issueEvent = changetype(newMockEvent()) 19 | 20 | issueEvent.parameters = new Array() 21 | 22 | issueEvent.parameters.push( 23 | new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)) 24 | ) 25 | 26 | return issueEvent 27 | } 28 | 29 | export function createRedeemEvent(amount: BigInt): Redeem { 30 | let redeemEvent = changetype(newMockEvent()) 31 | 32 | redeemEvent.parameters = new Array() 33 | 34 | redeemEvent.parameters.push( 35 | new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)) 36 | ) 37 | 38 | return redeemEvent 39 | } 40 | 41 | export function createDeprecateEvent(newAddress: Address): Deprecate { 42 | let deprecateEvent = changetype(newMockEvent()) 43 | 44 | deprecateEvent.parameters = new Array() 45 | 46 | deprecateEvent.parameters.push( 47 | new ethereum.EventParam( 48 | "newAddress", 49 | ethereum.Value.fromAddress(newAddress) 50 | ) 51 | ) 52 | 53 | return deprecateEvent 54 | } 55 | 56 | export function createParamsEvent( 57 | feeBasisPoints: BigInt, 58 | maxFee: BigInt 59 | ): Params { 60 | let paramsEvent = changetype(newMockEvent()) 61 | 62 | paramsEvent.parameters = new Array() 63 | 64 | paramsEvent.parameters.push( 65 | new ethereum.EventParam( 66 | "feeBasisPoints", 67 | ethereum.Value.fromUnsignedBigInt(feeBasisPoints) 68 | ) 69 | ) 70 | paramsEvent.parameters.push( 71 | new ethereum.EventParam("maxFee", ethereum.Value.fromUnsignedBigInt(maxFee)) 72 | ) 73 | 74 | return paramsEvent 75 | } 76 | 77 | export function createDestroyedBlackFundsEvent( 78 | _blackListedUser: Address, 79 | _balance: BigInt 80 | ): DestroyedBlackFunds { 81 | let destroyedBlackFundsEvent = changetype(newMockEvent()) 82 | 83 | destroyedBlackFundsEvent.parameters = new Array() 84 | 85 | destroyedBlackFundsEvent.parameters.push( 86 | new ethereum.EventParam( 87 | "_blackListedUser", 88 | ethereum.Value.fromAddress(_blackListedUser) 89 | ) 90 | ) 91 | destroyedBlackFundsEvent.parameters.push( 92 | new ethereum.EventParam( 93 | "_balance", 94 | ethereum.Value.fromUnsignedBigInt(_balance) 95 | ) 96 | ) 97 | 98 | return destroyedBlackFundsEvent 99 | } 100 | 101 | export function createAddedBlackListEvent(_user: Address): AddedBlackList { 102 | let addedBlackListEvent = changetype(newMockEvent()) 103 | 104 | addedBlackListEvent.parameters = new Array() 105 | 106 | addedBlackListEvent.parameters.push( 107 | new ethereum.EventParam("_user", ethereum.Value.fromAddress(_user)) 108 | ) 109 | 110 | return addedBlackListEvent 111 | } 112 | 113 | export function createRemovedBlackListEvent(_user: Address): RemovedBlackList { 114 | let removedBlackListEvent = changetype(newMockEvent()) 115 | 116 | removedBlackListEvent.parameters = new Array() 117 | 118 | removedBlackListEvent.parameters.push( 119 | new ethereum.EventParam("_user", ethereum.Value.fromAddress(_user)) 120 | ) 121 | 122 | return removedBlackListEvent 123 | } 124 | 125 | export function createApprovalEvent( 126 | owner: Address, 127 | spender: Address, 128 | value: BigInt 129 | ): Approval { 130 | let approvalEvent = changetype(newMockEvent()) 131 | 132 | approvalEvent.parameters = new Array() 133 | 134 | approvalEvent.parameters.push( 135 | new ethereum.EventParam("owner", ethereum.Value.fromAddress(owner)) 136 | ) 137 | approvalEvent.parameters.push( 138 | new ethereum.EventParam("spender", ethereum.Value.fromAddress(spender)) 139 | ) 140 | approvalEvent.parameters.push( 141 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 142 | ) 143 | 144 | return approvalEvent 145 | } 146 | 147 | export function createTransferEvent( 148 | from: Address, 149 | to: Address, 150 | value: BigInt 151 | ): Transfer { 152 | let transferEvent = changetype(newMockEvent()) 153 | 154 | transferEvent.parameters = new Array() 155 | 156 | transferEvent.parameters.push( 157 | new ethereum.EventParam("from", ethereum.Value.fromAddress(from)) 158 | ) 159 | transferEvent.parameters.push( 160 | new ethereum.EventParam("to", ethereum.Value.fromAddress(to)) 161 | ) 162 | transferEvent.parameters.push( 163 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 164 | ) 165 | 166 | return transferEvent 167 | } 168 | 169 | export function createPauseEvent(): Pause { 170 | let pauseEvent = changetype(newMockEvent()) 171 | 172 | pauseEvent.parameters = new Array() 173 | 174 | return pauseEvent 175 | } 176 | 177 | export function createUnpauseEvent(): Unpause { 178 | let unpauseEvent = changetype(newMockEvent()) 179 | 180 | unpauseEvent.parameters = new Array() 181 | 182 | return unpauseEvent 183 | } 184 | -------------------------------------------------------------------------------- /top_holders_updater_example/tests/contract-utils.ts: -------------------------------------------------------------------------------- 1 | import { newMockEvent } from "matchstick-as" 2 | import { ethereum, BigInt, Address } from "@graphprotocol/graph-ts" 3 | import { 4 | Issue, 5 | Redeem, 6 | Deprecate, 7 | Params, 8 | DestroyedBlackFunds, 9 | AddedBlackList, 10 | RemovedBlackList, 11 | Approval, 12 | Transfer, 13 | Pause, 14 | Unpause 15 | } from "../generated/Contract/Contract" 16 | 17 | export function createIssueEvent(amount: BigInt): Issue { 18 | let issueEvent = changetype(newMockEvent()) 19 | 20 | issueEvent.parameters = new Array() 21 | 22 | issueEvent.parameters.push( 23 | new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)) 24 | ) 25 | 26 | return issueEvent 27 | } 28 | 29 | export function createRedeemEvent(amount: BigInt): Redeem { 30 | let redeemEvent = changetype(newMockEvent()) 31 | 32 | redeemEvent.parameters = new Array() 33 | 34 | redeemEvent.parameters.push( 35 | new ethereum.EventParam("amount", ethereum.Value.fromUnsignedBigInt(amount)) 36 | ) 37 | 38 | return redeemEvent 39 | } 40 | 41 | export function createDeprecateEvent(newAddress: Address): Deprecate { 42 | let deprecateEvent = changetype(newMockEvent()) 43 | 44 | deprecateEvent.parameters = new Array() 45 | 46 | deprecateEvent.parameters.push( 47 | new ethereum.EventParam( 48 | "newAddress", 49 | ethereum.Value.fromAddress(newAddress) 50 | ) 51 | ) 52 | 53 | return deprecateEvent 54 | } 55 | 56 | export function createParamsEvent( 57 | feeBasisPoints: BigInt, 58 | maxFee: BigInt 59 | ): Params { 60 | let paramsEvent = changetype(newMockEvent()) 61 | 62 | paramsEvent.parameters = new Array() 63 | 64 | paramsEvent.parameters.push( 65 | new ethereum.EventParam( 66 | "feeBasisPoints", 67 | ethereum.Value.fromUnsignedBigInt(feeBasisPoints) 68 | ) 69 | ) 70 | paramsEvent.parameters.push( 71 | new ethereum.EventParam("maxFee", ethereum.Value.fromUnsignedBigInt(maxFee)) 72 | ) 73 | 74 | return paramsEvent 75 | } 76 | 77 | export function createDestroyedBlackFundsEvent( 78 | _blackListedUser: Address, 79 | _balance: BigInt 80 | ): DestroyedBlackFunds { 81 | let destroyedBlackFundsEvent = changetype(newMockEvent()) 82 | 83 | destroyedBlackFundsEvent.parameters = new Array() 84 | 85 | destroyedBlackFundsEvent.parameters.push( 86 | new ethereum.EventParam( 87 | "_blackListedUser", 88 | ethereum.Value.fromAddress(_blackListedUser) 89 | ) 90 | ) 91 | destroyedBlackFundsEvent.parameters.push( 92 | new ethereum.EventParam( 93 | "_balance", 94 | ethereum.Value.fromUnsignedBigInt(_balance) 95 | ) 96 | ) 97 | 98 | return destroyedBlackFundsEvent 99 | } 100 | 101 | export function createAddedBlackListEvent(_user: Address): AddedBlackList { 102 | let addedBlackListEvent = changetype(newMockEvent()) 103 | 104 | addedBlackListEvent.parameters = new Array() 105 | 106 | addedBlackListEvent.parameters.push( 107 | new ethereum.EventParam("_user", ethereum.Value.fromAddress(_user)) 108 | ) 109 | 110 | return addedBlackListEvent 111 | } 112 | 113 | export function createRemovedBlackListEvent(_user: Address): RemovedBlackList { 114 | let removedBlackListEvent = changetype(newMockEvent()) 115 | 116 | removedBlackListEvent.parameters = new Array() 117 | 118 | removedBlackListEvent.parameters.push( 119 | new ethereum.EventParam("_user", ethereum.Value.fromAddress(_user)) 120 | ) 121 | 122 | return removedBlackListEvent 123 | } 124 | 125 | export function createApprovalEvent( 126 | owner: Address, 127 | spender: Address, 128 | value: BigInt 129 | ): Approval { 130 | let approvalEvent = changetype(newMockEvent()) 131 | 132 | approvalEvent.parameters = new Array() 133 | 134 | approvalEvent.parameters.push( 135 | new ethereum.EventParam("owner", ethereum.Value.fromAddress(owner)) 136 | ) 137 | approvalEvent.parameters.push( 138 | new ethereum.EventParam("spender", ethereum.Value.fromAddress(spender)) 139 | ) 140 | approvalEvent.parameters.push( 141 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 142 | ) 143 | 144 | return approvalEvent 145 | } 146 | 147 | export function createTransferEvent( 148 | from: Address, 149 | to: Address, 150 | value: BigInt 151 | ): Transfer { 152 | let transferEvent = changetype(newMockEvent()) 153 | 154 | transferEvent.parameters = new Array() 155 | 156 | transferEvent.parameters.push( 157 | new ethereum.EventParam("from", ethereum.Value.fromAddress(from)) 158 | ) 159 | transferEvent.parameters.push( 160 | new ethereum.EventParam("to", ethereum.Value.fromAddress(to)) 161 | ) 162 | transferEvent.parameters.push( 163 | new ethereum.EventParam("value", ethereum.Value.fromUnsignedBigInt(value)) 164 | ) 165 | 166 | return transferEvent 167 | } 168 | 169 | export function createPauseEvent(): Pause { 170 | let pauseEvent = changetype(newMockEvent()) 171 | 172 | pauseEvent.parameters = new Array() 173 | 174 | return pauseEvent 175 | } 176 | 177 | export function createUnpauseEvent(): Unpause { 178 | let unpauseEvent = changetype(newMockEvent()) 179 | 180 | unpauseEvent.parameters = new Array() 181 | 182 | return unpauseEvent 183 | } 184 | -------------------------------------------------------------------------------- /lido-subgraph/src/Easytrack.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from '@graphprotocol/graph-ts' 2 | import { 3 | EVMScriptExecutorChanged, 4 | EVMScriptFactoryAdded, 5 | EVMScriptFactoryRemoved, 6 | MotionCanceled, 7 | MotionCreated, 8 | MotionDurationChanged, 9 | MotionEnacted, 10 | MotionObjected, 11 | MotionRejected, 12 | MotionsCountLimitChanged, 13 | ObjectionsThresholdChanged, 14 | Paused, 15 | Unpaused, 16 | RoleAdminChanged, 17 | RoleGranted, 18 | RoleRevoked, 19 | } from '../generated/Easytrack/Easytrack' 20 | 21 | import { 22 | Motion, 23 | Role, 24 | EVMScriptFactory, 25 | Objection, 26 | EasyTrackConfig, 27 | } from '../generated/schema' 28 | 29 | function loadConfig(): EasyTrackConfig { 30 | let entity = EasyTrackConfig.load('0') 31 | if (!entity) entity = new EasyTrackConfig('0') 32 | return entity 33 | } 34 | 35 | export function handleEVMScriptExecutorChanged( 36 | event: EVMScriptExecutorChanged 37 | ): void { 38 | let entity = loadConfig() 39 | 40 | entity.evmScriptExecutor = event.params._evmScriptExecutor 41 | 42 | entity.save() 43 | } 44 | 45 | export function handleMotionDurationChanged( 46 | event: MotionDurationChanged 47 | ): void { 48 | let entity = loadConfig() 49 | 50 | entity.motionDuration = event.params._motionDuration 51 | 52 | entity.save() 53 | } 54 | 55 | export function handleMotionsCountLimitChanged( 56 | event: MotionsCountLimitChanged 57 | ): void { 58 | let entity = loadConfig() 59 | 60 | entity.motionsCountLimit = event.params._newMotionsCountLimit 61 | 62 | entity.save() 63 | } 64 | 65 | export function handleObjectionsThresholdChanged( 66 | event: ObjectionsThresholdChanged 67 | ): void { 68 | let entity = loadConfig() 69 | 70 | entity.objectionsThreshold = event.params._newThreshold 71 | 72 | entity.save() 73 | } 74 | 75 | export function handlePaused(_event: Paused): void { 76 | let entity = loadConfig() 77 | 78 | entity.isPaused = true 79 | 80 | entity.save() 81 | } 82 | 83 | export function handleUnpaused(_event: Unpaused): void { 84 | let entity = loadConfig() 85 | 86 | entity.isPaused = false 87 | 88 | entity.save() 89 | } 90 | 91 | export function handleRoleAdminChanged(_event: RoleAdminChanged): void {} 92 | 93 | export function handleEVMScriptFactoryAdded( 94 | event: EVMScriptFactoryAdded 95 | ): void { 96 | let entity = new EVMScriptFactory(event.params._evmScriptFactory.toHex()) 97 | 98 | entity.address = event.params._evmScriptFactory 99 | entity.permissions = event.params._permissions 100 | entity.isActive = true 101 | 102 | entity.save() 103 | } 104 | 105 | export function handleEVMScriptFactoryRemoved( 106 | event: EVMScriptFactoryRemoved 107 | ): void { 108 | let entity = EVMScriptFactory.load(event.params._evmScriptFactory.toHex())! 109 | 110 | entity.isActive = false 111 | 112 | entity.save() 113 | } 114 | 115 | export function handleMotionCreated(event: MotionCreated): void { 116 | let entity = new Motion(event.params._motionId.toString()) 117 | 118 | let config = loadConfig() 119 | 120 | entity.snapshotBlock = event.block.number 121 | entity.startDate = event.block.timestamp 122 | 123 | entity.creator = event.params._creator 124 | entity.duration = config.motionDuration 125 | entity.evmScriptHash = event.params._evmScript 126 | entity.evmScriptFactory = event.params._evmScriptFactory 127 | entity.objectionsAmountPct = new BigInt(0) 128 | entity.objectionsThreshold = config.objectionsThreshold 129 | entity.objectionsAmount = new BigInt(0) 130 | entity.evmScriptCalldata = event.params._evmScriptCallData 131 | entity.status = 'ACTIVE' 132 | 133 | entity.save() 134 | } 135 | 136 | export function handleMotionObjected(event: MotionObjected): void { 137 | let entity = Motion.load(event.params._motionId.toString())! 138 | 139 | entity.objectionsAmount = event.params._newObjectionsAmount 140 | entity.objectionsAmountPct = event.params._newObjectionsAmountPct 141 | 142 | entity.save() 143 | 144 | let objectionEntity = new Objection( 145 | event.params._motionId.toHex() + '-' + event.params._objector.toHex() 146 | ) 147 | 148 | objectionEntity.objector = event.params._objector 149 | objectionEntity.motionId = event.params._motionId 150 | objectionEntity.weight = event.params._weight 151 | objectionEntity.block = event.block.number 152 | objectionEntity.blockTime = event.block.timestamp 153 | 154 | objectionEntity.save() 155 | } 156 | 157 | export function handleMotionCanceled(event: MotionCanceled): void { 158 | let entity = Motion.load(event.params._motionId.toString())! 159 | 160 | entity.status = 'CANCELED' 161 | entity.canceled_at = event.block.timestamp 162 | 163 | entity.save() 164 | } 165 | 166 | export function handleMotionEnacted(event: MotionEnacted): void { 167 | let entity = Motion.load(event.params._motionId.toString())! 168 | 169 | entity.status = 'ENACTED' 170 | entity.enacted_at = event.block.timestamp 171 | 172 | entity.save() 173 | } 174 | 175 | export function handleMotionRejected(event: MotionRejected): void { 176 | let entity = Motion.load(event.params._motionId.toString())! 177 | 178 | entity.status = 'REJECTED' 179 | entity.rejected_at = event.block.timestamp 180 | 181 | entity.save() 182 | } 183 | 184 | export function handleRoleGranted(event: RoleGranted): void { 185 | let entity = new Role( 186 | event.params.account.toHex() + '-' + event.params.role.toHex() 187 | ) 188 | 189 | entity.role = event.params.role 190 | entity.address = event.params.account 191 | entity.creator = event.params.sender 192 | entity.isActive = true 193 | 194 | entity.save() 195 | } 196 | 197 | export function handleRoleRevoked(event: RoleRevoked): void { 198 | let entity = Role.load( 199 | event.params.account.toHex() + '-' + event.params.role.toHex() 200 | )! 201 | 202 | entity.isActive = false 203 | 204 | entity.save() 205 | } 206 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Awesome Subgraphs by [Chainstack](https://chainstack.com/subgraphs/) Subgraphs hosting 2 | A curated list of awesome resources related to [The Graph](https://thegraph.com/) powered subgraph development. 3 | 4 | Feel free to send me any related links in [Twitter](https://twitter.com/balakhonoff) or [Telegram](https://t.me/kirill_balakhonov) to add them here. 5 | 6 | # Tutorials 7 | - [A beginner’s guide to getting started with The Graph](https://docs.chainstack.com/docs/subgraphs-tutorial-a-beginners-guide-to-getting-started-with-the-graph) 8 | - [How to access real-time smart contract data from Python code (using Lido contract as an example)](https://medium.com/@balakhonoff_47314/how-to-access-real-time-smart-contract-data-from-python-code-using-lido-as-an-example-38738ff077c5) 9 | - [Web3 Indexing: The Ultimate Guide (No Prior Knowledge Required)](https://hackernoon.com/web3-indexing-the-ultimate-guide-no-prior-knowledge-required) 10 | - [Explaining Subgraph schemas](https://docs.chainstack.com/docs/subgraphs-tutorial-working-with-schemas) 11 | - [Debugging subgraphs with a local Graph Node](https://docs.chainstack.com/docs/subgraphs-tutorial-debug-subgraphs-with-a-local-graph-node) 12 | - [Indexing ERC-20 token balance using Subgraphs](https://docs.chainstack.com/docs/subgraphs-tutorial-indexing-erc-20-token-balance) 13 | - [Indexing Uniswap data with Subgraphs](https://docs.chainstack.com/docs/subgraphs-tutorial-indexing-uniswap-data) 14 | - [Fetching subgraph data using JS](https://docs.chainstack.com/docs/subgraphs-tutorial-indexing-uniswap-data) 15 | - [How to access the Tornado Cash data easily using The Graph’s subgraphs](https://medium.com/@balakhonoff_47314/how-to-access-the-tornado-cash-data-easily-using-the-graphs-subgraphs-a70a7e21449d) 16 | - [How to access transactions of PEPE coin using The Graph subgraphs](https://medium.com/@balakhonoff_47314/tutorial-how-to-access-transactions-of-pepe-pepe-coin-using-the-graph-subgraphs-and-chatgpt-5cb4349fbf9e) 17 | - [The Graph Tutorial: Creating a Subgraph](https://mirror.xyz/0xB38709B8198d147cc9Ff9C133838a044d78B064B/DdiikBvOLngfOotpqNEoi7gIy9RDlEr0Ztv4yWlYyzc) 18 | - [Notifications from a Subgraph using Push](https://docs.push.org/developers/developer-guides/sending-notifications/using-subgraph-gasless) 19 | - [How to properly request JSON metadata stored in IPFS for your "The Graph" Subgraph](https://blog.developerdao.com/how-to-properly-request-json-metadata-stored-in-ipfs-for-your-the-graph-subgraph) 20 | - [Building a Full Stack Web3 YouTube Clone with Next, IPFS, The Graph, Solidity, and Livepeer](https://blog.suhailkakar.com/building-a-full-stack-web3-youtube-clone-with-next-ipfs-the-graph-solidity-and-livepeer) 21 | - [Subgraph Development](https://docs.blastapi.io/indexing/subgraph-development) 22 | - [How to Integrate The Graph and Create and Deploy a Subgraph](https://nodereal.io/tutorials/how-to-integrate-with-thegraph-using-meganode-archive-node/) 23 | - [Web3 data querying with The Graph and subgraphs](https://blog.logrocket.com/web3-data-querying-the-graph-subgraphs/) 24 | - [Create Lens Subgraph on The Graph Protocol](https://blog.devgenius.io/create-lens-subgraph-on-the-graph-protocol-8acfbac94ea8) 25 | - [Indexing data using The Graph's Indexer by LearnWeb3](https://learnweb3.io/lessons/indexing-data-using-the-graphs-indexer/) 26 | - [Writing a subgraph to get the friend.tech real-time trading data](https://docs.chainstack.com/docs/writing-a-subgraph-to-get-the-friendtech-real-time-trading-data) 27 | - [Tracking token total supply over millions of blocks: A guide to creating a subgraph and deploying to Chainstack](docs.chainstack.com/docs/tracking-token-total-supply-over-millions-of-blocks-a-guide-to-creating-a-subgraph-and-deploying-to-chainstack) 28 | 29 | # Videos 30 | - [Build a Subgraph in 5 Minutes: Supercharging Your DApp](https://www.youtube.com/watch?v=L8jYtr4omKM) 31 | - [How to Deploy a Subgraph for Indexing Solidity Smart Contracts 2022](https://www.youtube.com/watch?v=YvKIkJTDD9E) 32 | - [Query Ethereum with GraphQL with The Graph](https://www.youtube.com/watch?v=l2rzT_Dp4T0&pp=ygUSc3ViZ3JhcGggdGhlIGdyYXBo) 33 | - [Building a Subgraph with Subgraph Studio](https://www.youtube.com/watch?v=HfDgC2oNnwo&t=5s) 34 | - [Building Subgraphs on The Graph](https://www.youtube.com/watch?v=coa0Vw47qNc&ab_channel=ETHGlobal) 35 | - [Building Rich APIs on top of Ethereum with The Graph](https://www.youtube.com/watch?v=wrV7cMebwyE) 36 | - [Building Subgraphs with The Graph](https://www.youtube.com/watch?v=ct1UMSpZLgk&t=9s) 37 | 38 | # Useful links from the official documentation 39 | - [Creating a subgraph](https://thegraph.com/docs/en/developing/creating-a-subgraph/) 40 | - [Supported networks](https://thegraph.com/docs/en/developing/supported-networks/) 41 | - [AssemblyScript API](https://thegraph.com/docs/en/developing/assemblyscript-api/) 42 | - [Developer FAQs](https://thegraph.com/docs/en/developing/developer-faqs/) 43 | - [Query The Graph](https://thegraph.com/docs/en/querying/querying-the-graph/) 44 | - [Querying Best Practices](https://thegraph.com/docs/en/querying/querying-best-practices/) 45 | - [Querying from an Application](https://thegraph.com/docs/en/querying/querying-from-an-application/) 46 | - [GraphQL API](https://thegraph.com/docs/en/querying/graphql-api/) 47 | - [Subgraphs on NEAR](https://thegraph.com/docs/en/cookbook/near/) 48 | - [Subgraphs on Cosmos](https://thegraph.com/docs/en/cookbook/cosmos/) 49 | - [Subgraphs on Arweave](https://thegraph.com/docs/en/cookbook/arweave/) 50 | - [Substreams-powered subgraphs](https://thegraph.com/docs/en/cookbook/substreams-powered-subgraphs/) 51 | 52 | # Tools 53 | - [The Graph Hosted service](https://thegraph.com/hosted-service) 54 | - [SubgraphGPT](https://t.me/SubgraphGPT_bot) 55 | 56 | # GitHub repositories 57 | - [Messari Standard Subgraphs](https://github.com/messari/subgraphs). Standardized subgraphs for blockchain data 58 | - [Subgraph Toolkit](https://github.com/protofire/subgraph-toolkit). A collection of utilities and helpers to support the development of subgraphs 59 | - [Subgraph Query Portal](https://github.com/Evan-Kim2028/subgraph-query-portal). A collection of reusable public goods subgraph queries. 60 | - [Subgrounds](https://github.com/0xPlaygrounds/subgrounds). An intuitive python library for interfacing with Subgraphs. 61 | - [Example subgraph by The Graph](https://github.com/graphprotocol/example-subgraph). An example to help you get started with The Graph 62 | 63 | -------------------------------------------------------------------------------- /lido-subgraph/src/DepositSecurityModule.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DepositsPaused, 3 | DepositsUnpaused, 4 | GuardianAdded, 5 | GuardianQuorumChanged, 6 | GuardianRemoved, 7 | MaxDepositsChanged, 8 | MinDepositBlockDistanceChanged, 9 | NodeOperatorsRegistryChanged, 10 | OwnerChanged, 11 | PauseIntentValidityPeriodBlocksChanged, 12 | } from '../generated/DepositSecurityModule/DepositSecurityModule' 13 | 14 | import { 15 | DepositSecurityModuleSettings, 16 | DepositsPause, 17 | DepositsUnpause, 18 | Guardian, 19 | GuardianQuorumChange, 20 | MaxDepositsChange, 21 | MinDepositBlockDistanceChange, 22 | NodeOperatorsRegistryChange, 23 | OwnerChange, 24 | PauseIntentValidityPeriodBlocksChange, 25 | } from '../generated/schema' 26 | 27 | function loadConfig(): DepositSecurityModuleSettings { 28 | let entity = DepositSecurityModuleSettings.load('') 29 | if (!entity) entity = new DepositSecurityModuleSettings('') 30 | return entity 31 | } 32 | 33 | export function handleDepositsPaused(event: DepositsPaused): void { 34 | let entity = new DepositsPause( 35 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 36 | ) 37 | 38 | entity.guardian = event.params.guardian.toHexString() 39 | 40 | entity.block = event.block.number 41 | entity.blockTime = event.block.timestamp 42 | entity.transactionHash = event.block.hash 43 | 44 | entity.save() 45 | 46 | let config = loadConfig() 47 | config.paused = true 48 | config.save() 49 | } 50 | 51 | export function handleDepositsUnpaused(event: DepositsUnpaused): void { 52 | let entity = new DepositsUnpause( 53 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 54 | ) 55 | 56 | entity.block = event.block.number 57 | entity.blockTime = event.block.timestamp 58 | entity.transactionHash = event.block.hash 59 | 60 | entity.save() 61 | 62 | let config = loadConfig() 63 | config.paused = false 64 | config.save() 65 | } 66 | 67 | export function handleGuardianAdded(event: GuardianAdded): void { 68 | let entity = new Guardian(event.params.guardian.toHexString()) 69 | 70 | entity.address = event.params.guardian 71 | 72 | entity.block = event.block.number 73 | entity.blockTime = event.block.timestamp 74 | entity.transactionHash = event.block.hash 75 | 76 | entity.removed = false 77 | 78 | entity.save() 79 | } 80 | 81 | export function handleGuardianQuorumChanged( 82 | event: GuardianQuorumChanged 83 | ): void { 84 | let newValue = event.params.newValue 85 | 86 | let entity = new GuardianQuorumChange( 87 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 88 | ) 89 | 90 | entity.guardianQuorum = newValue 91 | 92 | entity.block = event.block.number 93 | entity.blockTime = event.block.timestamp 94 | entity.transactionHash = event.block.hash 95 | 96 | entity.save() 97 | 98 | let config = loadConfig() 99 | config.guardianQuorum = newValue 100 | config.save() 101 | } 102 | 103 | export function handleGuardianRemoved(event: GuardianRemoved): void { 104 | let entity = Guardian.load(event.params.guardian.toHexString()) 105 | 106 | // Do we have this guardian? 107 | if (entity) { 108 | entity.removed = true 109 | entity.save() 110 | } 111 | } 112 | 113 | export function handleMaxDepositsChanged(event: MaxDepositsChanged): void { 114 | let newValue = event.params.newValue 115 | 116 | let entity = new MaxDepositsChange( 117 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 118 | ) 119 | 120 | entity.maxDeposits = newValue 121 | 122 | entity.block = event.block.number 123 | entity.blockTime = event.block.timestamp 124 | entity.transactionHash = event.block.hash 125 | 126 | entity.save() 127 | 128 | let config = loadConfig() 129 | config.maxDeposits = newValue 130 | config.save() 131 | } 132 | 133 | export function handleMinDepositBlockDistanceChanged( 134 | event: MinDepositBlockDistanceChanged 135 | ): void { 136 | let newValue = event.params.newValue 137 | 138 | let entity = new MinDepositBlockDistanceChange( 139 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 140 | ) 141 | 142 | entity.minDepositBlockDistance = newValue 143 | 144 | entity.block = event.block.number 145 | entity.blockTime = event.block.timestamp 146 | entity.transactionHash = event.block.hash 147 | 148 | entity.save() 149 | 150 | let config = loadConfig() 151 | config.minDepositBlockDistance = newValue 152 | config.save() 153 | } 154 | 155 | export function handleNodeOperatorsRegistryChanged( 156 | event: NodeOperatorsRegistryChanged 157 | ): void { 158 | let newValue = event.params.newValue 159 | 160 | let entity = new NodeOperatorsRegistryChange( 161 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 162 | ) 163 | 164 | entity.nodeOperatorsRegistry = newValue 165 | 166 | entity.block = event.block.number 167 | entity.blockTime = event.block.timestamp 168 | entity.transactionHash = event.block.hash 169 | 170 | entity.save() 171 | 172 | let config = loadConfig() 173 | config.nodeOperatorsRegistry = newValue 174 | config.save() 175 | } 176 | 177 | export function handleOwnerChanged(event: OwnerChanged): void { 178 | let newValue = event.params.newValue 179 | 180 | let entity = new OwnerChange( 181 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 182 | ) 183 | 184 | entity.owner = newValue 185 | 186 | entity.block = event.block.number 187 | entity.blockTime = event.block.timestamp 188 | entity.transactionHash = event.block.hash 189 | 190 | entity.save() 191 | 192 | let config = loadConfig() 193 | config.owner = newValue 194 | config.save() 195 | } 196 | 197 | export function handlePauseIntentValidityPeriodBlocksChanged( 198 | event: PauseIntentValidityPeriodBlocksChanged 199 | ): void { 200 | let newValue = event.params.newValue 201 | 202 | let entity = new PauseIntentValidityPeriodBlocksChange( 203 | event.transaction.hash.toHex() + '-' + event.logIndex.toString() 204 | ) 205 | 206 | entity.pauseIntentValidityPeriodBlocks = newValue 207 | 208 | entity.block = event.block.number 209 | entity.blockTime = event.block.timestamp 210 | entity.transactionHash = event.block.hash 211 | 212 | entity.save() 213 | 214 | let config = loadConfig() 215 | config.pauseIntentValidityPeriodBlocks = newValue 216 | config.save() 217 | } 218 | -------------------------------------------------------------------------------- /friendtech-by-chainstack/abis/FriendtechSharesV1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "previousOwner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "newOwner", 15 | "type": "address" 16 | } 17 | ], 18 | "name": "OwnershipTransferred", 19 | "type": "event" 20 | }, 21 | { 22 | "anonymous": false, 23 | "inputs": [ 24 | { 25 | "indexed": false, 26 | "internalType": "address", 27 | "name": "trader", 28 | "type": "address" 29 | }, 30 | { 31 | "indexed": false, 32 | "internalType": "address", 33 | "name": "subject", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": false, 38 | "internalType": "bool", 39 | "name": "isBuy", 40 | "type": "bool" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "shareAmount", 46 | "type": "uint256" 47 | }, 48 | { 49 | "indexed": false, 50 | "internalType": "uint256", 51 | "name": "ethAmount", 52 | "type": "uint256" 53 | }, 54 | { 55 | "indexed": false, 56 | "internalType": "uint256", 57 | "name": "protocolEthAmount", 58 | "type": "uint256" 59 | }, 60 | { 61 | "indexed": false, 62 | "internalType": "uint256", 63 | "name": "subjectEthAmount", 64 | "type": "uint256" 65 | }, 66 | { 67 | "indexed": false, 68 | "internalType": "uint256", 69 | "name": "supply", 70 | "type": "uint256" 71 | } 72 | ], 73 | "name": "Trade", 74 | "type": "event" 75 | }, 76 | { 77 | "inputs": [ 78 | { "internalType": "address", "name": "sharesSubject", "type": "address" }, 79 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 80 | ], 81 | "name": "buyShares", 82 | "outputs": [], 83 | "stateMutability": "payable", 84 | "type": "function" 85 | }, 86 | { 87 | "inputs": [ 88 | { "internalType": "address", "name": "sharesSubject", "type": "address" }, 89 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 90 | ], 91 | "name": "getBuyPrice", 92 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 93 | "stateMutability": "view", 94 | "type": "function" 95 | }, 96 | { 97 | "inputs": [ 98 | { "internalType": "address", "name": "sharesSubject", "type": "address" }, 99 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 100 | ], 101 | "name": "getBuyPriceAfterFee", 102 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 103 | "stateMutability": "view", 104 | "type": "function" 105 | }, 106 | { 107 | "inputs": [ 108 | { "internalType": "uint256", "name": "supply", "type": "uint256" }, 109 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 110 | ], 111 | "name": "getPrice", 112 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 113 | "stateMutability": "pure", 114 | "type": "function" 115 | }, 116 | { 117 | "inputs": [ 118 | { "internalType": "address", "name": "sharesSubject", "type": "address" }, 119 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 120 | ], 121 | "name": "getSellPrice", 122 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 123 | "stateMutability": "view", 124 | "type": "function" 125 | }, 126 | { 127 | "inputs": [ 128 | { "internalType": "address", "name": "sharesSubject", "type": "address" }, 129 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 130 | ], 131 | "name": "getSellPriceAfterFee", 132 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 133 | "stateMutability": "view", 134 | "type": "function" 135 | }, 136 | { 137 | "inputs": [], 138 | "name": "owner", 139 | "outputs": [{ "internalType": "address", "name": "", "type": "address" }], 140 | "stateMutability": "view", 141 | "type": "function" 142 | }, 143 | { 144 | "inputs": [], 145 | "name": "protocolFeeDestination", 146 | "outputs": [{ "internalType": "address", "name": "", "type": "address" }], 147 | "stateMutability": "view", 148 | "type": "function" 149 | }, 150 | { 151 | "inputs": [], 152 | "name": "protocolFeePercent", 153 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 154 | "stateMutability": "view", 155 | "type": "function" 156 | }, 157 | { 158 | "inputs": [], 159 | "name": "renounceOwnership", 160 | "outputs": [], 161 | "stateMutability": "nonpayable", 162 | "type": "function" 163 | }, 164 | { 165 | "inputs": [ 166 | { "internalType": "address", "name": "sharesSubject", "type": "address" }, 167 | { "internalType": "uint256", "name": "amount", "type": "uint256" } 168 | ], 169 | "name": "sellShares", 170 | "outputs": [], 171 | "stateMutability": "payable", 172 | "type": "function" 173 | }, 174 | { 175 | "inputs": [ 176 | { 177 | "internalType": "address", 178 | "name": "_feeDestination", 179 | "type": "address" 180 | } 181 | ], 182 | "name": "setFeeDestination", 183 | "outputs": [], 184 | "stateMutability": "nonpayable", 185 | "type": "function" 186 | }, 187 | { 188 | "inputs": [ 189 | { "internalType": "uint256", "name": "_feePercent", "type": "uint256" } 190 | ], 191 | "name": "setProtocolFeePercent", 192 | "outputs": [], 193 | "stateMutability": "nonpayable", 194 | "type": "function" 195 | }, 196 | { 197 | "inputs": [ 198 | { "internalType": "uint256", "name": "_feePercent", "type": "uint256" } 199 | ], 200 | "name": "setSubjectFeePercent", 201 | "outputs": [], 202 | "stateMutability": "nonpayable", 203 | "type": "function" 204 | }, 205 | { 206 | "inputs": [ 207 | { "internalType": "address", "name": "", "type": "address" }, 208 | { "internalType": "address", "name": "", "type": "address" } 209 | ], 210 | "name": "sharesBalance", 211 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 212 | "stateMutability": "view", 213 | "type": "function" 214 | }, 215 | { 216 | "inputs": [{ "internalType": "address", "name": "", "type": "address" }], 217 | "name": "sharesSupply", 218 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 219 | "stateMutability": "view", 220 | "type": "function" 221 | }, 222 | { 223 | "inputs": [], 224 | "name": "subjectFeePercent", 225 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 226 | "stateMutability": "view", 227 | "type": "function" 228 | }, 229 | { 230 | "inputs": [ 231 | { "internalType": "address", "name": "newOwner", "type": "address" } 232 | ], 233 | "name": "transferOwnership", 234 | "outputs": [], 235 | "stateMutability": "nonpayable", 236 | "type": "function" 237 | } 238 | ] 239 | -------------------------------------------------------------------------------- /lido-subgraph/abis/Billing.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { "internalType": "address", "name": "_gateway", "type": "address" }, 5 | { 6 | "internalType": "contract IERC20", 7 | "name": "_token", 8 | "type": "address" 9 | }, 10 | { "internalType": "address", "name": "_governor", "type": "address" } 11 | ], 12 | "stateMutability": "nonpayable", 13 | "type": "constructor" 14 | }, 15 | { 16 | "anonymous": false, 17 | "inputs": [ 18 | { 19 | "indexed": true, 20 | "internalType": "address", 21 | "name": "newGateway", 22 | "type": "address" 23 | } 24 | ], 25 | "name": "GatewayUpdated", 26 | "type": "event" 27 | }, 28 | { 29 | "anonymous": false, 30 | "inputs": [ 31 | { 32 | "indexed": true, 33 | "internalType": "address", 34 | "name": "from", 35 | "type": "address" 36 | }, 37 | { 38 | "indexed": true, 39 | "internalType": "address", 40 | "name": "to", 41 | "type": "address" 42 | } 43 | ], 44 | "name": "NewOwnership", 45 | "type": "event" 46 | }, 47 | { 48 | "anonymous": false, 49 | "inputs": [ 50 | { 51 | "indexed": true, 52 | "internalType": "address", 53 | "name": "from", 54 | "type": "address" 55 | }, 56 | { 57 | "indexed": true, 58 | "internalType": "address", 59 | "name": "to", 60 | "type": "address" 61 | } 62 | ], 63 | "name": "NewPendingOwnership", 64 | "type": "event" 65 | }, 66 | { 67 | "anonymous": false, 68 | "inputs": [ 69 | { 70 | "indexed": true, 71 | "internalType": "address", 72 | "name": "user", 73 | "type": "address" 74 | }, 75 | { 76 | "indexed": false, 77 | "internalType": "uint256", 78 | "name": "amount", 79 | "type": "uint256" 80 | } 81 | ], 82 | "name": "TokensAdded", 83 | "type": "event" 84 | }, 85 | { 86 | "anonymous": false, 87 | "inputs": [ 88 | { 89 | "indexed": true, 90 | "internalType": "address", 91 | "name": "user", 92 | "type": "address" 93 | }, 94 | { 95 | "indexed": false, 96 | "internalType": "uint256", 97 | "name": "amount", 98 | "type": "uint256" 99 | } 100 | ], 101 | "name": "TokensPulled", 102 | "type": "event" 103 | }, 104 | { 105 | "anonymous": false, 106 | "inputs": [ 107 | { 108 | "indexed": true, 109 | "internalType": "address", 110 | "name": "user", 111 | "type": "address" 112 | }, 113 | { 114 | "indexed": true, 115 | "internalType": "address", 116 | "name": "to", 117 | "type": "address" 118 | }, 119 | { 120 | "indexed": false, 121 | "internalType": "uint256", 122 | "name": "amount", 123 | "type": "uint256" 124 | } 125 | ], 126 | "name": "TokensRemoved", 127 | "type": "event" 128 | }, 129 | { 130 | "anonymous": false, 131 | "inputs": [ 132 | { 133 | "indexed": true, 134 | "internalType": "address", 135 | "name": "to", 136 | "type": "address" 137 | }, 138 | { 139 | "indexed": true, 140 | "internalType": "address", 141 | "name": "token", 142 | "type": "address" 143 | }, 144 | { 145 | "indexed": false, 146 | "internalType": "uint256", 147 | "name": "amount", 148 | "type": "uint256" 149 | } 150 | ], 151 | "name": "TokensRescued", 152 | "type": "event" 153 | }, 154 | { 155 | "inputs": [], 156 | "name": "acceptOwnership", 157 | "outputs": [], 158 | "stateMutability": "nonpayable", 159 | "type": "function" 160 | }, 161 | { 162 | "inputs": [ 163 | { "internalType": "uint256", "name": "_amount", "type": "uint256" } 164 | ], 165 | "name": "add", 166 | "outputs": [], 167 | "stateMutability": "nonpayable", 168 | "type": "function" 169 | }, 170 | { 171 | "inputs": [ 172 | { "internalType": "address", "name": "_to", "type": "address" }, 173 | { "internalType": "uint256", "name": "_amount", "type": "uint256" } 174 | ], 175 | "name": "addTo", 176 | "outputs": [], 177 | "stateMutability": "nonpayable", 178 | "type": "function" 179 | }, 180 | { 181 | "inputs": [ 182 | { "internalType": "address[]", "name": "_to", "type": "address[]" }, 183 | { "internalType": "uint256[]", "name": "_amount", "type": "uint256[]" } 184 | ], 185 | "name": "addToMany", 186 | "outputs": [], 187 | "stateMutability": "nonpayable", 188 | "type": "function" 189 | }, 190 | { 191 | "inputs": [], 192 | "name": "gateway", 193 | "outputs": [{ "internalType": "address", "name": "", "type": "address" }], 194 | "stateMutability": "view", 195 | "type": "function" 196 | }, 197 | { 198 | "inputs": [], 199 | "name": "governor", 200 | "outputs": [{ "internalType": "address", "name": "", "type": "address" }], 201 | "stateMutability": "view", 202 | "type": "function" 203 | }, 204 | { 205 | "inputs": [], 206 | "name": "pendingGovernor", 207 | "outputs": [{ "internalType": "address", "name": "", "type": "address" }], 208 | "stateMutability": "view", 209 | "type": "function" 210 | }, 211 | { 212 | "inputs": [ 213 | { "internalType": "address", "name": "_user", "type": "address" }, 214 | { "internalType": "uint256", "name": "_amount", "type": "uint256" }, 215 | { "internalType": "address", "name": "_to", "type": "address" } 216 | ], 217 | "name": "pull", 218 | "outputs": [], 219 | "stateMutability": "nonpayable", 220 | "type": "function" 221 | }, 222 | { 223 | "inputs": [ 224 | { "internalType": "address[]", "name": "_users", "type": "address[]" }, 225 | { "internalType": "uint256[]", "name": "_amounts", "type": "uint256[]" }, 226 | { "internalType": "address", "name": "_to", "type": "address" } 227 | ], 228 | "name": "pullMany", 229 | "outputs": [], 230 | "stateMutability": "nonpayable", 231 | "type": "function" 232 | }, 233 | { 234 | "inputs": [ 235 | { "internalType": "address", "name": "_user", "type": "address" }, 236 | { "internalType": "uint256", "name": "_amount", "type": "uint256" } 237 | ], 238 | "name": "remove", 239 | "outputs": [], 240 | "stateMutability": "nonpayable", 241 | "type": "function" 242 | }, 243 | { 244 | "inputs": [ 245 | { "internalType": "address", "name": "_to", "type": "address" }, 246 | { "internalType": "address", "name": "_token", "type": "address" }, 247 | { "internalType": "uint256", "name": "_amount", "type": "uint256" } 248 | ], 249 | "name": "rescueTokens", 250 | "outputs": [], 251 | "stateMutability": "nonpayable", 252 | "type": "function" 253 | }, 254 | { 255 | "inputs": [ 256 | { "internalType": "address", "name": "_newGateway", "type": "address" } 257 | ], 258 | "name": "setGateway", 259 | "outputs": [], 260 | "stateMutability": "nonpayable", 261 | "type": "function" 262 | }, 263 | { 264 | "inputs": [ 265 | { "internalType": "address", "name": "_newGovernor", "type": "address" } 266 | ], 267 | "name": "transferOwnership", 268 | "outputs": [], 269 | "stateMutability": "nonpayable", 270 | "type": "function" 271 | }, 272 | { 273 | "inputs": [{ "internalType": "address", "name": "", "type": "address" }], 274 | "name": "userBalances", 275 | "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], 276 | "stateMutability": "view", 277 | "type": "function" 278 | } 279 | ] 280 | --------------------------------------------------------------------------------