Commit d068cc8a authored by Kelvin Fichter's avatar Kelvin Fichter Committed by GitHub

Adds an ovm compilation plugin, compiles into artifacts and artifacts/ovm (#118)

* Linted files

* Revert back to old artifacts structure

* Fixed conflict with smock
parent 87d1b3cc
......@@ -9,7 +9,7 @@ usePlugin('@nomiclabs/buidler-ethers')
usePlugin('@nomiclabs/buidler-waffle')
usePlugin('buidler-typechain')
import '@eth-optimism/smock/build/src/buidler-plugins/compiler-storage-layout'
import './plugins/buidler/ovm-compiler'
const config: BuidlerConfig = {
networks: {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
import { iOVM_BaseCrossDomainMessenger } from "../../iOVM/bridge/iOVM_BaseCrossDomainMessenger.sol";
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
/* Library Imports */
import { Lib_ReentrancyGuard } from "../../libraries/utils/Lib_ReentrancyGuard.sol";
/**
* @title OVM_BaseCrossDomainMessenger
*/
contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger, ReentrancyGuard {
contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger, Lib_ReentrancyGuard {
/**********************
* Contract Variables *
......@@ -25,7 +28,7 @@ contract OVM_BaseCrossDomainMessenger is iOVM_BaseCrossDomainMessenger, Reentran
* Public Functions *
********************/
constructor() ReentrancyGuard() public {}
constructor() Lib_ReentrancyGuard() public {}
/**
* Sends a cross domain message to the target messenger.
......
// SPDX-License-Identifier: MIT
// +build ovm
pragma solidity >0.6.0 <0.8.0;
pragma experimental ABIEncoderV2;
......@@ -28,7 +29,10 @@ contract OVM_L2CrossDomainMessenger is iOVM_L2CrossDomainMessenger, OVM_BaseCros
*/
constructor(
address _libAddressManager
) Lib_AddressResolver(_libAddressManager) {}
)
public
Lib_AddressResolver(_libAddressManager)
{}
/********************
......
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
pragma experimental ABIEncoderV2;
/**
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Interface Imports */
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
/**
* @title iOVM_L1MessageSender
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
/**
* @title iOVM_L2ToL1MessagePasser
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
/* Contract Imports */
import { Ownable } from "./Lib_Ownable.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
/* Library Imports */
import { Lib_AddressManager } from "./Lib_AddressManager.sol";
......@@ -25,7 +26,7 @@ contract Lib_AddressResolver {
*/
constructor(
address _libAddressManager
) {
) public {
libAddressManager = Lib_AddressManager(_libAddressManager);
}
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// +build ovm
pragma solidity >0.6.0 <0.8.0;
/**
* @title Ownable
......@@ -28,7 +29,7 @@ abstract contract Ownable {
* Constructor *
***************/
constructor() {
constructor() internal {
owner = msg.sender;
emit OwnershipTransferred(address(0), owner);
}
......
// SPDX-License-Identifier: MIT
// +build ovm
pragma solidity >0.6.0 <0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract Lib_ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
......@@ -22,11 +22,12 @@
"test:gas": "buidler test \"test/contracts/OVM/execution/OVM_StateManager.gas-spec.ts\" --no-compile --show-stack-traces",
"lint": "yarn run lint:typescript",
"lint:typescript": "tslint --format stylish --project .",
"lint:fix:typescript": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test}/**/*.ts\"",
"lint:fix:typescript": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test,plugins}/**/*.ts\"",
"clean": "rm -rf ./artifacts ./build ./cache",
"deploy": "./bin/deploy.js"
},
"dependencies": {
"@eth-optimism/solc": "^0.6.12-alpha.1",
"@ethersproject/contracts": "^5.0.5",
"@ethersproject/hardware-wallets": "^5.0.8",
"@openzeppelin/contracts": "^3.3.0",
......
import * as path from 'path'
import fsExtra from 'fs-extra'
import { internalTask } from '@nomiclabs/buidler/config'
import { SolcInput } from '@nomiclabs/buidler/types'
import { Compiler } from '@nomiclabs/buidler/internal/solidity/compiler'
import { pluralize } from '@nomiclabs/buidler/internal/util/strings'
import {
saveArtifact,
getArtifactFromContractOutput,
} from '@nomiclabs/buidler/internal/artifacts'
import {
TASK_COMPILE_RUN_COMPILER,
TASK_BUILD_ARTIFACTS,
TASK_COMPILE_GET_SOURCE_PATHS,
TASK_COMPILE_CHECK_CACHE,
TASK_COMPILE_COMPILE,
TASK_COMPILE_GET_COMPILER_INPUT,
} from '@nomiclabs/buidler/builtin-tasks/task-names'
internalTask(TASK_COMPILE_RUN_COMPILER).setAction(
async ({ input }: { input: SolcInput }, { config }) => {
// Try to find a path to @eth-optimism/solc, throw if we can't.
let ovmSolcJs: any
try {
ovmSolcJs = require('@eth-optimism/solc')
} catch (err) {
if (err.toString().contains('Cannot find module')) {
throw new Error(
`ovm-toolchain: Could not find "@eth-optimism/solc" in your node_modules.`
)
} else {
throw err
}
}
const evmCompiler = new Compiler(
config.solc.version,
path.join(config.paths.cache, 'compilers')
)
const ovmCompiler = new Compiler(
ovmSolcJs.version(),
path.join(config.paths.cache, 'compilers')
)
ovmCompiler.getSolc = () => {
return ovmSolcJs
}
const ovmInput = {
language: 'Solidity',
sources: {},
settings: input.settings,
}
const evmInput = {
language: 'Solidity',
sources: {},
settings: input.settings,
}
// Separate the EVM and OVM inputs.
for (const file of Object.keys(input.sources)) {
evmInput.sources[file] = input.sources[file]
if (input.sources[file].content.includes('// +build ovm')) {
ovmInput.sources[file] = input.sources[file]
}
}
// Build both inputs separately.
console.log('Compiling ovm contracts...')
const ovmOutput = await ovmCompiler.compile(ovmInput)
console.log('Compiling evm contracts...')
const evmOutput = await evmCompiler.compile(evmInput)
// Filter out any "No input sources specified" errors, but only if one of the two compilations
// threw the error.
let errors = (ovmOutput.errors || []).concat(evmOutput.errors || [])
const filtered = errors.filter((error: any) => {
return error.message !== 'No input sources specified.'
})
if (errors.length === filtered.length + 1) {
errors = filtered
}
for (const name of Object.keys(ovmOutput.contracts)) {
ovmOutput.contracts[`${name}.ovm`] = ovmOutput.contracts[name]
delete ovmOutput.contracts[name]
}
// Combine the outputs.
const output = {
contracts: {
...ovmOutput.contracts,
...evmOutput.contracts,
},
errors,
sources: {
...ovmOutput.sources,
...evmOutput.sources,
},
}
return output
}
)
internalTask(
TASK_COMPILE_GET_COMPILER_INPUT,
async (_, { config, run }, runSuper) => {
const input = await runSuper()
// For smock.
input.settings.outputSelection['*']['*'].push('storageLayout')
return input
}
)
internalTask(TASK_BUILD_ARTIFACTS, async ({ force }, { config, run }) => {
const sources = await run(TASK_COMPILE_GET_SOURCE_PATHS)
if (sources.length === 0) {
console.log('No Solidity source file available.')
return
}
const isCached: boolean = await run(TASK_COMPILE_CHECK_CACHE, { force })
if (isCached) {
console.log(
'All contracts have already been compiled, skipping compilation.'
)
return
}
const compilationOutput = await run(TASK_COMPILE_COMPILE)
if (compilationOutput === undefined) {
return
}
await fsExtra.ensureDir(config.paths.artifacts)
let numberOfContracts = 0
for (const [fileName, file] of Object.entries<any>(
compilationOutput.contracts
)) {
for (const [contractName, contractOutput] of Object.entries(file)) {
const artifact = getArtifactFromContractOutput(
contractName,
contractOutput
)
numberOfContracts += 1
// For smock.
;(artifact as any).storageLayout = (contractOutput as any).storageLayout
if (fileName.endsWith('.ovm')) {
await saveArtifact(config.paths.artifacts + '/ovm', artifact)
} else {
await saveArtifact(config.paths.artifacts, artifact)
}
}
}
console.log(
'Compiled',
numberOfContracts,
pluralize(numberOfContracts, 'contract'),
'successfully'
)
})
......@@ -2,20 +2,28 @@ import * as path from 'path'
import { ethers, ContractFactory, Signer } from 'ethers'
import { Interface } from 'ethers/lib/utils'
export const getContractDefinition = (name: string): any => {
return require(path.join(__dirname, '../artifacts', `${name}.json`))
export const getContractDefinition = (name: string, ovm?: boolean): any => {
return require(path.join(
__dirname,
`../artifacts${ovm ? '/ovm' : ''}`,
`${name}.json`
))
}
export const getContractInterface = (name: string): Interface => {
const definition = getContractDefinition(name)
export const getContractInterface = (
name: string,
ovm?: boolean
): Interface => {
const definition = getContractDefinition(name, ovm)
return new ethers.utils.Interface(definition.abi)
}
export const getContractFactory = (
name: string,
signer?: Signer
signer?: Signer,
ovm?: boolean
): ContractFactory => {
const definition = getContractDefinition(name)
const contractInterface = getContractInterface(name)
const definition = getContractDefinition(name, ovm)
const contractInterface = getContractInterface(name, ovm)
return new ContractFactory(contractInterface, definition.bytecode, signer)
}
......@@ -22,6 +22,7 @@
},
"include": [
"src/**/*.ts",
"plugins/**/*.ts",
"artifacts/*.json",
"./buidler.config.ts",
"./buidler-env.d.ts",
......
......@@ -48,6 +48,21 @@
ethers "^5"
fs-extra "^9.0.1"
"@eth-optimism/solc@^0.6.12-alpha.1":
version "0.6.12-alpha.1"
resolved "https://registry.yarnpkg.com/@eth-optimism/solc/-/solc-0.6.12-alpha.1.tgz#041876f83b34c6afe2f19dfe9626568df6ed8590"
integrity sha512-Ky73mo+2iNJs/VTaT751nMeZ7hXns0TBAlffTOxIOsScjAZ/zi/KWsDUo3r89aV2JKXcYAU/bLidxF40MVJeUw==
dependencies:
command-exists "^1.2.8"
commander "3.0.2"
follow-redirects "^1.12.1"
fs-extra "^0.30.0"
js-sha3 "0.8.0"
memorystream "^0.3.1"
require-from-string "^2.0.0"
semver "^5.5.0"
tmp "0.0.33"
"@ethereum-waffle/chai@^3.0.0", "@ethereum-waffle/chai@^3.1.1":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@ethereum-waffle/chai/-/chai-3.1.1.tgz#2d2594e19613cdee0eaa1a38fc8dac31f66460e6"
......@@ -4060,6 +4075,11 @@ flow-stoplight@^1.0.0:
resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b"
integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s=
follow-redirects@^1.12.1:
version "1.13.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
for-each@~0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment