Created
September 2, 2022 14:39
-
-
Save bswags/ccbc409cda18abf139e575878e37e7e3 to your computer and use it in GitHub Desktop.
Query transfers for an ERC721 to materialize currently owned ones
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { BigNumber, ethers } from 'ethers'; | |
import { sortBy } from 'ramda'; | |
import { useCallback, useEffect, useMemo, useState } from 'react'; | |
import { useContractEvent, useProvider } from 'wagmi'; | |
import { NFT_CONTRACT_ABI, NFT_CONTRACT_ADDRESS, nftConfig } from './contracts'; | |
export interface TokenState { | |
owned: number[]; | |
} | |
interface TransferEvent { | |
block: number; | |
send: boolean; | |
tokenId: number; | |
} | |
const toEvent = (event: ethers.Event, send: boolean): TransferEvent | null => { | |
let tokenId; | |
if (event.args) { | |
tokenId = (event.args['tokenId'] as BigNumber).toNumber(); | |
} else { | |
return null; | |
} | |
return { | |
block: event.blockNumber, | |
tokenId, | |
send, | |
}; | |
}; | |
const toCurrentState = (events: TransferEvent[]): TokenState => { | |
const current = new Set<number>(); | |
const ordered = sortBy((e) => e.block, events); | |
ordered.forEach((event) => { | |
if (event.send) { | |
// Remove from current. | |
if (received.has(event.tokenId)) { | |
received.delete(event.tokenId); | |
} | |
} else { | |
// Add to received. | |
received.add(event.tokenId); | |
} | |
}); | |
return { | |
owned: Array.from(current), | |
}; | |
}; | |
function useTokens(account: string): TokenState | null { | |
const provider = useProvider(); | |
// Refresh when a new transfer is detected. | |
useContractEvent(nftConfig, 'Transfer', () => onRefresh()); | |
const [fetchedSend, setFetchedSend] = useState(false); | |
const [sendEvents, setSendEvents] = useState<TransferEvent[]>([]); | |
const [fetchedReceive, setFetchedReceive] = useState(false); | |
const [receiveEvents, setReceiveEvents] = useState<TransferEvent[]>([]); | |
const queryFromTransfers = useCallback( | |
async (provider: ethers.providers.Provider) => { | |
const contract = new ethers.Contract(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_ABI, provider); | |
const filter = contract.filters.Transfer(account); | |
const events = await contract.queryFilter(filter); | |
const parsed = events.map((e) => toEvent(e, true)).filter((e) => e !== null) as TransferEvent[]; | |
setSendEvents(parsed); | |
setFetchedSend(true); | |
}, | |
[account] | |
); | |
const queryToTransfers = useCallback( | |
async (provider: ethers.providers.Provider) => { | |
const contract = new ethers.Contract(NFT_CONTRACT_ADDRESS, NFT_CONTRACT_ABI, provider); | |
const filter = contract.filters.Transfer(null, account); | |
const events = await contract.queryFilter(filter); | |
const parsed = events.map((e) => toEvent(e, false)).filter((e) => e !== null) as TransferEvent[]; | |
setReceiveEvents(parsed); | |
setFetchedReceive(true); | |
}, | |
[account] | |
); | |
const onRefresh = useCallback(() => { | |
if (provider) { | |
queryFromTransfers(provider); | |
queryToTransfers(provider); | |
} | |
}, [provider, queryFromTransfers, queryToTransfers]); | |
useEffect(() => { | |
if (provider) { | |
queryFromTransfers(provider); | |
queryToTransfers(provider); | |
} | |
}, [provider, queryFromTransfers, queryToTransfers]); | |
const state = useMemo(() => { | |
return toCurrentState([...sendEvents, ...receiveEvents]); | |
}, [sendEvents, receiveEvents]); | |
// console.log('current state', state); | |
if (fetchedSend && fetchedReceive) { | |
return state; | |
} else { | |
return null; | |
} | |
} | |
export default useTokens; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment