In-Order Delivery

Here is an example that sends and receives cross-chain messages with guaranteed in-order delivery.
1
// a simple example to enforce in-order message delivery
2
contract MsgExampleInOrder is MessageApp {
3
event MessageReceived(
4
address srcContract,
5
uint64 srcChainId,
6
address sender,
7
uint64 seq,
8
bytes message
9
);
10
11
// map at source chain. (dstChainId, dstContract) -> seq
12
mapping(uint64 => mapping(address => uint64)) public sendSeq;
13
14
// map at destination chain (srcChainId, srcContract) -> seq
15
mapping(uint64 => mapping(address => uint64)) public recvSeq;
16
17
constructor(address _messageBus) MessageApp(_messageBus) {}
18
19
// called by user on source chain to send cross-chain message
20
function sendMessage(
21
address _dstContract,
22
uint64 _dstChainId,
23
bytes calldata _message
24
) external payable {
25
uint64 seq = sendSeq[_dstChainId][_dstContract];
26
bytes memory message = abi.encode(msg.sender, seq, _message);
27
sendMessage(_dstContract, _dstChainId, message, msg.value);
28
sendSeq[_dstChainId][_dstContract] += 1;
29
}
30
31
// called by MessageBus on destination chain to receive message
32
function executeMessage(
33
address _srcContract,
34
uint64 _srcChainId,
35
bytes calldata _message,
36
address // executor
37
) external payable override onlyMessageBus returns (ExecutionStatus) {
38
(address sender, uint64 seq, bytes memory message) = abi.decode(
39
(_message),
40
(address, uint64, bytes)
41
);
42
uint64 expectedSeq = recvSeq[_srcChainId][_srcContract];
43
if (seq != expectedSeq) {
44
// sequence number not expected, let executor retry.
45
// Note: cannot revert here, because once a message execute tx is
46
// reverted, it cannot be retried later.
47
return ExecutionStatus.Retry;
48
}
49
emit MessageReceived(_srcContract, _srcChainId, sender, seq, message);
50
recvSeq[_srcChainId][_srcContract] += 1;
51
return ExecutionStatus.Success;
52
}
53
}