UNPKG

@foreverrbum/ethsign

Version:

This package will allow you to electronically sign documents within your application

314 lines (288 loc) 11.9 kB
import React, { useEffect, useState } from 'react'; import { withRouter } from 'react-router-dom' import { ethers } from 'ethers'; import { useIntl } from 'react-intl'; import VerificationForm from './VerificationForm'; import VerificationIcon from '../assets/verification.svg'; import { loadContractDetails, validateSignature } from '../helpers/graphql'; import VerificationDetails from './VerificationDetails'; import { getLoggedOutWeb3Credentials, storeNotif } from '../helpers/dashboard'; import History from './History'; const VerifyContract = (props) => { const { } = props; const [showContractVerification, handleShowContractVerification] = useState(false); const [showContractHistory, handleShowContractHistory] = useState(false); const [loading, handleLoading] = useState(false); const [gqlResult, handleGqlResult] = useState(null); const [docKey, handleDocKey] = useState(null); const [signersAddress, handleSignersAddress] = useState(null); const [networkId, handleNetworkId] = useState(1); const [verifyData, handleVerifyData] = useState(null); const [ensEnabled, handleEnsEnabled] = useState(false); const [historySave, handleHistorySave] = useState(null); const [historySavedCards, handleHistorySavedCards] = useState(null); const [previousHistoryDocKey, handlePreviousHistoryDocKey] = useState(null); const [provider, handleProvider] = useState(null); const [contract, handleContract] = useState(null); // 0 = Verification Form // 1 = Verification Details // 2 = Document History - unreachable at the moment const [lastMenu, handleLastMenu] = useState(0); const { formatMessage } = useIntl(); // NOTE: I'm keeping this here for reference if we decide to re-implement it in the future. // useEffect(() => { // // Detect the current network and update the network dropdown menu // if (typeof window !== "undefined" && typeof window.ethereum !== "undefined" ) { // if (window.ethereum.isMetaMask || window.ethereum.isTorus || window.ethereum.isImToken){ // window.ethereum.request({ method: 'eth_requestAccounts' }).then(async()=>{ // const provider = new ethers.providers.Web3Provider(window.ethereum); // const network = await provider.getNetwork(); // handleNetworkId(network.chainId); // }); // } // } // }, []); useEffect(() => { if(networkId == 1 || networkId == 3) { handleEnsEnabled(true); } else { handleEnsEnabled(false); } }, [networkId]); const goBack = () => { switch(lastMenu) { case 0: // Verification Form handleShowContractVerification(false); handleShowContractHistory(false); break; case 1: // Verification Details handleLastMenu(0); handleShowContractVerification(true); handleShowContractHistory(false); break; case 2: // Document History // This is the final step, and is unreachable. break; } } const verifySignature = async (chainId, docKey, signerAddress) => { handleLoading(true); handleDocKey(docKey); handleSignersAddress(signerAddress); handleNetworkId(chainId); await getLoggedOutWeb3Credentials(chainId).then(async (result) => { handleProvider(result.provider); handleContract(result.contract); if(chainId == 1 || chainId == 3) { // ENS enabled await result.provider.resolveName(signerAddress).then(async (address) => { if(!address) { storeNotif(formatMessage({id: 'SIGNATURE_INVALID'}), formatMessage({id: 'ENTERED_SIGNERS_ADDRESS_NO_MATCH'}), "warning"); handleGqlResult(null); } else { address = address.toLowerCase(); if(ethers.utils.isAddress(address)) { let result = await validateSignature(chainId, docKey, address); if(result?.status > 0) { handleGqlResult(result); handleLastMenu(0); handleShowContractVerification(true); // We may want to provide users with an option to enter another address rather than just clearing the data // handleDocKey(null); // handleSignersAddress(null); // handleNetworkId(1); } else { storeNotif(formatMessage({id: 'SIGNATURE_INVALID'}), formatMessage({id: 'ENTERED_SIGNERS_ADDRESS_NO_MATCH'}), "warning"); handleGqlResult(null); } } else { storeNotif(formatMessage({id: 'ERROR'}), formatMessage({id: 'THE_ADDRESS_YOU_ENTER_IS_INVALID'}),"warning"); } } }) } else { // ENS disabled let result = await validateSignature(chainId, docKey, signerAddress); if(result?.status > 0) { handleGqlResult(result); handleLastMenu(0); handleShowContractVerification(true); // We may want to provide users with an option to enter another address rather than just clearing the data // handleDocKey(null); // handleSignersAddress(null); // handleNetworkId(1); } else { storeNotif(formatMessage({id: 'SIGNATURE_INVALID'}), formatMessage({id: 'ENTERED_SIGNERS_ADDRESS_NO_MATCH'}), "warning"); handleGqlResult(null); } } }); handleLoading(false); } const loadContractHistory = async (chainId, signerAddress, docKey, lastMenu) => { if(historySave && historySavedCards && previousHistoryDocKey == docKey) { handleLastMenu(lastMenu); handleSignersAddress(signerAddress); handleShowContractHistory(true); handleShowContractVerification(false); handleLoading(false); return; } else { if(historySave) { handleHistorySave(null); } if(historySavedCards) { handleHistorySavedCards(null); } } handleDocKey(docKey); handleSignersAddress(signerAddress); handleNetworkId(chainId); // Show the loading animation until the document history data can be loaded and sent to the component if(!(docKey?.length > 0)) { storeNotif(formatMessage({id: 'INVALID_DOCUMENT_KEY'}), formatMessage({id: 'PLEASE_ENTER_DOC_KEY'}), "danger"); return; } handleLoading(true); let doc = null; await getLoggedOutWeb3Credentials(chainId).then(async (result) => { handleProvider(result.provider); handleContract(result.contract); doc = await loadContractDetails(chainId, docKey, result.provider, null); if(doc == null) { storeNotif(formatMessage({id: 'INVALID_DOCUMENT_KEY'}), formatMessage({id: 'DOC_KEY_NETWORK_SELECTION_INVALID'}), "warning"); handleGqlResult(null); handleShowContractHistory(false); handleLoading(false); return; } // Try to grab the alias and avatar of each of the signers if(ensEnabled) { try { for(let signer of doc.signers) { let alias = await result.provider.lookupAddress(signer.address); if(alias) { signer.alias = alias; } const resolver = await result.provider.getResolver(alias); if(resolver) { signer.avatar = await resolver.getText("avatar"); } } } catch(err) { console.log(err) } } }).catch(e => {console.log(e);}); // Doc is invalid, do not process it if(doc == null) { return; } const initiatorIndex = _.findIndex(doc.signers, function(o) { return o.address == doc.initiator }); let initiator = null; if(initiatorIndex>-1) { initiator = doc.signers[initiatorIndex]; } else { initiator = { address: doc.initiator, avatar: null, alias: null }; } let verifyData = { doc: doc, networkId: chainId, initiator: initiator, } handleVerifyData(verifyData); // Once everything has been loaded, we will handleShowContractHistory(true); handlePreviousHistoryDocKey(docKey); handleLastMenu(lastMenu); handleShowContractHistory(true); handleShowContractVerification(false); handleLoading(false); } if(showContractVerification) { return ( <div className="bg-gray-400"> <div className="flex flex-col-reverse sm:flex-row section-2"> <div className="w-full sm:w-1/2 py-8 px-8 sm:px-0 sm:py-8 sm:pl-8 md:py-12 md:pl-12 lg:py-24 lg:pl-24"> <h1 className="heading"> {formatMessage({id: 'DIGITAL_SIGNATURE_VALIDATION'})} </h1> <p>{formatMessage({id: 'VALIDATE_AND_AUDIT'})}</p> </div> <div className="flex ml-auto sm:w-1/2"> <img src={VerificationIcon} className="select-none w-full max-w-xs sm:max-w-md ml-auto" /> </div> </div> <div className="px-4 sm:px-8 md:px-12 lg:px-24 pb-4 sm:pb-8 md:pb-12 lg:pb-24"> <VerificationDetails loading={loading} gqlResult={gqlResult} loadContractHistory={loadContractHistory} goBack={goBack} /> </div> </div> ); } if(showContractHistory) { return ( <div className="bg-gray-400"> <div className="flex flex-col-reverse sm:flex-row section-2"> <div className="w-full sm:w-1/2 py-8 px-8 sm:px-0 sm:py-8 sm:pl-8 md:py-12 md:pl-12 lg:py-24 lg:pl-24"> <h1 className="heading"> {formatMessage({id: 'DIGITAL_SIGNATURE_VALIDATION'})} </h1> <p>{formatMessage({id: 'VALIDATE_AND_AUDIT'})}</p> </div> <div className="flex ml-auto sm:w-1/2"> <img src={VerificationIcon} className="select-none w-full max-w-xs sm:max-w-md ml-auto" /> </div> </div> <div className="px-4 sm:px-8 md:px-12 lg:px-24 pb-4 sm:pb-8 md:pb-12 lg:pb-24"> <div className="flex"> <div className="m-auto w-full flex justify-center"> <div onClick={(event) => event.stopPropagation()} className="w-full bg-gray-45 sm:border-t-4 border-t-8 border-orange-500 sm:rounded-none rounded-lg shadow-md sm:shadow-none"> <History fromVerify={true} verifyData={verifyData} web3={provider} contract={contract} ensEnabled={ensEnabled} goBack={goBack} shouldSave={true} fromSave={historySave !== null} save={historySave} handleSave={handleHistorySave} savedCards={historySavedCards} handleSavedCards={handleHistorySavedCards} /> </div> </div> </div> </div> </div> ); } return ( <div className="bg-gray-400"> <div className="flex flex-col-reverse sm:flex-row section-2"> <div className="w-full sm:w-1/2 py-8 px-8 sm:px-0 sm:py-8 sm:pl-8 md:py-12 md:pl-12 lg:py-24 lg:pl-24"> <h1 className="heading"> {formatMessage({id: 'DIGITAL_SIGNATURE_VALIDATION'})} </h1> <p>{formatMessage({id: 'VALIDATE_AND_AUDIT'})}</p> </div> <div className="flex ml-auto sm:w-1/2" onDragStart={(e) => e.preventDefault()}> <img src={VerificationIcon} className="select-none w-full max-w-xs sm:max-w-md ml-auto" /> </div> </div> <div className="px-4 sm:px-8 md:px-12 lg:px-24 pb-4 sm:pb-8 md:pb-12 lg:pb-24"> <VerificationForm loading={loading} verifySignature={verifySignature} loadContractHistory={loadContractHistory} startingDocKey={docKey} startingSignersAddress={signersAddress} startingNetworkId={networkId} /> </div> </div> ); } export default withRouter(VerifyContract);