CCIP v2.0.0 RMNRemote API Reference
Summary
RMNRemote verifies RMN reports for Any2EVM OffRamps, maintains curse state used to gate message flows, and provides a legacy [IRMN](/ccip/evm/api-reference/v2.0.0/i-rmn) passthrough for isBlessed.
It implements both:
[IRMNRemote](/ccip/evm/api-reference/v2.0.0/i-rmn-remote)IRMN(legacy interface)
to support migration from pre-1.6 flows.
Core responsibilities:
- Verify RMN reports via threshold ECDSA signatures.
- Maintain per-subject and global curse state.
- Expose legacy
isBlessedpassthrough when a legacy RMN is configured.
Contract
chains/evm/contracts/rmn/RMNRemote.sol
Import
import {RMNRemote} from "chainlink-ccip/chains/evm/contracts/rmn/RMNRemote.sol";
Inheritance
Ownable2StepMsgSenderITypeAndVersionIRMNRemoteIRMN
typeAndVersion
string public constant override typeAndVersion = "RMNRemote 1.6.0";
Returns:
"RMNRemote 1.6.0"
State
Constants
GLOBAL_CURSE_SUBJECT
bytes16 constant GLOBAL_CURSE_SUBJECT = 0x01000000000000000000000000000001;
If this subject is cursed, isCursed() and isCursed(bytes16) return true.
RMN_V1_6_ANY2EVM_REPORT
bytes32 private constant RMN_V1_6_ANY2EVM_REPORT =
keccak256("RMN_V1_6_ANY2EVM_REPORT");
Used as the header when computing the report digest.
ECDSA_RECOVERY_V
uint8 private constant ECDSA_RECOVERY_V = 27;
RMN nodes generate signatures with v = 27.
Immutables
uint64 internal immutable i_localChainSelector;
IRMN internal immutable i_legacyRMN;
Storage
Config private s_config;
uint32 private s_configCount;
EnumerableSet.Bytes16Set private s_cursedSubjects;
mapping(address signer => bool exists) private s_signers;
Constructor
constructor(uint64 localChainSelector, IRMN legacyRMN)
Reverts:
ZeroValueNotAllowed()iflocalChainSelector == 0.
Initializes:
i_localChainSelectori_legacyRMN
External API
Verification
verify
function verify(
address offRampAddress,
Internal.MerkleRoot[] calldata merkleRoots,
Signature[] calldata signatures
) external view
Verifies an RMN Any2EVM report.
Reverts:
ConfigNotSet()ifs_configCount == 0ThresholdNotMet()ifsignatures.length < s_config.fSign + 1InvalidSignature()ifecrecover(...) == address(0)OutOfOrderSignatures()if recovered signer addresses are not strictly increasingUnexpectedSigner()if recovered signer not present ins_signers
Digest computation:
digest = keccak256(
abi.encode(
RMN_V1_6_ANY2EVM_REPORT,
Report({
destChainId: block.chainid,
destChainSelector: i_localChainSelector,
rmnRemoteContractAddress: address(this),
offrampAddress: offRampAddress,
rmnHomeContractConfigDigest: s_config.rmnHomeContractConfigDigest,
merkleRoots: merkleRoots
})
)
)
Configuration
setConfig
function setConfig(Config calldata newConfig) external onlyOwner
Reverts:
ZeroValueNotAllowed()ifrmnHomeContractConfigDigest == bytes32(0)InvalidSignerOrder()ifnodeIndexnot strictly increasingNotEnoughSigners()ifsigners.length < 2 * fSign + 1DuplicateOnchainPublicKey()if duplicate signer keys
Effects:
- Clears previous signer set
- Sets new signer set
- Stores
s_config = newConfig - Increments
s_configCount - Emits
ConfigSet(version, newConfig)
getVersionedConfig
function getVersionedConfig()
external
view
returns (uint32 version, Config memory config)
Returns:
version = s_configCountconfig = s_config
getLocalChainSelector
function getLocalChainSelector()
external
view
returns (uint64)
Returns i_localChainSelector.
getReportDigestHeader
function getReportDigestHeader()
external
pure
returns (bytes32)
Returns RMN_V1_6_ANY2EVM_REPORT.
Cursing
curse(bytes16)
Wrapper that calls curse(bytes16[] memory).
curse(bytes16[])
function curse(bytes16[] memory subjects) public onlyOwner
For each subject:
- Reverts
AlreadyCursed(subject)if already cursed.
Adds to s_cursedSubjects.
Emits Cursed(subjects).
uncurse(bytes16)
Wrapper that calls uncurse(bytes16[] memory).
uncurse(bytes16[])
function uncurse(bytes16[] memory subjects) public onlyOwner
For each subject:
- Reverts
NotCursed(subject)if not present.
Removes from s_cursedSubjects.
Emits Uncursed(subjects).
getCursedSubjects
function getCursedSubjects()
external
view
returns (bytes16[] memory)
Returns all cursed subjects.
isCursed()
function isCursed()
external
view
override(IRMN, IRMNRemote)
returns (bool)
Returns:
falseif no subjects cursed- otherwise
trueifGLOBAL_CURSE_SUBJECTpresent
isCursed(bytes16)
function isCursed(bytes16 subject)
external
view
override(IRMN, IRMNRemote)
returns (bool)
Returns:
falseif no subjects cursed- otherwise
trueifsubjectorGLOBAL_CURSE_SUBJECTis present
Legacy passthrough
isBlessed
function isBlessed(TaggedRoot calldata taggedRoot)
external
view
returns (bool)
Reverts:
IsBlessedNotAvailable()ifi_legacyRMN == address(0)
Otherwise returns:
i_legacyRMN.isBlessed(taggedRoot)
Events
event ConfigSet(uint32 indexed version, Config config);
event Cursed(bytes16[] subjects);
event Uncursed(bytes16[] subjects);
Errors
error AlreadyCursed(bytes16 subject);
error ConfigNotSet();
error DuplicateOnchainPublicKey();
error InvalidSignature();
error InvalidSignerOrder();
error NotEnoughSigners();
error NotCursed(bytes16 subject);
error OutOfOrderSignatures();
error ThresholdNotMet();
error UnexpectedSigner();
error ZeroValueNotAllowed();
error IsBlessedNotAvailable();
Structs
Signer
struct Signer {
address onchainPublicKey;
uint64 nodeIndex;
}
Config
struct Config {
bytes32 rmnHomeContractConfigDigest;
Signer[] signers;
uint64 fSign;
}
fSign defines the maximum number of faulty nodes.
Verification requires at least fSign + 1 valid signatures.
Total configured signers must be at least 2 * fSign + 1.
Report
struct Report {
uint256 destChainId;
uint64 destChainSelector;
address rmnRemoteContractAddress;
address offrampAddress;
bytes32 rmnHomeContractConfigDigest;
Internal.MerkleRoot[] merkleRoots;
}
Used only for hashing and digest computation; not stored.
Internal Functions
No additional internal helper functions are declared beyond those described above.
Security model
Trust boundaries
Owner:
- Controls RMN configuration.
- Controls curse state.
Signers:
- Must meet threshold (
fSign + 1). - Must be strictly ordered and unique.
Legacy RMN:
- Used only for
isBlessedpassthrough when configured.
Verification invariants
- Signatures must be strictly increasing by recovered signer address.
- Signers must match configured set.
- At least
fSign + 1valid signatures required. - Config must be set before verification.
Curse gating
- Global curse overrides per-subject state.
- Curse state is independent from configuration.
- Removing a subject requires explicit
uncurse.