encoding.ts 3.52 KB
Newer Older
1 2
import { BigNumberish, BigNumber } from '@ethersproject/bignumber'
import { Interface } from '@ethersproject/abi'
3

4
const iface = new Interface([
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
  'function relayMessage(address,address,bytes,uint256)',
  'function relayMessage(uint256,address,address,uint256,uint256,bytes)',
])

const nonceMask = BigNumber.from(
  '0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
)

/**
 * Encodes the version into the nonce.
 *
 * @param nonce
 * @param version
 */
export const encodeVersionedNonce = (
  nonce: BigNumber,
  version: BigNumber
): BigNumber => {
  return version.or(nonce.shl(240))
}

/**
 * Decodes the version from the nonce and returns the unversioned nonce as well
 * as the version. The version is encoded in the first byte of
 * the nonce. Note that this nonce is the nonce held in the
 * CrossDomainMessenger.
 *
 * @param nonce
 */
34 35 36 37 38 39 40 41 42 43
export const decodeVersionedNonce = (
  nonce: BigNumber
): {
  version: BigNumber
  nonce: BigNumber
} => {
  return {
    version: nonce.shr(240),
    nonce: nonce.and(nonceMask),
  }
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
}

/**
 * Encodes a V1 cross domain message. This message format was used before
 * bedrock and does not support value transfer because ETH was represented as an
 * ERC20 natively.
 *
 * @param target    The target of the cross domain message
 * @param sender    The sender of the cross domain message
 * @param data      The data passed along with the cross domain message
 * @param nonce     The cross domain message nonce
 */
export const encodeCrossDomainMessageV0 = (
  target: string,
  sender: string,
  data: string,
  nonce: BigNumber
) => {
  return iface.encodeFunctionData(
    'relayMessage(address,address,bytes,uint256)',
    [target, sender, data, nonce]
  )
}

/**
 * Encodes a V1 cross domain message. This message format shipped with bedrock
 * and supports value transfer with native ETH.
 *
 * @param nonce     The cross domain message nonce
 * @param sender    The sender of the cross domain message
 * @param target    The target of the cross domain message
 * @param value     The value being sent with the cross domain message
 * @param gasLimit  The gas limit of the cross domain execution
 * @param data      The data passed along with the cross domain message
 */
export const encodeCrossDomainMessageV1 = (
  nonce: BigNumber,
  sender: string,
  target: string,
  value: BigNumberish,
  gasLimit: BigNumberish,
  data: string
) => {
  return iface.encodeFunctionData(
    'relayMessage(uint256,address,address,uint256,uint256,bytes)',
    [nonce, sender, target, value, gasLimit, data]
  )
}

/**
 * Encodes a cross domain message. The version byte in the nonce determines
 * the serialization format that is used.
 *
 * @param nonce     The cross domain message nonce
 * @param sender    The sender of the cross domain message
 * @param target    The target of the cross domain message
 * @param value     The value being sent with the cross domain message
 * @param gasLimit  The gas limit of the cross domain execution
 * @param data      The data passed along with the cross domain message
 */
export const encodeCrossDomainMessage = (
  nonce: BigNumber,
  sender: string,
  target: string,
  value: BigNumber,
  gasLimit: BigNumber,
  data: string
) => {
112 113
  const { version } = decodeVersionedNonce(nonce)
  if (version.eq(0)) {
114
    return encodeCrossDomainMessageV0(target, sender, data, nonce)
115
  } else if (version.eq(1)) {
116 117 118 119 120 121 122 123 124 125 126
    return encodeCrossDomainMessageV1(
      nonce,
      sender,
      target,
      value,
      gasLimit,
      data
    )
  }
  throw new Error(`unknown version ${version.toString()}`)
}