Author: Mats Heming Julner (Recur Labs)
Status: Draft
Type: Standards Track (Verification)
Created: 2025-11-20
Requires: RIP-001
(Note: “RIP-009” here refers to the Recur Improvement Proposal draft; not yet part of the official Rollup RIP process.)
Abstract
RIP-009 defines the standard for cross-network verification of signed Authorizations.
It specifies how an independent system B reconstructs the exact EIP-712 digest originally signed for system A, and verifies the signature without shared state, consensus, custody, or trust assumptions.
The core contribution is the Cross-System Consent Universality Theorem, which proves:
If two independent systems compute the same
(domainSeparator, structHash)pair,
then a signature overkeccak256(0x1901 || domainSeparator || structHash)
is valid in all systems; regardless of chain, VM, consensus, or execution environment.
This invariant is denoted κ₍c₎, the consent constant.
Motivation
Existing cross-chain systems rely on:
- bridges
- custodians
- oracles
- relayers
- shared consensus
These systems introduce systemic risk, centralization, and multi-billion-dollar exploits.
RIP-009 removes all of these by showing that consent itself is portable.
A signature, once produced, exists in mathematical space, not chain space.
With RIP-009, any system can independently verify consent provided it reconstructs:
- the same
structHash, and - the same
domainSeparator.
This unlocks:
- cross-chain Authorization
- observability without bridges
- chain-to-chain pull flows
- cross-network rebalancing
- multi-domain consent portability
RIP-009 does not define settlement or pull semantics; those are covered by RIP-001, RIP-002, and RIP-003/004.
—-
Specification Overview
Authorization Structure (RIP-001)
All fields and their ordering are inherited exactly from RIP-001.
grantor: address
grantee: address
token: address
maxPerPull: uint256
validAfter: uint256
validBefore: uint256
nonce: bytes32
signature: bytes
AUTH_TYPEHASH
AUTH_TYPEHASH = keccak256(
"Authorization(address grantor,address grantee,address token,uint256 maxPerPull,uint256 validAfter,uint256 validBefore,bytes32 nonce)"
);
Struct Hash
structHash = keccak256(
abi.encode(
AUTH_TYPEHASH,
grantor,
grantee,
token,
maxPerPull,
validAfter,
validBefore,
nonce
)
);
Note: This is a minimal reference verifier. Production implementations SHOULD enforce low-s signatures (EIP-2) and MAY support EIP-1271 smart wallets and EIP-2098 compact signatures.
Domain Separator (EIP-712)
EIP712_DOMAIN_TYPEHASH = keccak256(
"EIP712Domain(string name,string version,address verifyingContract,uint256 chainId)"
);
domainSeparator = keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256(bytes(name)),
keccak256(bytes(version)),
verifyingContract,
chainId
)
);
Systems interoperate only if domainSeparator is identical.
Cross-System Consent Universality Theorem
Statement
If two independent systems reconstruct:
domainSeparator = DstructHash = S
Then for:
digest = keccak256(0x1901 || D || S)
we have:
Verify_A(digest, σ) == Verify_B(digest, σ)
for any ECDSA secp256k1 signature σ.
Signature validity is independent of:
- chain
- VM
- consensus
- block time
- global state
The pair (D, S) is the invariant κ₍c₎.
Proof Sketch
- EIP-712 digest = deterministic hash over explicit bytes
- Signature is over digest only
- Signature verification reduces to checking recovery of the signer
- ECDSA verification depends only on
(digest, σ, pk) - Therefore, if digest is equal, signature validity is equal
- Thus consent verification is invariant across systems
Full formal proof included in Appendix X.
Observe-and-Verify Flow
System B performs:
- Receive Authorization fields
- Recompute structHash
- Recompute domainSeparator
- Compute digest
- Verify signature via
ecrecover - If recovered signer equals grantor → consent is valid
No bridge, oracle, or shared state is needed.
Reference Implementation
pragma solidity ^0.8.0;
contract RecurCrossNetworkVerifier {
bytes32 public constant AUTH_TYPEHASH =
keccak256(
"Authorization(address grantor,address grantee,address token,uint256 maxPerPull,uint256 validAfter,uint256 validBefore,bytes32 nonce)"
);
bytes32 public immutable DOMAIN_SEPARATOR;
constructor(
string memory name,
string memory version,
address verifyingContract,
uint256 chainId
) {
bytes32 EIP712_DOMAIN_TYPEHASH = keccak256(
"EIP712Domain(string name,string version,address verifyingContract,uint256 chainId)"
);
DOMAIN_SEPARATOR = keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256(bytes(name)),
keccak256(bytes(version)),
verifyingContract,
chainId
)
);
}
function verify(
address grantor,
address grantee,
address token,
uint256 maxPerPull,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes memory signature
) public view returns (bool) {
bytes32 structHash = keccak256(
abi.encode(
AUTH_TYPEHASH,
grantor,
grantee,
token,
maxPerPull,
validAfter,
validBefore,
nonce
)
);
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
structHash
)
);
(bytes32 r, bytes32 s, uint8 v) = _split(signature);
return ecrecover(digest, v, r, s) == grantor;
}
function _split(bytes memory sig)
internal pure returns (bytes32 r, bytes32 s, uint8 v)
{
require(sig.length == 65, "BAD_SIG");
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
}
}
Formal Derivation
Appendix X provides a full formal derivation of the Cross-System Consent Universality Theorem, with lemmas on:
- encoding determinism
- digest equivalence
- ECDSA invariance
This demonstrates that consent validity is network-agnostic as long as (domainSeparator, structHash) are preserved.
Request for Comments
RIP-009 is intended to become a foundational standard for:
- cross-chain consent
- multi-domain Authorization
- off-chain verifiers
- multi-chain payment flows
- programmable treasury systems
Feedback requested on:
- boundary conditions
- domain separator namespacing
- revocation semantics
- cross-network replay rules
- off-chain verifier behavior
Reference
https://github.com/recurmj/recur-standard/blob/main/docs/RIP-009.md