@foreverrbum/ethsign
Version:
This package will allow you to electronically sign documents within your application
229 lines (207 loc) • 10.1 kB
JavaScript
import { ethers } from 'ethers';
import { storeNotif } from './dashboard';
import {setDocumentCommentsForSigner, getEncryptedStringFromFile, docUpload, aggregateNewBasicDocumentAndSetStorage} from './newdocument';
const emptyHex = '0x0000000000000000000000000000000000000000000000000000000000000000';
export const createAndSign = async (provider, ethAccount, instance, signaturesToSave, formatMessage, contract, handleSaveStatus, password, docName, initialSigners, bar, handleProgress, user, networkId, callback, errorCallback, handleSavedAnnotations, handleNewChanges) => {
// 1-docUpload
// 2-setDocStorage
// 3-addSignersAndNumofSigFields
// 4-signMessage
// 5-saveSigsAndSaveComment
const {docViewer} = instance;
let key, storageHash;
const expiration = 0;
let storageProvider = "FL"
const doc = docViewer.getDocument();
const fileData = await doc.getFileData();
const buffer = new Uint8Array(fileData);
const blob = new Blob([buffer], { type: 'application/pdf' });
const encryptedFile = await getEncryptedStringFromFile(blob, password)
try{
const {documentKey, hash, storage_provider} = await docUpload(formatMessage, encryptedFile, contract, handleSaveStatus, storageProvider)
key = documentKey
storageHash = hash
storageProvider = storage_provider
}catch(err){
console.log(err)
storeNotif(formatMessage({id: 'FILE_UPLOAD_ERROR'}),formatMessage({id: 'FILE_UPLOAD_ERROR_MESSAGE'}, {button: bar.button2} ), 'danger')
handleSaveStatus(null)
if(errorCallback){
errorCallback();
}
return;
}
handleProgress(1)
const docuStorageHash = [storageHash, emptyHex, emptyHex, emptyHex, emptyHex, emptyHex]
const {annotManager} = instance;
const x = annotManager.getAnnotationsList().filter(a=> a.Subject == "Widget" && a.Signer != null).length
const signers = [ethAccount, ...initialSigners]
const signerFieldCount = [x]
initialSigners.map(a=>{
signerFieldCount.push(1)
})
const created = await aggregateNewBasicDocumentAndSetStorage (provider, contract, key, docName, expiration, storageProvider, docuStorageHash, signers, signerFieldCount, handleSaveStatus, formatMessage,
async (tx)=>{
const networkId = (await provider.getNetwork()).chainId;
await tx.wait(networkId == 1287 ? 2 : 1).then((receipt) => {
handleProgress(2);
});
},
()=>{
handleSaveStatus(null);
if(errorCallback){
errorCallback();
}
},
bar.button2
)
if(created){
const doc = {
documentKey: key,
storageProvider: storageProvider,
}
await signAndSaveComments(annotManager, signaturesToSave, provider, ethAccount, contract, doc, password, user, callback, errorCallback, formatMessage, handleSaveStatus, handleSavedAnnotations, handleNewChanges, 2, handleProgress)
}
}
export const signAndSaveComments = async (annotManager, signaturesToSave, provider, ethAccount, contract, doc, password, user, callback, errorCallback, formatMessage, handleSaveStatus, handleSavedAnnotations, handleNewChanges, progress, handleProgress) => {
const annotSignatures = []
// exporting and setting custom Data
const signedIndices = []
const ec_signatures = [];
const signatures = []
const signer = provider.getSigner();
const instance = contract.connect(signer);
if (signaturesToSave.length>0){
handleSaveStatus(formatMessage({id: 'REQUESTING_SIGNATURES'}))
try{
// get signature
await Promise.all(
signaturesToSave.map(async (annot) => {
annot.annot.setCustomData('Signer', annot.Signer)
annot.annot.setCustomData('Index', annot.Index)
signedIndices.push(annot.Index)
annotSignatures.push(annot.annot)
// const hash = await contract.hashDocumentKey(doc.ipfsHash + doc.metaHash + `${annot.Index}`);
const hash = await instance.hashSaltedAddressWithIndexMappingKeyAsSigner(doc.documentKey, annot.Index);
const hashArray = ethers.utils.arrayify(hash);
const ec_signature = await signer.signMessage(hashArray);
ec_signatures.push(ec_signature);
signatures.push(annot.annot)
})
);
handleProgress(progress+1);
}catch(err){
signaturesToSave.map(async (annot) => {
annot.annot.setCustomData('Signer', null)
annot.annot.setCustomData('Index', null)
})
console.log(err)
handleSaveStatus(null)
if(errorCallback){
errorCallback();
}
storeNotif(formatMessage({id: 'TRANSACTION_ERROR'}), formatMessage({id: 'YOU_REJECTED_TRANSACTION'}), "danger")
return;
}
}
handleSaveStatus(formatMessage({id: 'ENCRYPTING_ANNOTATIONS'}))
// deleting those Ethsign Signature Fields (orange) widgets that are not signed
const annotsToDelete = annotManager.getAnnotationsList().filter((annot) => (annot.Subject == "SignatureField" || annot.Subject == "Widget") && annot.Signer != null && annot.annot == null)
await annotManager.deleteAnnotations(annotsToDelete, null, true);
const xfdfStringAnnotations = await annotManager.exportAnnotations();
const commentsToSave = [...annotManager.getAnnotationsList()]
await annotManager.addAnnotations(annotsToDelete);
await annotManager.drawAnnotationsFromList(annotsToDelete);
var annotationsFile = new Blob([xfdfStringAnnotations], {type: 'text/plain'});
const encryptedAnnotations = await getEncryptedStringFromFile(annotationsFile, password)
if (await setDocumentCommentsForSigner(provider, contract, ethAccount, doc.storageProvider, doc.documentKey, signedIndices, ec_signatures, encryptedAnnotations, formatMessage, callback, errorCallback, handleSaveStatus, signaturesToSave.length)){
handleProgress(progress + 2);
signatures.map(async (annot) => {
annot.ReadOnly = true;
})
handleSavedAnnotations(commentsToSave)
var newChanges = annotManager.getAnnotationsList().filter((annot) => annot.Subject != "SignatureField" && annot.Subject != "Widget" && annot.Author == user)
_.pullAll(newChanges, commentsToSave);
handleNewChanges(newChanges.length > 0)
}else{
signatures.map(async (annot) => {
annot.setCustomData('Signer', null)
annot.setCustomData('Index', null)
})
}
return;
}
export const makeSignaturesReadOnly = (signatures) => {
signatures.map(annot => {
annot.ReadOnly = true;
})
}
export const aggregateSetSigFieldAsSignerForDocument = async (ethAccount, contract, provider, documentKey, indices, r, s, v, handleSaveStatus, commentsLength, formatMessage) => {
try {
const signer = provider.getSigner();
const instance = await contract.connect(signer);
const saltedAddressMappingKey = await contract.hashSaltedAddressMappingKey(documentKey, ethAccount);
const saltedMetaDocumentMappingKey = await contract.hashSaltedMetaDocumentMappingKey(documentKey);
// Sign it
let tx = await instance.aggregateSetSigFieldAsSignerForDocument(documentKey, indices, saltedAddressMappingKey, saltedMetaDocumentMappingKey, r, s, v);
handleSaveStatus(formatMessage({id: 'WAITING_FOR_CONFIRMATIONS_FROM_NEWWORK'}))
const networkId = (await provider.getNetwork()).chainId;
await tx.wait(networkId == 1287 ? 2 : 1).then((receipt) => {
console.log(receipt)
if(commentsLength==0){
handleSaveStatus(null)
storeNotif(formatMessage({id: 'SIGNATURE_APPLIED'}), formatMessage({id: 'SUCCESSFULLY_SIGNED_CONTRACT'}), 'success')
}
})
} catch (err) {
console.log(err)
return false;
}
return true;
}
// Signing a particular field in a specific version of the document
export const signDocumentAtIndex = async (contract, provider, documentKey, doc_storage_id, metadata_storage_id, index, handleSubmitButton, formatMessage) => {
try {
const signer = provider.getSigner();
const instance = await contract.connect(signer);
const ethAccount = await signer.getAddress();
// Sign it
// const hash = await instance.hashDocumentKey(doc_storage_id + metadata_storage_id + `${index}`);
const hash = await instance.hashSaltedAddressWithMappingKeyAsSigner(documentKey, index);
const hashArray = ethers.utils.arrayify(hash);
const ec_signature = await signer.signMessage(hashArray);
let tx = await instance.aggregateSetSigFieldForDocument(documentKey, ethAccount, [index], ec_signature);
if(handleSubmitButton) {
handleSubmitButton(formatMessage({id: 'WAITING_FOR_CONFIRMATIONS_FROM_NEWWORK'}));
}
const networkId = (await provider.getNetwork()).chainId;
await tx.wait(networkId == 1287 ? 2 : 1).then((receipt) => {
console.log(receipt)
storeNotif(formatMessage({id: 'SIGNATURE_APPLIED'}), formatMessage({id: 'SUCCESSFULLY_SIGNED_CONTRACT'}), 'success')
})
} catch (err) {
console.log(err)
return false;
}
return true;
}
export const getDocumentSignatureAtIndex = async (contract, doc, index, signerAddress) => {
let legit;
try {
const signatureArray = await contract.getDocumentECSignatureForSignerAtIndex(signerAddress, doc.documentKey, index);
let ec_signature = ethers.utils.joinSignature({ "r": signatureArray.r, "s": signatureArray.s, "v": signatureArray.v });
const hash = await contract.hashDocumentKey(doc.ipfsHash + doc.metaHash + `${index}`);
const hashArray = ethers.utils.arrayify(hash);
legit = ethers.utils.verifyMessage(hashArray, ec_signature)
} catch (err) {
console.log(err);
return false;
}
// Compare, ignoring lowercase/uppercase
if (legit.localeCompare(signerAddress, undefined, { sensitivity: 'accent' }) === 0) {
return true;
} else {
return false;
}
}
export default { getDocumentSignatureAtIndex, signDocumentAtIndex, signAndSaveComments, makeSignaturesReadOnly }