message-utils.ts 3.08 KB
Newer Older
1
import { hashWithdrawal } from '@eth-optimism/core-utils'
Will Cory's avatar
Will Cory committed
2
import { BigNumber, utils, ethers } from 'ethers'
3 4 5

import { LowLevelMessage } from '../interfaces'

6 7
const { hexDataLength } = utils

8
// Constants used by `CrossDomainMessenger.baseGas`
9 10 11 12 13 14 15
const RELAY_CONSTANT_OVERHEAD = BigNumber.from(200_000)
const RELAY_PER_BYTE_DATA_COST = BigNumber.from(16)
const MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR = BigNumber.from(64)
const MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR = BigNumber.from(63)
const RELAY_CALL_OVERHEAD = BigNumber.from(40_000)
const RELAY_RESERVED_GAS = BigNumber.from(40_000)
const RELAY_GAS_CHECK_BUFFER = BigNumber.from(5_000)
16

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/**
 * Utility for hashing a LowLevelMessage object.
 *
 * @param message LowLevelMessage object to hash.
 * @returns Hash of the given LowLevelMessage.
 */
export const hashLowLevelMessage = (message: LowLevelMessage): string => {
  return hashWithdrawal(
    message.messageNonce,
    message.sender,
    message.target,
    message.value,
    message.minGasLimit,
    message.message
  )
}
33

Will Cory's avatar
Will Cory committed
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
/**
 * Utility for hashing a message hash. This computes the storage slot
 * where the message hash will be stored in state. HashZero is used
 * because the first mapping in the contract is used.
 *
 * @param messageHash Message hash to hash.
 * @returns Hash of the given message hash.
 */
export const hashMessageHash = (messageHash: string): string => {
  const data = ethers.utils.defaultAbiCoder.encode(
    ['bytes32', 'uint256'],
    [messageHash, ethers.constants.HashZero]
  )
  return ethers.utils.keccak256(data)
}

50 51 52
/**
 * Compute the min gas limit for a migrated withdrawal.
 */
53 54 55 56
export const migratedWithdrawalGasLimit = (
  data: string,
  chainID: number
): BigNumber => {
57
  // Compute the gas limit and cap at 25 million
clabby's avatar
clabby committed
58 59 60
  const dataCost = BigNumber.from(hexDataLength(data)).mul(
    RELAY_PER_BYTE_DATA_COST
  )
clabby's avatar
clabby committed
61
  let overhead: BigNumber
62
  if (chainID === 420) {
clabby's avatar
clabby committed
63
    overhead = BigNumber.from(200_000)
64
  } else {
65
    // Dynamic overhead (EIP-150)
66 67 68 69
    // We use a constant 1 million gas limit due to the overhead of simulating all migrated withdrawal
    // transactions during the migration. This is a conservative estimate, and if a withdrawal
    // uses more than the minimum gas limit, it will fail and need to be replayed with a higher
    // gas limit.
Mark Tyneway's avatar
Mark Tyneway committed
70 71 72
    const dynamicOverhead = MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR.mul(
      1_000_000
    ).div(MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR)
73

74
    // Constant overhead
Mark Tyneway's avatar
Mark Tyneway committed
75
    overhead = RELAY_CONSTANT_OVERHEAD.add(dynamicOverhead)
76 77 78 79 80 81 82 83 84
      .add(RELAY_CALL_OVERHEAD)
      // Gas reserved for the worst-case cost of 3/5 of the `CALL` opcode's dynamic gas
      // factors. (Conservative)
      // Relay reserved gas (to ensure execution of `relayMessage` completes after the
      // subcontext finishes executing) (Conservative)
      .add(RELAY_RESERVED_GAS)
      // Gas reserved for the execution between the `hasMinGas` check and the `CALL`
      // opcode. (Conservative)
      .add(RELAY_GAS_CHECK_BUFFER)
85
  }
86

87
  let minGasLimit = dataCost.add(overhead)
88 89 90 91 92
  if (minGasLimit.gt(25_000_000)) {
    minGasLimit = BigNumber.from(25_000_000)
  }
  return minGasLimit
}