import VM from '@nomiclabs/ethereumjs-vm' ; import { FactoryOptions } from '@nomiclabs/hardhat-ethers/types' ; import { BaseContract , ContractFactory , ethers } from 'ethers' ; import hre from 'hardhat' ; import { ethersInterfaceFromSpec } from './factories/ethers-interface' ; import { createFakeContract , createMockContractFactory } from './factories/smock-contract' ; import { ObservableVM } from './observable-vm' ; import { FakeContract , FakeContractOptions , FakeContractSpec , MockContractFactory } from './types' ; import { getHardhatBaseProvider , makeRandomAddress } from './utils' ; let decodeRevertReason ; try { decodeRevertReason = require ( 'hardhat/internal/hardhat-network/stack-traces/revert-reasons' ) . decodeRevertReason ; } catch ( err ) { const { ReturnData } = require ( 'hardhat/internal/hardhat-network/provider/return-data' ) ; decodeRevertReason = ( value ) => { const returnData = new ReturnData ( value ) ; return returnData . isErrorReturnData ( ) ? returnData . decodeError ( ) : '' ; } ; } let TransactionExecutionError ; try { TransactionExecutionError = require ( 'hardhat/internal/hardhat-network/provider/errors' ) . TransactionExecutionError ; } catch ( err ) { TransactionExecutionError = require ( 'hardhat/internal/core/providers/errors' ) . TransactionExecutionError ; } export class Sandbox { private vm : ObservableVM ; private static nonce : number = 0 ; constructor ( vm ) { this . vm = new ObservableVM ( vm ) ; } async fake < Type extends BaseContract > ( spec , opts = { } ) { return createFakeContract ( this . vm , opts . address || makeRandomAddress ( ) , await ethersInterfaceFromSpec ( spec ) , opts . provider || hre . ethers . provider ) ; } async mock < T extends ContractFactory > ( contractName , signerOrOptions ? ) { return createMockContractFactory ( this . vm , contractName , signerOrOptions ) ; } static async create ( ) { if ( hre . network . name !== 'hardhat' ) { throw new Error ( `Smock is only compatible with the "hardhat" network, got: ${ hre . network . name }. Follow this issue for more info: https://github.com/defi-wonderland/smock/issues/29` ) ; } const provider = await getHardhatBaseProvider ( hre ) ; const node = provider . _node ; if ( node === undefined ) { await provider . _init ( ) ; } const originalManagerErrorsFn = node . _manageErrors . bind ( node ) ; node . _manageErrors = async ( vmResult , vmTrace , vmTracerError ? ) : Promise < any > => { if ( vmResult . exceptionError && vmResult . exceptionError . error === 'smock revert' ) { return new TransactionExecutionError ( `VM Exception while processing transaction: revert ${ decodeRevertReason ( vmResult . returnValue ) }` ) ; } return originalManagerErrorsFn ( vmResult , vmTrace , vmTracerError ) ; } ; return new Sandbox ( provider . _node . _vm as VM ) ; } static getNextNonce ( ) { return Sandbox . nonce ++ ; } }