Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 0 additions & 26 deletions packages/ovm-toolchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,30 +86,4 @@ 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)
})
```
1 change: 0 additions & 1 deletion packages/ovm-toolchain/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './ganache'
export * from './waffle'
export * from './x-domain-utils'
export * from './watcher'
68 changes: 0 additions & 68 deletions packages/ovm-toolchain/src/watcher.ts

This file was deleted.

3 changes: 3 additions & 0 deletions packages/watcher/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
build/
test/temp/
24 changes: 24 additions & 0 deletions packages/watcher/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# @eth-optimism/watcher

#### 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. `getL2TransactionReceipt(messageHash)` takes in an L1->L2 message hash and then after 2-5 minutes, returns the receipt of the L2 tx that the message ends up getting relayed in. `getL1TransactionReceipt(messageHash)` 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)
const l2TxReceipt = await watcher.getL2TransactionReceipt(messageHash)
```
1 change: 1 addition & 0 deletions packages/watcher/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src'
58 changes: 58 additions & 0 deletions packages/watcher/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"name": "@eth-optimism/watcher",
"version": "0.0.1-alpha.4",
"description": "Watcher for cross domain messages",
"main": "build/index.js",
"files": [
"build/**/*.js"
],
"workspaces": {
"nohoist": [
"**/@nomiclabs",
"**/@nomiclabs/**",
"**/typescript",
"**/typescript/**",
"**/ts-node",
"**/ts-node/**"
]
},
"scripts": {
"all": "yarn clean && yarn build && yarn test && yarn fix && yarn lint",
"lint": "tslint --format stylish --project .",
"fix": "prettier --config ../../prettier-config.json --write \"index.ts\" \"{deploy,test,src,bin}/**/*.ts\"",
"build": "yarn run build:typescript",
"build:typescript": "tsc -p .",
"clean": "rimraf build/"
},
"keywords": [
"optimistic",
"rollup",
"group",
"ethereum",
"smart",
"contract"
],
"author": "Optimism",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/ethereum-optimism/optimism-monorepo.git"
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.6",
"@types/node": "^11.11.3",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"mocha": "^6.0.2",
"rimraf": "^2.6.3",
"ts-node": "^8.10.2",
"typescript": "^3.3.3333"
},
"dependencies": {
"ethers-v4": "npm:ethers@4"
}
}
1 change: 1 addition & 0 deletions packages/watcher/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './watcher'
100 changes: 100 additions & 0 deletions packages/watcher/src/watcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* 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
public NUM_BLOCKS_TO_FETCH: number = 10_000_000

constructor(opts: WatcherOptions) {
this.l1 = opts.l1
this.l2 = opts.l2
}

public async getMessageHashesFromL1Tx(l1TxHash: string): Promise<string[]> {
return this._getMessageHashesFromTx(true, l1TxHash)
}
public async getMessageHashesFromL2Tx(l2TxHash: string): Promise<string[]> {
return this._getMessageHashesFromTx(false, l2TxHash)
}

public async getL1TransactionReceipt(l2ToL1MsgHash: string): Promise<any> {
return this._getLXTransactionReceipt(true, l2ToL1MsgHash)
}

public async getL2TransactionReceipt(l1ToL2MsgHash: string): Promise<any> {
return this._getLXTransactionReceipt(false, l1ToL2MsgHash)
}

private async _getMessageHashesFromTx(
isL1: boolean,
txHash: string
): Promise<string[]> {
const layer = isL1 ? this.l1 : this.l2
const receipt = await layer.provider.getTransactionReceipt(txHash)
const msgHashes = []
for (const log of receipt.logs) {
if (
log.address === layer.messengerAddress &&
log.topics[0] === ethers.utils.id('SentMessage(bytes32)')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that its a good idea to turn this function signature into a constant

) {
msgHashes.push(log.data)
}
}
return msgHashes
}

public async _getLXTransactionReceipt(
isL1: boolean,
msgHash: string
): Promise<any> {
const layer = isL1 ? this.l1 : this.l2
const blockNumber = await layer.provider.getBlockNumber()
const startingBlock = Math.max(blockNumber - this.NUM_BLOCKS_TO_FETCH, 0)
const filter = {
address: layer.messengerAddress,
topics: [
ethers.utils.id(`Relayed${isL1 ? 'L2ToL1' : 'L1ToL2'}Message(bytes32)`),
],
fromBlock: startingBlock,
}
const logs = await layer.provider.getLogs(filter)
const matches = logs.filter((log: any) => log.data === msgHash)

// Message was relayed in the past
if (matches.length > 0) {
if (matches.length > 1) {
throw Error(
'Found multiple transactions relaying the same message hash.'
)
}
return layer.provider.getTransactionReceipt(matches[0].transactionHash)
}

// Message has yet to be relayed
return new Promise(async (resolve, reject) => {
layer.provider.on(filter, async (log: any) => {
if (log.data === msgHash) {
try {
const txReceipt = await layer.provider.getTransactionReceipt(
log.transactionHash
)
resolve(txReceipt)
} catch (e) {
reject(e)
}
}
})
})
}
}
12 changes: 12 additions & 0 deletions packages/watcher/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "./../../tsconfig.json",
"compilerOptions": {
"outDir": "./build",
"baseUrl": "./",
"resolveJsonModule": true,
"esModuleInterop": true,
"allowJs": true
},
"include": ["*.ts", "**/*.ts", "artifacts/*.json"],
"exclude": ["./build", "node_modules"]
}
7 changes: 7 additions & 0 deletions packages/watcher/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": ["./../../tslint.json"],
"rules": {
"prettier": [true, "../../prettier-config.json"],
"no-console": false
}
}