Skip to content

Commit

Permalink
Alternative RPC for fetching events (#678)
Browse files Browse the repository at this point in the history
Alternative RPC for fetching events
  • Loading branch information
muhammedea committed Jan 18, 2024
1 parent 4bfbb17 commit 0f95bc4
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 48 deletions.
12 changes: 12 additions & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ export const ubeGovernanceAddresses = {
[ChainId.BAKLAVA]: '0xa7581d8E26007f4D2374507736327f5b46Dd6bA8',
}

export const farmRegistryAddresses = {
[ChainId.MAINNET]: '0xa2bf67e12EeEDA23C7cA1e5a34ae2441a17789Ec',
[ChainId.ALFAJORES]: '0xa2bf67e12EeEDA23C7cA1e5a34ae2441a17789Ec',
[ChainId.BAKLAVA]: '0xa2bf67e12EeEDA23C7cA1e5a34ae2441a17789Ec',
}

export const KNOWN_ADDRESSES: Record<
string,
{
Expand Down Expand Up @@ -281,3 +287,9 @@ export const DEXES_TO_EXCLUDE = [
'uniswap-v3',
...(process.env.REACT_APP_DEX_EXCLUSION_LIST?.split(',') ?? []),
]

export const EVENT_FETCH_RPC_URLS = {
[ChainId.MAINNET]: 'https://celo-mainnet.infura.io/v3/801f4c55ea6b48b4b629c9645964eaa9',
[ChainId.ALFAJORES]: '',
[ChainId.BAKLAVA]: '',
}
3 changes: 2 additions & 1 deletion src/hooks/romulus/useProposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BigNumber } from 'ethers'
import { TypedEvent } from 'generated/common'
import { useRomulusDelegateContract } from 'hooks/useContract'
import { useCallback, useEffect, useRef, useState } from 'react'
import fetchEvents from 'utils/fetchEvents'

import { ubeGovernanceAddresses } from '../../constants'

Expand All @@ -29,7 +30,7 @@ export const useProposals = (): Array<TypedEvent<Proposal>> | undefined => {
const call = useCallback(async () => {
if (!romulusAddress || !romulusContract || !mountRef.current) return
const filter = romulusContract.filters.ProposalCreated(null, null, null, null, null, null, null, null, null)
const proposalEvents = await romulusContract.queryFilter(filter)
const proposalEvents = await fetchEvents<TypedEvent<Proposal>>(romulusContract, filter)
setProposals(proposalEvents)
}, [romulusContract, romulusAddress])

Expand Down
11 changes: 10 additions & 1 deletion src/hooks/romulus/useVoteCasts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import { BigNumber } from 'ethers'
import { TypedEvent } from 'generated/common'
import { useRomulusDelegateContract } from 'hooks/useContract'
import { useCallback, useEffect, useRef, useState } from 'react'
import fetchEvents from 'utils/fetchEvents'

type VoteCast = [string, BigNumber, number, BigNumber, string] & {
voter: string
proposalId: BigNumber
support: number
votes: BigNumber
reason: string
}

type VoteMap = {
[proposalId: string]: TypedEvent<
Expand All @@ -29,7 +38,7 @@ export const useVoteCasts = (romulusAddress: Address) => {
return
}
const filter = romulusContract.filters.VoteCast(address, null, null, null, null)
const voteEvents = await romulusContract.queryFilter(filter)
const voteEvents = await fetchEvents<TypedEvent<VoteCast>>(romulusContract, filter)
setVoteEvents(
voteEvents.reduce((acc, event) => {
acc[event.args.proposalId.toString()] = event
Expand Down
6 changes: 6 additions & 0 deletions src/hooks/useContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { StakingInfo } from 'state/stake/hooks'

import ENS_PUBLIC_RESOLVER_ABI from '../constants/abis/ens-public-resolver.json'
import ERC20_ABI, { ERC20_BYTES32_ABI } from '../constants/abis/erc20'
import FARM_REGISTRY_ABI from '../constants/abis/FarmRegistry.json'
import LIMIT_ORDER_PROTOCOL_ABI from '../constants/abis/limit/LimitOrderProtocol.json'
import ORDER_BOOK_ABI from '../constants/abis/limit/OrderBook.json'
import ORDER_BOOK_REWARD_DISTRUBUTOR_ABI from '../constants/abis/limit/OrderBookRewardDistributor.json'
Expand All @@ -21,6 +22,7 @@ import VOTABLE_STAKING_REWARDS_ABI from '../constants/abis/VotableStakingRewards
import { MULTICALL_ABI, MULTICALL_NETWORKS } from '../constants/multicall'
import {
Erc20,
FarmRegistry,
LimitOrderProtocol,
MoolaStakingRewards,
OrderBook,
Expand Down Expand Up @@ -165,3 +167,7 @@ export function useRomulusDelegateContract(address?: string, withSignerIfPossibl
export function usePoofTokenContract(address?: string, withSignerIfPossible?: boolean): PoofToken | null {
return useContract(address, POOF_TOKEN_ABI, withSignerIfPossible) as PoofToken | null
}

export function useFarmRegistryContract(address?: string, withSignerIfPossible?: boolean): FarmRegistry | null {
return useContract(address, FARM_REGISTRY_ABI, withSignerIfPossible) as FarmRegistry | null
}
80 changes: 43 additions & 37 deletions src/pages/Earn/useFarmRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { ApolloQueryResult, gql, useApolloClient, useQuery } from '@apollo/clien
import { useCelo } from '@celo/react-celo'
import { BigNumber } from '@ethersproject/bignumber'
import { formatEther, parseEther } from '@ethersproject/units'
import { Percent, TokenAmount } from '@ubeswap/sdk'
import { ChainId, Percent, TokenAmount } from '@ubeswap/sdk'
import { ethers } from 'ethers'
import { FarmDataEvent, FarmInfoEvent, LPInfoEvent } from 'generated/FarmRegistry'
import { useFarmRegistryContract } from 'hooks/useContract'
import React, { useEffect } from 'react'
import { AbiItem } from 'web3-utils'
import fetchEvents from 'utils/fetchEvents'

import farmRegistryAbi from '../../constants/abis/FarmRegistry.json'
import { farmRegistryAddresses } from '../../constants'
import { CACHED_FARM_INFO_BLOCK, cachedFarmInfoEvents, cachedLpInfoEvents } from './cachedFarmInfo'
import { useCustomStakingInfo } from './useCustomStakingInfo'

Expand Down Expand Up @@ -57,7 +59,6 @@ const pairDataGql = gql`
}
`
const COMPOUNDS_PER_YEAR = 2
const CREATION_BLOCK = 9840049
const LAST_N_BLOCKS = 5760 // Last 8 hours

export interface WarningInfo {
Expand All @@ -66,59 +67,64 @@ export interface WarningInfo {
}

export const useFarmRegistry = () => {
const { kit } = useCelo()
const { network } = useCelo()
const farmRegistryAddress = farmRegistryAddresses[network.chainId as ChainId]
const farmRegistryContract = useFarmRegistryContract(farmRegistryAddress)
const client = useApolloClient()
const [farmSummaries, setFarmSummaries] = React.useState<FarmSummary[]>([])
const olderFarmInfoEvents = cachedFarmInfoEvents.map((e) => e.returnValues)
const olderLpInfoEvents = cachedLpInfoEvents.map((e) => e.returnValues)

const call = React.useCallback(async () => {
const farmRegistry = new kit.connection.web3.eth.Contract(
farmRegistryAbi as AbiItem[],
'0xa2bf67e12EeEDA23C7cA1e5a34ae2441a17789Ec'
)
const lastBlock = await kit.connection.web3.eth.getBlockNumber()
if (!farmRegistryAddress || !farmRegistryContract || !client) return

const farmInfoFilter = farmRegistryContract.filters.FarmInfo()
const lpInfoFilter = farmRegistryContract.filters.LPInfo()
const farmDataFilter = farmRegistryContract.filters.FarmData()
const [farmInfoEvents, lpInfoEvents, farmDataEvents] = await Promise.all([
farmRegistry
.getPastEvents('FarmInfo', {
fromBlock: CACHED_FARM_INFO_BLOCK,
toBlock: lastBlock,
})
.then((events) => events.concat(cachedFarmInfoEvents)),
farmRegistry
.getPastEvents('LPInfo', { fromBlock: CACHED_FARM_INFO_BLOCK, toBlock: lastBlock })
.then((events) => events.concat(cachedLpInfoEvents)),
farmRegistry.getPastEvents('FarmData', {
fromBlock: lastBlock - LAST_N_BLOCKS,
toBlock: lastBlock,
fetchEvents<FarmInfoEvent>(farmRegistryContract, farmInfoFilter, CACHED_FARM_INFO_BLOCK, 'latest').then(
(events) => {
const onlyArgs = events.map((e) => e.args)
return olderFarmInfoEvents.concat(onlyArgs)
}
),
fetchEvents<LPInfoEvent>(farmRegistryContract, lpInfoFilter, CACHED_FARM_INFO_BLOCK, 'latest').then((events) => {
const onlyArgs = events.map((e) => e.args)
return olderLpInfoEvents.concat(onlyArgs)
}),
fetchEvents<FarmDataEvent>(farmRegistryContract, farmDataFilter, -LAST_N_BLOCKS, 'latest').then((events) =>
events.map((e) => e.args)
),
])

const lps: Record<string, [string, string]> = {}
lpInfoEvents.forEach((e) => {
lps[e.returnValues.lpAddress] = [e.returnValues.token0Address, e.returnValues.token1Address]
lps[e.lpAddress] = [e.token0Address, e.token1Address]
})
const farmData: Record<string, FarmData> = {}
farmDataEvents.forEach((e) => {
farmData[e.returnValues.stakingAddress] = {
tvlUSD: e.returnValues.tvlUSD,
rewardsUSDPerYear: e.returnValues.rewardsUSDPerYear,
farmData[e.stakingAddress] = {
tvlUSD: e.tvlUSD.toString(),
rewardsUSDPerYear: e.rewardsUSDPerYear.toString(),
}
})
const farmSummaries: FarmSummary[] = []
farmInfoEvents
.filter((e) => !blacklist[e.returnValues.stakingAddress.toLowerCase()])
.filter((e) => !blacklist[e.stakingAddress.toLowerCase()])
.forEach((e) => {
// sometimes there is no farm data for the staking address return early to avoid crash
if (!farmData[e.returnValues.stakingAddress]) {
if (!farmData[e.stakingAddress]) {
return
}
farmSummaries.push({
farmName: ethers.utils.parseBytes32String(e.returnValues.farmName),
stakingAddress: e.returnValues.stakingAddress,
lpAddress: e.returnValues.lpAddress,
token0Address: lps[e.returnValues.lpAddress][0],
token1Address: lps[e.returnValues.lpAddress][1],
tvlUSD: BigNumber.from(farmData[e.returnValues.stakingAddress].tvlUSD),
rewardsUSDPerYear: BigNumber.from(farmData[e.returnValues.stakingAddress].rewardsUSDPerYear),
isFeatured: !!featuredPoolWhitelist[e.returnValues.stakingAddress],
farmName: ethers.utils.parseBytes32String(e.farmName),
stakingAddress: e.stakingAddress,
lpAddress: e.lpAddress,
token0Address: lps[e.lpAddress][0],
token1Address: lps[e.lpAddress][1],
tvlUSD: BigNumber.from(farmData[e.stakingAddress].tvlUSD),
rewardsUSDPerYear: BigNumber.from(farmData[e.stakingAddress].rewardsUSDPerYear),
isFeatured: !!featuredPoolWhitelist[e.stakingAddress],
isImported: false,
})
})
Expand All @@ -139,7 +145,7 @@ export const useFarmRegistry = () => {
...farmInfos[index],
}))
)
}, [kit.connection.web3.eth, client])
}, [farmRegistryAddress, farmRegistryContract, client, olderFarmInfoEvents, olderLpInfoEvents])

useEffect(() => {
call()
Expand Down
5 changes: 4 additions & 1 deletion src/pages/LimitOrder/executCancelOrder.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { CancelLimitOrderExecutor } from 'components/swap/routing'
import { ContractTransaction } from 'ethers'
import { LimitOrderProtocol__factory, OrderBook__factory } from 'generated'
import { OrderBroadcastedEvent } from 'generated/OrderBook'
import fetchEvents from 'utils/fetchEvents'

import { LIMIT_ORDER_ADDRESS, ORDER_BOOK_ADDRESS } from '../../constants'

Expand All @@ -12,7 +14,8 @@ export const executeCancelOrder: CancelLimitOrderExecutor = async ({ signer, cha
const limitOrderProtocolFactory = LimitOrderProtocol__factory.connect(limitOrderAddr, signer)

const cancel = async (): Promise<ContractTransaction> => {
const orders = await orderBook.queryFilter(orderBook.filters['OrderBroadcasted'](undefined, orderHash), 0, 'latest')
const filter = orderBook.filters['OrderBroadcasted'](undefined, orderHash)
const orders = await fetchEvents<OrderBroadcastedEvent>(orderBook, filter)
if (orders.length === 0) {
throw new Error('Error finding the order')
} else if (orders.length > 0) {
Expand Down
12 changes: 4 additions & 8 deletions src/pages/LimitOrder/useOrderBroadcasted.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import { useCelo, useProvider } from '@celo/react-celo'
import { ChainId } from '@ubeswap/sdk'
import { BigNumber } from 'ethers'
import { OrderBook__factory } from 'generated'
import { OrderBroadcastedEvent } from 'generated/OrderBook'
import { useLimitOrderProtocolContract } from 'hooks/useContract'
import React, { useEffect } from 'react'
import { useSingleContractMultipleData } from 'state/multicall/hooks'
import fetchEvents from 'utils/fetchEvents'

import { LIMIT_ORDER_ADDRESS, ORDER_BOOK_ADDRESS } from '../../constants'

// TODO: Just do batch fetching in the future
const CREATION_BLOCK = 10_000_000

export interface LimitOrdersHistory {
orderHash: string
isOrderOpen: boolean
Expand Down Expand Up @@ -63,11 +62,8 @@ export const useOrderBroadcasted = () => {
return
}
const orderBook = OrderBook__factory.connect(orderBookAddr, provider)
const orderBookEvents = await orderBook.queryFilter(
orderBook.filters['OrderBroadcasted'](account),
CREATION_BLOCK,
'latest'
)
const filter = orderBook.filters['OrderBroadcasted'](account)
const orderBookEvents = await fetchEvents<OrderBroadcastedEvent>(orderBook, filter)
const orderBroadcasts = orderBookEvents.map((orderBookEvent) => {
return {
...orderBookEvent.args,
Expand Down
26 changes: 26 additions & 0 deletions src/utils/fetchEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { BlockTag } from '@ethersproject/abstract-provider'
import { JsonRpcProvider } from '@ethersproject/providers'
import { ChainId } from '@ubeswap/sdk'
import { BaseContract, Event, EventFilter } from 'ethers'

import { EVENT_FETCH_RPC_URLS } from '../constants'

export default async function fetchEvents<T>(
contract: BaseContract,
filter: EventFilter,
fromBlockOrBlockhash?: BlockTag | undefined,
toBlock?: BlockTag | undefined
): Promise<T[]> {
const chainId = (await contract.provider.getNetwork()).chainId

const promises = [contract.queryFilter(filter, fromBlockOrBlockhash, toBlock)]

const alternativeRpc = EVENT_FETCH_RPC_URLS[chainId as ChainId]
if (alternativeRpc) {
const alternativeProvider = new JsonRpcProvider(alternativeRpc)
promises.push(contract.connect(alternativeProvider).queryFilter(filter, fromBlockOrBlockhash, toBlock))
}

const result = await Promise.any<Event[]>(promises)
return result as unknown as T[]
}

1 comment on commit 0f95bc4

@vercel
Copy link

@vercel vercel bot commented on 0f95bc4 Jan 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.