Example: Message Proof

Here is a simple proof-of-concept message bridge built on top of ZK slot value proof. For a full-fledged cross-chain message bridge, please refer to the previous section.
The source chain sender contract has a map to store the hash of all messages, with a counter nonce as the map key. Proof of a sent message is equivalent to the proof of a map storage value.
// source chain contract
contract MsgSender {
uint64 public nonce; // slot 0
// slot 1, map of (nonce -> msgHash)
// each msgHash is stored in slot keccak256(abi.encode(nonce, 1))
mapping(uint64 => bytes32) public sent;
event MsgSent(uint64 nonce, address sender, bytes msg);
function sendMsg(bytes calldata _msg) external {
bytes32 msgHash = keccak256(abi.encodePacked(msg.sender, _msg));
sent[nonce] = msgHash;
emit MsgSent(nonce++, msg.sender, _msg);
The destination chain receiver contract connects to the Brevis slot value verifier contract to validate that a given <nonce, sender, message> tuple can match the map storage slot value in the source chain sender contract.
// destination chain contract
contract MsgReceiver {
uint64 public senderChainId;
bytes32 public senderContractHash;
ISlotValueVerifier public slotVerifier;
event MsgReceived(uint64 nonce, address sender, bytes msg);
ISlotValueVerifier _verifier,
uint64 _senderChainId,
address _senderContract
) {
slotVerifier = _verifier;
senderChainId = _senderChainId;
senderContractHash = keccak256(abi.encodePacked(_senderContract));
function recvMsg(
uint64 _nonce,
address _sender,
bytes calldata _msg,
bytes calldata _proofData,
bytes calldata _blkVerifyInfo
) external {
// compute expected slot and msg hash, sender map slot is 1
bytes32 slotKeyHash = keccak256(abi.encode(keccak256(abi.encode(_nonce, 1))));
bytes32 msgHash = keccak256(abi.encodePacked(_sender, _msg));
// retrieve zk verified slot info
ISlotValueVerifier.SlotInfo memory slotInfo = slotVerifier.verifySlotValue(
// compare expected and verified values
require(slotInfo.slotKeyHash == slotKeyHash, "slot key not match");
require(slotInfo.slotValue == msgHash, "slot value not match");
require(slotInfo.addrHash == senderContractHash, "sender contract not match");
emit MsgReceived(_nonce, _sender, _msg);