Skip to content

Instantly share code, notes, and snippets.

@bswags
Created September 2, 2022 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bswags/ccbc409cda18abf139e575878e37e7e3 to your computer and use it in GitHub Desktop.
Save bswags/ccbc409cda18abf139e575878e37e7e3 to your computer and use it in GitHub Desktop.
Query transfers for an ERC721 to materialize currently owned ones
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