@foreverrbum/ethsign
Version:
This package will allow you to electronically sign documents within your application
314 lines (288 loc) • 11.9 kB
JavaScript
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);