diff --git a/packages/hyperverse-evm-whitelist/source/environment.ts b/packages/hyperverse-evm-whitelist/source/environment.ts index 06f1a426..927e7a53 100644 --- a/packages/hyperverse-evm-whitelist/source/environment.ts +++ b/packages/hyperverse-evm-whitelist/source/environment.ts @@ -2,8 +2,8 @@ import { isEvm, Blockchain, BlockchainEvm, - useHyperverse, EvmEnvironment, + NetworkConfig, } from '@decentology/hyperverse'; import Whitelist from '../artifacts/contracts/Whitelist.sol/Whitelist.json'; import WhitelistFactory from '../artifacts/contracts/WhitelistFactory.sol/WhitelistFactory.json'; diff --git a/packages/hyperverse-evm-whitelist/source/index.ts b/packages/hyperverse-evm-whitelist/source/index.ts index 2468e8e3..3c8fcd9d 100644 --- a/packages/hyperverse-evm-whitelist/source/index.ts +++ b/packages/hyperverse-evm-whitelist/source/index.ts @@ -1,2 +1,4 @@ export { useWhitelist } from './useWhitelist'; export { Provider } from './Provider'; +export const ModuleName = 'Whitelist'; +export { WhitelistLibrary } from './library/WhitelistLibrary'; \ No newline at end of file diff --git a/packages/hyperverse-evm-whitelist/source/library/WhitelistLibrary.ts b/packages/hyperverse-evm-whitelist/source/library/WhitelistLibrary.ts index aae29273..d1979599 100644 --- a/packages/hyperverse-evm-whitelist/source/library/WhitelistLibrary.ts +++ b/packages/hyperverse-evm-whitelist/source/library/WhitelistLibrary.ts @@ -1,30 +1,35 @@ -import { HyperverseConfig } from '@decentology/hyperverse' -import { BaseLibrary, getProvider } from '@decentology/hyperverse-evm' -import { ethers } from 'ethers' -import { getEnvironment } from '../environment' -import { MetaData } from '../types' +import { HyperverseConfig } from '@decentology/hyperverse'; +import { BaseLibrary, getProvider } from '@decentology/hyperverse-evm'; +import { ethers } from 'ethers'; +import { getEnvironment } from '../environment'; +import { MerkleTree } from 'merkletreejs'; +import keccak256 from 'keccak256'; export type WhitelistLibraryType = Awaited>; -export async function WhitelistLibrary( - hyperverse: HyperverseConfig, - providerOrSigner?: ethers.providers.Provider | ethers.Signer -){ - - const {FactoryABI, factoryAddress, ContractABI} = getEnvironment( - hyperverse.blockchain?.name!, - hyperverse.network - ); - - if (!providerOrSigner) { - providerOrSigner = getProvider(hyperverse.network); - } +export async function WhitelistLibrary( + hyperverse: HyperverseConfig, + providerOrSigner?: ethers.providers.Provider | ethers.Signer +) { + const { FactoryABI, factoryAddress, ContractABI } = getEnvironment( + hyperverse.blockchain?.name!, + hyperverse.network + ); - const base = await BaseLibrary(hyperverse, factoryAddress!, FactoryABI, ContractABI, providerOrSigner); + if (!providerOrSigner) { + providerOrSigner = getProvider(hyperverse.network); + } + const base = await BaseLibrary( + hyperverse, + factoryAddress!, + FactoryABI, + ContractABI, + providerOrSigner + ); - const errors = (err: any) => { - if (!base.factoryContract?.signer) { + const errors = (err: any) => { + if (!base.factoryContract?.signer) { throw new Error('Please connect your wallet!'); } @@ -33,11 +38,138 @@ export async function WhitelistLibrary( } throw err; - } + }; + + return { + ...base, + getTotalTenants: async () => { + try { + const tenantCount = await base.factoryContract.tenantCounter(); + return tenantCount.toNumber(); + } catch (err) { + throw err; + } + }, + getWhitelistedAddresses: async () => { + try { + const addresses = await base.proxyContract?.whitelistedAddresses(); + return addresses; + } catch (error) { + throw error; + } + }, + getAddressesClaimed: async () => { + try { + const addresses = await base.proxyContract?.addressesClaimed(); + return addresses; + } catch (error) { + throw error; + } + }, + isWhitelisted: async (account: string) => { + try { + const check = await base.proxyContract?.whitelistedAddresses(account); + return check; + } catch (error) { + throw error; + } + }, + isWhitelistedMerkle: async (account: string) => { + try { + const addresses = await base.proxyContract?.whitelistedAddresses(); + const leafNodes = addresses.map((address: string) => keccak256(address)); + const tree = new MerkleTree(leafNodes, keccak256, { sortPairs: true }); + + const check = await base.proxyContract?.checkMerkleWhitelist( + account, + tree.getHexProof(keccak256(account)) + ); + return check; + } catch (error) { + throw error; + } + }, + hasClaimed: async (address: string) => { + try { + const check = await base.proxyContract?.addressesClaimed(address); + return check; + } catch (error) { + throw error; + } + }, + active: async () => { + try { + const active = await base.proxyContract?.active(); + return active; + } catch (error) { + throw error; + } + }, + whitelist: async () => { + try { + const txn = await base.proxyContract?.getWhitelisted(); + return txn; + } catch (error) { + throw error; + } + }, + claimWhitelist: async (account: string) => { + try { + const addresses = await base.proxyContract?.whitelistedAddresses(); + const leafNodes = addresses.map((address: string) => keccak256(address)); + const tree = new MerkleTree(leafNodes, keccak256, { sortPairs: true }); + const txn = await base.proxyContract?.claimWhitelist( + account, + tree.getHexProof(keccak256(account)) + ); + return txn; + } catch (error) { + throw error; + } + }, - return { - ...base, - } + //Admin Functionality + setMerkleRoot: async (root: string) => { + try { + const txn = await base.proxyContract?.updateMerkleRoot(root); + return txn; + } catch (error) { + throw error; + } + }, -} \ No newline at end of file + activateClaiming: async () => { + try { + const txn = await base.proxyContract?.activateWhitelistClaiming(); + return txn; + } catch (error) { + throw error; + } + }, + deactivateClaiming: async () => { + try { + const txn = await base.proxyContract?.deactivateWhitelistClaiming(); + return txn; + } catch (error) { + throw error; + } + }, + authorizeOperator: async (operator: string) => { + try { + const txn = await base.proxyContract?.authorizeOperator(operator); + return txn; + } catch (error) { + throw error; + } + }, + revokeOperator: async (operator: string) => { + try { + const txn = await base.proxyContract?.revokeOperator(operator); + return txn; + } catch (error) { + throw error; + } + }, + }; +} diff --git a/packages/hyperverse-evm-whitelist/source/useWhitelist.ts b/packages/hyperverse-evm-whitelist/source/useWhitelist.ts index 891cddff..e4b66699 100644 --- a/packages/hyperverse-evm-whitelist/source/useWhitelist.ts +++ b/packages/hyperverse-evm-whitelist/source/useWhitelist.ts @@ -1,141 +1,72 @@ -import { useState, useEffect, useCallback, useMemo } from 'react'; -import { useQuery, useMutation, useQueryClient, UseMutationOptions } from 'react-query'; -import { ethers, constants } from 'ethers'; +import { useState, useEffect, useCallback } from 'react'; +import { useQuery, useMutation, UseMutationOptions } from 'react-query'; import { useEvent } from 'react-use'; +import { useHyperverse } from '@decentology/hyperverse'; import { createContainer, useContainer } from '@decentology/unstated-next'; import { useEvm } from '@decentology/hyperverse-evm'; -import { useEnvironment } from './environment'; - -type ContractState = ethers.Contract; - - +import { WhitelistLibrary, WhitelistLibraryType } from './library/WhitelistLibrary'; function WhitelistState(initialState: { tenantId: string } = { tenantId: '' }) { const { tenantId } = initialState; - const queryClient = useQueryClient(); - const { address, web3Provider, provider } = useEvm(); - const { ContractABI, FactoryABI, factoryAddress } = useEnvironment(); - const [factoryContract, setFactoryContract] = useState( - new ethers.Contract(factoryAddress!, FactoryABI, provider) as ContractState - ); - - const [proxyContract, setProxyContract] = useState(); - - const signer = useMemo(async () => { - return web3Provider?.getSigner(); - }, [web3Provider]); + const { address, connectedProvider, readOnlyProvider } = useEvm(); + const hyperverse = useHyperverse(); + const [whitelistLibrary, setWhitelistLibrary] = useState(); useEffect(() => { - const fetchContract = async () => { - const proxyAddress = await factoryContract.getProxy(tenantId); - if (proxyAddress == constants.AddressZero) { - return; - } - const proxyCtr = new ethers.Contract(proxyAddress, ContractABI, provider); - const accountSigner = await signer; - if (accountSigner) { - setProxyContract(proxyCtr.connect(accountSigner)); - } else { - setProxyContract(proxyCtr); + let canel = false; + WhitelistLibrary(hyperverse, connectedProvider || readOnlyProvider).then((result) => { + if (!canel) { + setWhitelistLibrary(result); } + }); + return () => { + canel = true; }; - fetchContract(); - }, [factoryContract, tenantId, provider, signer]); - - const setup = useCallback(async () => { - const accountSigner = await signer; - if (accountSigner) { - const ctr = factoryContract.connect(accountSigner) as ContractState; - setFactoryContract(ctr); - } - // We have a defualt contract that has no signer. Which will work for read-only operations. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [signer]); - - - const factoryErrors = useCallback( - (err: any) => { - if (!factoryContract?.signer) { - throw new Error('Please connect your wallet!'); - } - - if (err.code === 4001) { - throw new Error('You rejected the transaction!'); - } - - throw err; - }, - [factoryContract?.signer] - ); - - useEffect(() => { - if (web3Provider) { - setup(); - } - }, [web3Provider]); - - const checkInstance = async (account: any) => { - try { - const instance = await factoryContract.instance(account); - return instance; - } catch (err) { - factoryErrors(err); - throw err; - } - }; - - const createInstance = useCallback( - async (account: string) => { - try { - const createTxn = await factoryContract.createInstance(account); - return createTxn.wait(); - } catch (err) { - factoryErrors(err); - throw err; - } - }, - [factoryContract?.signer] - ); - - const getTotalTenants = async () => { - try { - const tenantCount = await factoryContract.tenantCounter(); - - return tenantCount.toNumber(); - } catch (err) { - factoryErrors(err); - throw err; - } - }; + }, [connectedProvider]); + if (typeof window !== 'undefined') { + // @ts-ignore + window['whitelistLibrary'] = whitelistLibrary; + } const useWhitelistEvents = (eventName: string, callback: any) => { - return useEvent(eventName, useCallback(callback, [proxyContract]), proxyContract); + return useEvent( + eventName, + useCallback(callback, [whitelistLibrary?.proxyContract]), + whitelistLibrary?.proxyContract + ); }; return { tenantId, - factoryContract, - proxyContract, + factoryContract: whitelistLibrary?.factoryContract, + proxyContract: whitelistLibrary?.proxyContract, useWhitelistEvents, CheckInstance: () => - useQuery( - ['checkInstance', address, factoryContract?.address], - () => checkInstance(address), - { - enabled: !!address && !!factoryContract?.signer, - } - ), + useQuery(['checkInstance', address], () => whitelistLibrary?.checkInstance(address), { + enabled: !!whitelistLibrary, + }), + NewInstance: ( options?: Omit< - UseMutationOptions, + UseMutationOptions< + unknown, + unknown, + { + account: string; + startTime?: number; + endTime?: number; + units?: number; + erc721?: string; + erc20?: string; + merkleRoot?: string; + }, + unknown + >, 'mutationFn' > - ) => useMutation(({ account }) => createInstance(account), options), - TotalTenants: () => - useQuery(['totalTenants', factoryContract?.address], () => getTotalTenants(), { - enabled: !!factoryContract?.address, - }), + ) => useMutation(({ account }) => whitelistLibrary!.createInstance(account), options), + TotalTenants: () => useQuery(['totalTenants',], () => whitelistLibrary?.getTotalTenants(), { enabled: !!whitelistLibrary }), }; } diff --git a/yarn.lock b/yarn.lock index 06b3be58..f9fa94c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24528,6 +24528,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +real-cancellable-promise@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/real-cancellable-promise/-/real-cancellable-promise-1.1.1.tgz#be793502f7ed8067936d95dc040a2d2a2a1944a7" + integrity sha512-vxanUX4Aff5sRX6Rb1CSeCDWhO20L0hKQXWTLOYbtRo9WYFMjlhEBX0E75iz3+7ucrmFdPpDolwLC7L65P7hag== + receptacle@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/receptacle/-/receptacle-1.3.2.tgz#a7994c7efafc7a01d0e2041839dab6c4951360d2"