Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alternative RPC for fetching events #678

Merged
merged 2 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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[]
}
Loading