Skip to content

Commit

Permalink
clean up react functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
rossgalloway committed Nov 21, 2024
1 parent ac0f834 commit 898547e
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 90 deletions.
12 changes: 2 additions & 10 deletions src/components/AddressCheck.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import React, { useContext, useEffect, useState } from 'react'
import React, { useContext } from 'react'
import { ContractAddressContext } from '../context/ContractAddressesContext'
import styles from '../css/AddressCheck.module.css'

function AddressCheck() {
const data = useContext(ContractAddressContext)
const [loading, setLoading] = useState(true)
const checks = data.checks
const failedChecks = data.checks.failedChecks
const allChecksPassed = data.checks.allChecksPassed

useEffect(() => {
if (checks && Object.keys(checks).length > 0) {
// Check if checks object isn't empty
setLoading(false)
}
}, [checks])
const loading = !checks || Object.keys(checks).length === 0

if (loading) {
return (
Expand All @@ -29,8 +23,6 @@ function AddressCheck() {
)
}

// allChecksPassed = false // only for testing!!

// Log failed checks to the console
if (!allChecksPassed) {
console.log('Some checks failed:', failedChecks)
Expand Down
10 changes: 2 additions & 8 deletions src/components/ContractAddress.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useState, useEffect } from 'react'
import React, { useContext } from 'react'
import { ContractAddressContext } from '../context/ContractAddressesContext'

/**
Expand All @@ -14,19 +14,13 @@ import { ContractAddressContext } from '../context/ContractAddressesContext'
*/
const ContractAddress = ({ contractName }) => {
const data = useContext(ContractAddressContext)
const [loading, setLoading] = useState(true)
const addresses = data.addresses
const loading = !data

const getNestedProperty = (obj, keys) => {
return keys.reduce((acc, key) => acc && acc[key], obj)
}

useEffect(() => {
if (data) {
setLoading(false)
}
}, [data])

if (loading) {
return <span>Loading Contract Address...</span>
}
Expand Down
5 changes: 1 addition & 4 deletions src/components/StaticContractAddress.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import React, { useContext, useState, useEffect } from 'react'
import { ContractAddressContext } from '../context/ContractAddressesContext'
import React from 'react'
import * as constants from '../ethereum/constants'

const ContractAddress = ({ contractName }) => {
const data = useContext(ContractAddressContext)

const getNestedProperty = (obj, keys) => {
return keys.reduce((acc, key) => acc && acc[key], obj)
}
Expand Down
14 changes: 7 additions & 7 deletions src/context/ContractAddressesContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ContractAddresses } from '../ethereum/types'
export const ContractAddressContext = createContext<{
addresses: ContractAddresses | Record<string, string | undefined>
checks: Record<string, any>
}>({ addresses: {}, checks: {} }) // Modified code: Updated context type
}>({ addresses: {}, checks: {} })

/**
* Provides contract addresses to the component tree.
Expand Down Expand Up @@ -43,46 +43,46 @@ export const ContractAddressProvider = ({ children }) => {
useEffect(() => {
const fetchAddresses = async () => {
try {
let checkFlag: boolean | undefined
let checkFlag: boolean | undefined = true
const failedChecks: string[] = []
checkFlag = true

const topLevelData = await fetchTopLevelAddressesFromENS(
publicClient,
checkFlag,
failedChecks
)
checkFlag = topLevelData?.checkFlag
if (!topLevelData)
throw new Error('Failed to fetch top-level contract addresses')
checkFlag = topLevelData.checkFlag

const protocolPeripheryData = await fetchAndCheckProtocolAddresses(
topLevelData.addresses.v3ProtocolAddressProvider,
publicClient,
checkFlag,
failedChecks
)
checkFlag = protocolPeripheryData?.checkFlag
if (!protocolPeripheryData || !protocolPeripheryData?.addresses)
throw new Error('Failed to fetch protocol addresses')
checkFlag = protocolPeripheryData.checkFlag

const releaseRegistryData = await fetchAndCheckFromReleaseRegistry(
topLevelData.addresses.v3ReleaseRegistry,
publicClient,
checkFlag,
failedChecks
)
checkFlag = releaseRegistryData?.checkFlag
if (!releaseRegistryData)
throw new Error('Failed to fetch release registry addresses')
checkFlag = releaseRegistryData?.checkFlag

const yearnV3Data = await fetchAndCheckYearnV3Addresses(
topLevelData.addresses.v3RoleManager,
publicClient,
checkFlag,
failedChecks
)
checkFlag = yearnV3Data?.checkFlag
if (!yearnV3Data) throw new Error('Failed to fetch Yearn V3 addresses')
checkFlag = yearnV3Data.checkFlag

const addressesData: ContractAddresses = {
topLevel: topLevelData.addresses,
Expand Down
159 changes: 98 additions & 61 deletions src/context/ContractDataContext.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// src/context/ContractDataContext.tsx
import React, { createContext, useState, useEffect, useContext } from 'react'
import React, {
createContext,
useState,
useEffect,
useContext,
useMemo,
} from 'react'
import { PublicClientContext } from './PublicClientContext'
import * as ABIs from '../ethereum/ABIs'
import { createPublicClient, getContract, http, getAddress } from 'viem'
import { mainnet } from 'viem/chains'
import { getAddress, getContract } from 'viem'

interface MethodWithArgs {
type MethodWithArgs = {
name: string
args: string[]
}

interface ContractReadData {
type ContractReadData = {
name: string
chain: string
address: string
Expand Down Expand Up @@ -39,69 +43,102 @@ const isContractReadData = (obj: any): obj is ContractReadData => {

export const ContractDataContext = createContext({})

export const ContractDataProvider = ({ children, contractParams }) => {
const [data, setData] = useState({})
const publicClient = useContext(PublicClientContext)
/**
* Fetches data from multiple contract read calls and updates the state with the results.
*
* @param {ContractReadData[]} contractReadParams - An array of contract read parameters, each containing the contract address, ABI name, and methods to call.
* @param {any} publicClient - The public client used to interact with the blockchain.
* @param {Record<string, any>} ABIs - A record of ABI names to ABI definitions.
* @param {(value: React.SetStateAction<{}>) => void} setData - A function to update the state with the fetched data.
*
* @returns {Promise<void>} A promise that resolves when all contract read calls have been completed and the state has been updated.
*
* @throws Will throw an error if there is an issue with fetching contract data.
*/
const fetchData = async (
contractReadParams: ContractReadData[],
publicClient,
ABIs,
setData: {
(value: React.SetStateAction<{}>): void
(arg0: (prevData: any) => any): void
}
) => {
try {
for (const contractReadCall of contractReadParams) {
const address = contractReadCall.address
const abi = ABIs[contractReadCall.abiName]

useEffect(() => {
const fetchData = async () => {
try {
const contractReadParams: ContractReadData[] = []
for (const rpcCall of contractParams) {
if (isContractReadData(rpcCall)) {
contractReadParams.push(rpcCall)
} else {
console.error('Invalid contract read data:', rpcCall)
}
if (!publicClient) {
console.error('publicClient is null')
return
}

const contract = getContract({
address: getAddress(address),
abi: abi,
client: publicClient,
})

// Dynamically call methods from contractReadCall
const methodCalls = contractReadCall.methods.map((method) => {
if (typeof method === 'string') {
// @ts-ignore
return contract.read[method]()
} else {
// @ts-ignore
return contract.read[method.name](method.args)
}
for (const contractReadCall of contractReadParams) {
const address = contractReadCall.address
})

const abi = ABIs[contractReadCall.abiName]
const results = await Promise.all(methodCalls) // Await all method calls

if (!publicClient) {
console.error('publicClient is null')
return
setData((prevData) => {
const newData = { ...prevData }
results.forEach((result, index) => {
const methodName =
typeof contractReadCall.methods[index] === 'string'
? contractReadCall.methods[index]
: contractReadCall.methods[index].name
if (!newData[contractReadCall.name]) {
newData[contractReadCall.name] = {}
}
const contract = getContract({
address: getAddress(address),
abi: abi,
client: publicClient,
})

// Dynamically call methods from contractReadCall
const methodCalls = contractReadCall.methods.map((method) => {
if (typeof method === 'string') {
return contract.read[method]() // Call method without arguments
} else {
return contract.read[method.name](method.args) // Call method with arguments
}
})
newData[contractReadCall.name][methodName] = result
})
return newData
})
}
} catch (error) {
console.error('Error fetching contract data:', error)
}
}

const results = await Promise.all(methodCalls) // Await all method calls
/**
* Provides contract data to its children components.
*
* This context provider fetches on-chain data based on the provided contract parameters
* and makes it available to its children components via context.
*
* @param {Object} props - The props object.
* @param {React.ReactNode} props.children - The child components that will have access to the contract data.
* @param {Array} props.contractParams - The parameters used to fetch contract data.
*
* @returns {JSX.Element} The context provider component that supplies contract data.
*/
export const ContractDataProvider = ({ children, contractParams }) => {
const [data, setData] = useState({})
const publicClient = useContext(PublicClientContext)

setData((prevData) => {
const newData = { ...prevData }
results.forEach((result, index) => {
const methodName =
typeof contractReadCall.methods[index] === 'string'
? contractReadCall.methods[index]
: contractReadCall.methods[index].name
if (!newData[contractReadCall.name]) {
newData[contractReadCall.name] = {}
}
newData[contractReadCall.name][methodName] = result
})
return newData
})
}
} catch (error) {
console.error('Error fetching contract data:', error)
}
}
// Memoize contractReadParams to prevent unnecessary re-renders
const contractReadParams = useMemo(
() => contractParams.filter(isContractReadData),
[contractParams]
)

fetchData()
}, [contractParams, publicClient])
useEffect(() => {
console.log('fetching on-chain data...')
fetchData(contractReadParams, publicClient, ABIs, setData)
}, [contractReadParams, publicClient])

return (
<ContractDataContext.Provider value={data}>
Expand Down

0 comments on commit 898547e

Please sign in to comment.