import { expect } from 'chai' ; import chaiAsPromised from 'chai-as-promised' ; import chai from 'chai' ; import { Contract , ContractFactory , ContractInterface , Signer , Wallet } from 'ethers' ; import { TransactionResponse } from '@ethersproject/abstract-provider' ; import { Provider } from '@ethersproject/providers' ; import { getStatic } from 'ethers/lib/utils' ; import { wallet } from '.' ; chai . use ( chaiAsPromised ) ; type Impersonator = Signer | Provider | string ; export const checkTxRevertedWithMessage = async ( { tx , message , } ) : Promise < void > => { await expect ( tx ) . to . be . reverted ; if ( message instanceof RegExp ) { await expect ( tx ) . eventually . rejected . have . property ( 'message' ) . match ( message ) ; } else { await expect ( tx ) . to . be . revertedWith ( message ) ; } } ; export const checkTxRevertedWithZeroAddress = async ( tx ) : Promise < void > => { await checkTxRevertedWithMessage ( { tx , message : /zero\saddress/ , } ) ; } ; export const deployShouldRevertWithZeroAddress = async ( { contract , args } ) : Promise < void > => { const deployContractTx = await contract . getDeployTransaction ( ... args ) ; const tx = contract . signer . sendTransaction ( deployContractTx ) ; await checkTxRevertedWithZeroAddress ( tx ) ; } ; export const deployShouldRevertWithMessage = async ( { contract , args , message , } ) : Promise < void > => { const deployContractTx = await contract . getDeployTransaction ( ... args ) ; const tx = contract . signer . sendTransaction ( deployContractTx ) ; await checkTxRevertedWithMessage ( { tx , message } ) ; } ; export const txShouldRevertWithZeroAddress = async ( { contract , func , args , } ) : Promise < void > => { const tx = contract [ func ] ( ... args ) ; await checkTxRevertedWithZeroAddress ( tx ) ; } ; export const txShouldRevertWithMessage = async ( { contract , func , args , message , } ) : Promise < void > => { const tx = contract [ func ] ( ... args ) ; await checkTxRevertedWithMessage ( { tx , message } ) ; } ; export const checkTxEmittedEvents = async ( { contract , tx , events , } ) : Promise < void > => { for ( let i = 0 ; i < events . length ; i ++ ) { await expect ( tx ) . to . emit ( contract , events [ i ] . name ) . withArgs ( ... events [ i ] . args ) ; } } ; export const deployShouldSetVariablesAndEmitEvents = async ( { contract , args , settersGettersVariablesAndEvents , } ) : Promise < void > => { const deployContractTx = await contract . getDeployTransaction ( ... args ) ; const tx = await contract . signer . sendTransaction ( deployContractTx ) ; const address = getStatic < ( tx : TransactionResponse ) => string > ( contract . constructor , 'getContractAddress' ) ( tx ) ; const deployedContract = getStatic < ( address : string , contractInterface : ContractInterface , signer ? : Signer ) => Contract > ( contract . constructor , 'getContract' ) ( address , contract . interface , contract . signer ) ; await txShouldHaveSetVariablesAndEmitEvents ( { contract : deployedContract , tx , settersGettersVariablesAndEvents , } ) ; } ; export const txShouldHaveSetVariablesAndEmitEvents = async ( { contract , tx , settersGettersVariablesAndEvents , } ) : Promise < void > => { for ( let i = 0 ; i < settersGettersVariablesAndEvents . length ; i ++ ) { await checkTxEmittedEvents ( { contract , tx , events : [ { name : settersGettersVariablesAndEvents [ i ] . eventEmitted , args : [ settersGettersVariablesAndEvents [ i ] . variable ] , } , ] , } ) ; expect ( await contract [ settersGettersVariablesAndEvents [ i ] . getterFunc ] ( ) ) . to . eq ( settersGettersVariablesAndEvents [ i ] . variable ) ; } } ; export const txShouldSetVariableAndEmitEvent = async ( { contract , setterFunc , getterFunc , variable , eventEmitted , } ) : Promise < void > => { expect ( await contract [ getterFunc ] ( ) ) . to . not . eq ( variable ) ; const tx = contract [ setterFunc ] ( variable ) ; await txShouldHaveSetVariablesAndEmitEvents ( { contract , tx , settersGettersVariablesAndEvents : [ { getterFunc , variable , eventEmitted , } , ] , } ) ; } ; export const fnShouldOnlyBeCallableByGovernance = ( delayedContract , fnName , governance , args ) : void => { it ( 'should be callable by governance' , ( ) => { return expect ( callFunction ( governance ) ) . not . to . be . revertedWith ( 'OnlyGovernance()' ) ; } ) ; it ( 'should not be callable by any address' , async ( ) => { return expect ( callFunction ( await wallet . generateRandom ( ) ) ) . to . be . revertedWith ( 'OnlyGovernance()' ) ; } ) ; function callFunction ( impersonator ) { const argsArray = typeof args === 'function' ? args ( ) : args ; const fn = delayedContract ( ) . connect ( impersonator ) [ fnName ] as ( ... args : unknown [ ] ) => unknown ; return fn ( ... argsArray , { gasPrice : 0 } ) ; } } ;