1
2
3
4
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
34
35
36
37
38
39
40
41
42
43
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
112
113
114
115
116
117
118
119
120
import { ethers, BigNumberish, BigNumber } from 'ethers'
const iface = new ethers.utils.Interface([
'function relayMessage(address,address,bytes,uint256)',
'function relayMessage(uint256,address,address,uint256,uint256,bytes)',
])
const nonceMask = BigNumber.from(
'0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
)
export const big0 = BigNumber.from(0)
export const big1 = BigNumber.from(1)
/**
* 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
*/
export const decodeVersionedNonce = (nonce: BigNumber): BigNumber[] => {
return [nonce.and(nonceMask), nonce.shr(240)]
}
/**
* 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
) => {
const [, version] = decodeVersionedNonce(nonce)
if (version.eq(big0)) {
return encodeCrossDomainMessageV0(target, sender, data, nonce)
} else if (version.eq(big1)) {
return encodeCrossDomainMessageV1(
nonce,
sender,
target,
value,
gasLimit,
data
)
}
throw new Error(`unknown version ${version.toString()}`)
}