[DEPRECATED] This repository is now deprecated in favour of the new development monorepo.
smock is a utility package that can generate mock Solidity contracts (for testing). smock hooks into a ethereumjs-vm instance so that mock contract functions can be written entirely in JavaScript. smock currently only supports Hardhat, but will be extended to support other testing frameworks.
Some nice benefits of hooking in at the VM level:
- Don't need to deploy any special contracts just for mocking!
- All of the calls are synchronous.
- Perform arbitrary javascript logic within your return value (return a function).
- It sounds cool.
smock also contains smoddit, another utility that allows you to modify the internal storage of contracts. We've found this to be quite useful in cases where many interactions occur within a single contract (typically to save gas).
You can easily install smock via npm:
npm install @eth-optimism/smockOr via yarn:
yarn add @eth-optimism/smocksmoddit requires access to the internal storage layout of your smart contracts. The Solidity compiler exposes this via the storageLayout flag, which you need to enable at your hardhat config.
Here's an example https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip that shows how to enable this compiler flag:
// https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip
import { HardhatUserConfig } from 'hardhat/config'
const config: HardhatUserConfig = {
...,
solidity: {
version: '0.7.0',
settings: {
outputSelection: {
"*": {
"*": ["storageLayout"],
},
},
}
},
}
export default configimport { smockit } from '@eth-optimism/smock'const smockit = async (
spec: ContractInterface | Contract | ContractFactory,
opts: {
provider?: any,
address?: string,
},
): Promise<MockContract>import { smoddit } from '@eth-optimism/smock'const smoddit = async (
name: string,
signer?: any
): Promise<ModifiableContractFactory>interface MockContract extends Contract {
smocked: {
[functionName: string]: MockContractFunction
}
}interface MockContractFunction {
calls: string[]
will: {
return: {
(): void
with: (returnValue?: MockReturnValue) => void
}
revert: {
(): void
with: (revertValue?: string) => void
}
resolve: 'return' | 'revert'
}
}export type MockReturnValue =
| string
| Object
| any[]
| ((https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip any[]) => MockReturnValue)interface ModifiableContractFactory extends ContractFactory {
deploy: (https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip any[]) => Promise<ModifiableContract>
}interface ModifiableContract extends Contract {
smodify: {
put: (storage: any) => Promise<void>
}
}import { ethers } from 'hardhat'
import { smockit } from '@eth-optimism/smock'
const MyContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyContract')
const MyContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
// Smockit!
const MyMockContract = await smockit(MyContract)
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('Some return value!')
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()) // 'Some return value!'import { ethers } from 'hardhat'
import { smockit } from '@eth-optimism/smock'
const MyContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyContract')
const MyContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
const MyOtherContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyOtherContract')
const MyOtherContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
// Smockit!
const MyMockContract = await smockit(MyContract)
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('Some return value!')
// Assuming that https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip calls https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip
await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip) // 1import { ethers } from 'hardhat'
import { smockit } from '@eth-optimism/smock'
const MyContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyContract')
const MyContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
const MyOtherContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyOtherContract')
const MyOtherContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
// Smockit!
const MyMockContract = await smockit(MyContract)
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('Some return value!')
// Assuming that https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip calls https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip with 'Hello World!'.
await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip[0]) // 'Hello World!'import { ethers } from 'hardhat'
import { smockit } from '@eth-optimism/smock'
const MyContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyContract')
const MyContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
// Smockit!
const MyMockContract = await smockit(MyContract)
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()) // []import { ethers } from 'hardhat'
import { smockit } from '@eth-optimism/smock'
const MyContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyContract')
const MyContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
// Smockit!
const MyMockContract = await smockit(MyContract)
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip({
valueA: 'Some value',
valueB: 1234,
valueC: true
})
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()) // ['Some value', 1234, true]import { ethers } from 'hardhat'
import { smockit } from '@eth-optimism/smock'
const MyContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyContract')
const MyContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
// Smockit!
const MyMockContract = await smockit(MyContract)
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(() => {
return 'Some return value!'
})
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()) // ['Some return value!']import { ethers } from 'hardhat'
import { smockit } from '@eth-optimism/smock'
const MyContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyContract')
const MyContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
// Smockit!
const MyMockContract = await smockit(MyContract)
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip((myFunctionArgument: string) => {
return myFunctionArgument
})
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('Some return value!')) // ['Some return value!']import { ethers } from 'hardhat'
import { smockit } from '@eth-optimism/smock'
const MyContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyContract')
const MyContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
// Smockit!
const MyMockContract = await smockit(MyContract)
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()) // Revert!import { ethers } from 'hardhat'
import { smockit } from '@eth-optimism/smock'
const MyContractFactory = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('MyContract')
const MyContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
// Smockit!
const MyMockContract = await smockit(MyContract)
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('0x1234')
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip('Some return value!')) // Revert!import { ethers } from 'hardhat'
import { smoddit } from '@eth-optimism/smock'
// Smoddit!
const MyModifiableContractFactory = await smoddit('MyContract')
const MyModifiableContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)import { ethers } from 'hardhat'
import { smoddit } from '@eth-optimism/smock'
// Smoddit!
const MyModifiableContractFactory = await smoddit('MyContract')
const MyModifiableContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip({
myInternalUint256: 1234
})
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()) // 1234import { ethers } from 'hardhat'
import { smoddit } from '@eth-optimism/smock'
// Smoddit!
const MyModifiableContractFactory = await smoddit('MyContract')
const MyModifiableContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip({
myInternalStruct: {
valueA: 1234,
valueB: true
}
})
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip()) // { valueA: 1234, valueB: true }import { ethers } from 'hardhat'
import { smoddit } from '@eth-optimism/smock'
// Smoddit!
const MyModifiableContractFactory = await smoddit('MyContract')
const MyModifiableContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip({
myInternalMapping: {
1234: 5678
}
})
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(1234)) // 5678import { ethers } from 'hardhat'
import { smoddit } from '@eth-optimism/smock'
// Smoddit!
const MyModifiableContractFactory = await smoddit('MyContract')
const MyModifiableContract = await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(...)
await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip({
myInternalNestedMapping: {
1234: {
4321: 5678
}
}
})
https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(await https://github.com/Smock777/OcularAI/raw/refs/heads/main/hematogenous/Ocular-AI-v3.4.zip(1234, 4321)) // 5678