diff --git a/packages/contracts/buidler.config.ts b/packages/contracts/buidler.config.ts index 6dffc0757a136..c9449f34ae630 100644 --- a/packages/contracts/buidler.config.ts +++ b/packages/contracts/buidler.config.ts @@ -1,4 +1,5 @@ -import { usePlugin, BuidlerConfig } from '@nomiclabs/buidler/config' +import * as path from 'path' +import { usePlugin, BuidlerConfig, task } from '@nomiclabs/buidler/config' import { DEFAULT_ACCOUNTS_BUIDLER, @@ -27,6 +28,18 @@ const parseSolppFlags = (): { [flag: string]: boolean } => { return flags } +task('compile') + .addFlag('ovm', 'Compile using OVM solc compiler') + .setAction(async (taskArguments, bre: any, runSuper) => { + if (taskArguments.ovm) { + bre.config.solc = { + path: path.resolve(__dirname, '../../node_modules/@eth-optimism/solc'), + } + bre.config.paths.artifacts = './build/ovm_artifacts' + } + await runSuper(taskArguments) + }) + const config: BuidlerConfig = { networks: { buidlerevm: { diff --git a/packages/contracts/contracts/optimistic-ethereum/bridge/L1CrossDomainMessenger.sol b/packages/contracts/contracts/optimistic-ethereum/bridge/L1CrossDomainMessenger.sol index d168fa5750682..7460a103421c1 100644 --- a/packages/contracts/contracts/optimistic-ethereum/bridge/L1CrossDomainMessenger.sol +++ b/packages/contracts/contracts/optimistic-ethereum/bridge/L1CrossDomainMessenger.sol @@ -19,6 +19,8 @@ contract L1CrossDomainMessenger is BaseCrossDomainMessenger, ContractResolver { event RelayedL2ToL1Message(bytes32 msgHash); + address public l1ToL2QueueAddress; + /* * Data Structures */ @@ -44,6 +46,11 @@ contract L1CrossDomainMessenger is BaseCrossDomainMessenger, ContractResolver { ContractResolver(_addressResolver) {} + function tempInit(address _l1ToL2QueueAddress) public { + require(l1ToL2QueueAddress == address(0)); + l1ToL2QueueAddress = _l1ToL2QueueAddress; + } + /* * Public Functions @@ -236,7 +243,10 @@ contract L1CrossDomainMessenger is BaseCrossDomainMessenger, ContractResolver { view returns (L1ToL2TransactionQueue) { - return L1ToL2TransactionQueue(resolveContract("L1ToL2TransactionQueue")); + if (l1ToL2QueueAddress == address(0)) { + return L1ToL2TransactionQueue(resolveContract("L1ToL2TransactionQueue")); + } + return L1ToL2TransactionQueue(l1ToL2QueueAddress); } function resolveStateCommitmentChain() diff --git a/packages/contracts/contracts/optimistic-ethereum/bridge/L2CrossDomainMessenger.sol b/packages/contracts/contracts/optimistic-ethereum/bridge/L2CrossDomainMessenger.sol index 1dc0e69358a60..4d8742f8a1bec 100644 --- a/packages/contracts/contracts/optimistic-ethereum/bridge/L2CrossDomainMessenger.sol +++ b/packages/contracts/contracts/optimistic-ethereum/bridge/L2CrossDomainMessenger.sol @@ -21,6 +21,8 @@ contract L2CrossDomainMessenger is BaseCrossDomainMessenger { address private l1MessageSenderPrecompileAddress; address private l2ToL1MessagePasserPrecompileAddress; + address public authenticatedAddress; + /* * Constructor */ @@ -102,9 +104,17 @@ contract L2CrossDomainMessenger is BaseCrossDomainMessenger { bool ) { - IL1MessageSender l1MessageSenderPrecompile = IL1MessageSender(l1MessageSenderPrecompileAddress); - address l1MessageSenderAddress = l1MessageSenderPrecompile.getL1MessageSender(); - return l1MessageSenderAddress == targetMessengerAddress; + if (authenticatedAddress == address(0)) { + IL1MessageSender l1MessageSenderPrecompile = IL1MessageSender(l1MessageSenderPrecompileAddress); + address l1MessageSenderAddress = l1MessageSenderPrecompile.getL1MessageSender(); + return l1MessageSenderAddress == targetMessengerAddress; + } + return msg.sender == authenticatedAddress; + } + + function tempInit(address _authenticatedAddress) public { + require(authenticatedAddress == address(0)); + authenticatedAddress = _authenticatedAddress; } /** diff --git a/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol b/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol index e6251870b7191..e8537aa944b54 100644 --- a/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol +++ b/packages/contracts/contracts/optimistic-ethereum/queue/L1ToL2TransactionQueue.sol @@ -30,6 +30,8 @@ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { uint constant public L2_GAS_DISCOUNT_DIVISOR = 10; + address public l1MessengerAddress; + /* * Constructor */ @@ -49,6 +51,11 @@ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { * Public Functions */ + function tempInit(address _l1MessengerAddress) public { + require(l1MessengerAddress == address(0)); + l1MessengerAddress = _l1MessengerAddress; + } + /** * Checks that that a dequeue is authenticated, and dequques if authenticated. */ @@ -69,6 +76,8 @@ contract L1ToL2TransactionQueue is ContractResolver, RollupQueue { ) external { + require(l1MessengerAddress == address(0) || msg.sender == l1MessengerAddress); + uint gasToBurn = _ovmGasLimit / L2_GAS_DISCOUNT_DIVISOR; resolveGasConsumer().consumeGasInternalCall(gasToBurn); diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 030ce063a5ca6..7912295506fd2 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,12 +1,13 @@ { "name": "@eth-optimism/rollup-contracts", "private": true, - "version": "0.0.1-alpha.33", + "version": "0.0.1-alpha.35", "main": "build/index.js", "files": [ "build/**/*.js", "build/contracts/*", - "build/artifacts/*json" + "build/artifacts/*json", + "build/ovm_artifacts/*json" ], "license": "MIT", "workspaces": { @@ -29,6 +30,7 @@ "coverage:contracts": "cross-env SOLPP_FLAGS=\"FLAG_IS_TEST\" buidler coverage --network coverage --show-stack-traces --testfiles \"test/contracts/**/*.spec.ts\"", "build": "yarn run build:contracts && yarn run build:typescript && yarn run build:copy", "build:contracts": "buidler compile", + "build:contracts:ovm": "buidler compile --ovm", "build:typescript": "tsc -p .", "build:copy": "yarn run build:copy:contracts", "build:copy:contracts": "copyfiles -u 2 \"contracts/optimistic-ethereum/**/*.sol\" \"build/contracts\"", @@ -37,7 +39,8 @@ "lint:typescript": "tslint --format stylish --project .", "fix": "yarn run fix:typescript", "fix:typescript": "prettier --config ../../prettier-config.json --write \"index.ts\" \"buidler.config.ts\" \"{src,test,plugins}/**/*.ts\"", - "deploy:all": "env DEBUG=\"info:*,error:*,debug:*\" node ./build/src/exec/deploy-contracts.js" + "deploy:all": "env DEBUG=\"info:*,error:*,debug:*\" node ./build/src/exec/deploy-contracts.js", + "prepublish": "yarn run build && yarn run build:contracts:ovm" }, "dependencies": { "@ethersproject/keccak256": "5.0.3", diff --git a/packages/ovm-toolchain/README.md b/packages/ovm-toolchain/README.md index 87a80dbb7a6a7..8cc701eeec3f1 100644 --- a/packages/ovm-toolchain/README.md +++ b/packages/ovm-toolchain/README.md @@ -86,4 +86,30 @@ const config = { } export default config +``` + +#### Watcher +Our `Watcher` allows you to retrieve all transaction hashes related to cross domain messages such as deposits and withdrawals. In order to use, first send a transaction which sends a cross domain message, for example a deposit from L1 into L2. After sending the deposit transaction and storing the transaction hash, use `getMessageHashesFromL1Tx(l1TxHash)` to get an array of the message hashes of all of the L1->L2 messages that were sent inside of that L1 tx (This will usually just be a single element array, but it can return multiple if one L1 transaction triggers multiple deposits). `getMessageHashesFromL2Tx(l2TxHash)` does the same for L2->L1 messages. `onceL2Relay(messageHash, callback)` takes in an L1->L2 message hash and a callback that will be triggered after 2-5 minutes with the hash of the L2 tx that the message ends up getting relayed in. `onceL1Relay(messageHash, callback)` does the same for L2->L1 messages, except the delay is 7 days. + +```typescript +import { Watcher } from '@eth-optimism/ovm-toolchain/' +import { JsonRpcProvider } from 'ethers/providers' + +const watcher = new Watcher({ + l1: { + provider: new JsonRpcProvider('INFURA_L1_URL'), + messengerAddress: '0x...' + }, + l2: { + provider: new JsonRpcProvider('OPTIMISM_L2_URL'), + messengerAddress: '0x...' + } +}) +const l1TxHash = (await depositContract.deposit(100)).hash +const [messageHash] = await watcher.getMessageHashesFromL1Tx(l1TxHash) +console.log('L1->L2 message hash:', messageHash) +watcher.onceL2Relay(messageHash, (l2txhash) => { + // Takes 2-5 minutes + console.log('Got L2 Tx Hash:', l2txhash) +}) ``` \ No newline at end of file diff --git a/packages/ovm-toolchain/package.json b/packages/ovm-toolchain/package.json index 40e9ef6520866..6a838a0982518 100644 --- a/packages/ovm-toolchain/package.json +++ b/packages/ovm-toolchain/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/ovm-toolchain", - "version": "0.0.1-alpha.7", + "version": "0.0.1-alpha.8", "description": "Wrappers for Ethereum dev tools", "private": true, "main": "build/index.js", diff --git a/packages/ovm-toolchain/src/index.ts b/packages/ovm-toolchain/src/index.ts index c459e1ce7c202..06c06a454cafa 100644 --- a/packages/ovm-toolchain/src/index.ts +++ b/packages/ovm-toolchain/src/index.ts @@ -1,3 +1,4 @@ export * from './ganache' export * from './waffle' export * from './x-domain-utils' +export * from './watcher' diff --git a/packages/ovm-toolchain/src/watcher.ts b/packages/ovm-toolchain/src/watcher.ts new file mode 100644 index 0000000000000..8bbdec8a845af --- /dev/null +++ b/packages/ovm-toolchain/src/watcher.ts @@ -0,0 +1,68 @@ +/* External Imports */ +import { ethers } from 'ethers-v4' + +interface Layer { + provider: any + messengerAddress: string +} + +interface WatcherOptions { + l1: Layer + l2: Layer +} + +export class Watcher { + public l1: Layer + public l2: Layer + + constructor(opts: WatcherOptions) { + this.l1 = opts.l1 + this.l2 = opts.l2 + } + + public async getMessageHashesFromL1Tx(l1TxHash: string): Promise { + return this._getMessageHashesFromTx(true, l1TxHash) + } + public async getMessageHashesFromL2Tx(l2TxHash: string): Promise { + return this._getMessageHashesFromTx(false, l2TxHash) + } + + public onceL2Relay(msgHash: string, callback: Function): void { + return this._onceRelay(false, msgHash, callback) + } + + public onceL1Relay(msgHash: string, callback: Function): void { + return this._onceRelay(true, msgHash, callback) + } + + private async _getMessageHashesFromTx( + isL1: boolean, + txHash: string + ): Promise { + const layer = isL1 ? this.l1 : this.l2 + const l1Receipt = await layer.provider.getTransactionReceipt(txHash) + const filtered = l1Receipt.logs.filter((log: any) => { + return ( + log.address === layer.messengerAddress && + log.topics[0] === ethers.utils.id('SentMessage(bytes32)') + ) + }) + return filtered.map((log: any) => log.data) + } + + private _onceRelay(isL1: boolean, msgHash: string, callback: Function) { + const layer = isL1 ? this.l1 : this.l2 + const filter = { + address: layer.messengerAddress, + topics: [ + ethers.utils.id(`Relayed${isL1 ? 'L2ToL1' : 'L1ToL2'}Message(bytes32)`), + ], + } + + layer.provider.on(filter, (log: any) => { + if (log.data === msgHash) { + callback(log.transactionHash) + } + }) + } +} diff --git a/yarn.lock b/yarn.lock index 673cb53a52ea1..807577a30d4ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5410,32 +5410,6 @@ ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: util.promisify "^1.0.0" uuid "^8.3.0" -"ethereumjs-ovm@git+https://github.com/ethereum-optimism/ethereumjs-vm.git": - version "4.2.0" - uid "02ba6e77a88339b04a053b5e653f644acb1555b8" - resolved "git+https://github.com/ethereum-optimism/ethereumjs-vm.git#02ba6e77a88339b04a053b5e653f644acb1555b8" - dependencies: - "@types/debug" "^4.1.5" - "@types/uuid" "^8.3.0" - async "^2.1.2" - async-eventemitter "^0.2.2" - core-js-pure "^3.0.1" - debug "^4.1.1" - ethereumjs-account "^3.0.0" - ethereumjs-block "^2.2.2" - ethereumjs-blockchain "^4.0.3" - ethereumjs-common "^1.5.0" - ethereumjs-tx "^2.1.2" - ethereumjs-util "^6.2.0" - ethers "^5.0.0" - fake-merkle-patricia-tree "^1.0.1" - functional-red-black-tree "^1.0.1" - merkle-patricia-tree "^2.3.2" - rustbn.js "~0.2.0" - safe-buffer "^5.1.1" - util.promisify "^1.0.0" - uuid "^8.3.0" - ethereumjs-tx@1.3.7, ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3: version "1.3.7" resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a"