import { promisify } from 'util' ; import debug from './utils/debug' ; import { makeNonEnumerable } from './utils/make-non-enumerable' ; import { EthereumProvider , getTransactionByHash , getTransactionReceipt , hasCode , isDevelopmentNetwork , isReceiptSuccessful , } from './provider' ; const sleep = promisify ( setTimeout ) ; export interface Deployment { address : string ; txHash ? : string ; } export async function resumeOrDeploy < T extends Deployment > ( provider , cached , deploy ) { if ( cached !== undefined ) { const { txHash } = cached ; if ( txHash === undefined ) { return cached ; } debug ( 'found previous deployment' , txHash ) ; const tx = await getTransactionByHash ( provider , txHash ) ; if ( tx !== null ) { debug ( 'resuming previous deployment' , txHash ) ; return cached ; } else if ( ! ( await isDevelopmentNetwork ( provider ) ) ) { throw new InvalidDeployment ( cached ) ; } else { debug ( 'ignoring invalid deployment in development network' , txHash ) ; } } const deployment = await deploy ( ) ; debug ( 'initiated deployment' , deployment . txHash ) ; return deployment ; } export async function waitAndValidateDeployment ( provider , deployment ) { const { txHash , address } = deployment ; const pollTimeout = 60e3 ; const pollInterval = 5e3 ; if ( txHash !== undefined ) { const startTime = Date . now ( ) ; while ( true ) { const elapsedTime = Date . now ( ) - startTime ; if ( elapsedTime >= pollTimeout ) { throw new TransactionMinedTimeout ( deployment ) ; } debug ( 'verifying deployment tx mined' , txHash ) ; const receipt = await getTransactionReceipt ( provider , txHash ) ; if ( receipt && isReceiptSuccessful ( receipt ) ) { debug ( 'succeeded verifying deployment tx mined' , txHash ) ; break ; } else if ( receipt ) { debug ( 'tx was reverted' , txHash ) ; throw new InvalidDeployment ( deployment ) ; } else { debug ( 'waiting for deployment tx mined' , txHash ) ; await sleep ( pollInterval ) ; } } } debug ( 'verifying code in target address' , address ) ; const startTime = Date . now ( ) ; while ( ! ( await hasCode ( provider , address ) ) ) { const elapsedTime = Date . now ( ) - startTime ; if ( elapsedTime >= pollTimeout || txHash === undefined ) { throw new InvalidDeployment ( deployment ) ; } await sleep ( pollInterval ) ; } debug ( 'code in target address found' , address ) ; } export class TransactionMinedTimeout extends Error { constructor ( readonly deployment ) { super ( `Timed out waiting for transaction ${ deployment . txHash }` ) ; } } export class InvalidDeployment extends Error { removed = false ; constructor ( readonly deployment ) { super ( ) ; makeNonEnumerable ( this , 'removed' ) ; makeNonEnumerable ( this , 'deployment' ) ; } get message ( ) : string { let msg = `No contract at address ${ this . deployment . address }` ; if ( this . removed ) { msg += ' (Removed from manifest)' ; } return msg ; } }