Skip to content
3F Docs

Request Whitelist

The Request Whitelist is a periphery contract that sits next to the core protocol. It is not part of the Facility / Request / Fund / Position Manager pipeline that moves capital — it exists so that integrators (most importantly bridge facilitators) can verify on-chain that a given Request contract was deployed as part of the 3F flow rather than spoofed by a third party.

RequestWhitelist is a single-purpose attestation registry. For any address a, it returns one of four states through isWhitelisted(address):

enum WhitelistStatus {
NotWhitelisted,
Whitelisted,
PausedNotWhitelisted,
PausedWhitelisted
}

Only Whitelisted should be treated as authorised. The two Paused* variants are returned while a circuit-breaker pause is active and exist so integrators can distinguish “never listed” from “temporarily gated” when surfacing the state to a user.

A Request contract is itself fully self-contained — bridge facilitators settle through it via signed offers and EIP-1271 checks. The whitelist is a separate layer of attestation: it does not change the Request’s behaviour, it lets external parties decide whether they trust this particular Request as an officially-issued one before sending funds or signatures to it.

The Facility deploys a new Request per intent through a factory, so the address of any given Request is not predictable in advance. Without an on-chain attestation, a bridge facilitator integrating programmatically would have to trust an off-chain channel to learn which addresses are legitimate. The whitelist replaces that off-chain trust with an on-chain check that any contract or off-chain script can perform.

In practice, the registry is updated when 3F governance attests that a freshly-deployed Request belongs to a real intent. From that point on, integrators can gate their interaction strictly on isWhitelisted(request) == Whitelisted.

The contract has two parallel write paths that converge on the same storage bit:

  • Owner-direct — a trusted multisig owner can call whitelist(address[]) and unwhitelist(address[]) directly. This is the bootstrap lane.
  • Validator quorum — a configured set of validators can flip the same bit by submitting a quorum of EIP-712 signatures (whitelistBySig / unwhitelistBySig). This is the distributed automation lane.

The registry is built to graduate from the first path to the second: while the owner is set, both paths are live; once renounceOwnership() is called, the owner-direct paths become permanently unreachable and only the quorum path remains. Admin operations on the validator set itself (add, remove, set quorum, set compliance officer) follow the same pattern — direct while the owner is set, quorum-signed afterwards.

A separate compliance officer role can pause the registry for a bounded window. While paused, isWhitelisted reports the Paused* variants and new whitelisting calls revert; revoking attestations stays available so the pause is a circuit breaker rather than a lockout.

A few properties are worth highlighting without going into the full implementation:

  • Single source of truth. Both write paths funnel through the same storage bit and emit the same Whitelisted / Unwhitelisted events, so consumers only ever read isWhitelisted(a).
  • Replay-resistant signatures. Each validator signature is bound to the validator address, a per-validator nonce, and a deadline. A raisable per-validator nonce floor lets a validator mass-invalidate every signature they ever produced below a chosen value, which is the recovery lever if a key is suspected compromised.
  • Removal is permanent. Removing a validator saturates their nonce floor to type(uint256).max, which combined with the bitmap’s “consumed nonces stay consumed” property makes every signature that address ever produced unusable forever — re-admitting the same key cannot resurrect old signatures.
  • One-step capture is blocked. The set always satisfies quorum < validators.length, so a quorum-sized coalition cannot in a single step shrink the validator set down to its own membership.
  • Pause is bounded and one-way extendable. pauseFor(seconds) can only push pausedUntil strictly forward; shortening a pause requires an explicit unpause from the compliance role. Recovery levers (unwhitelist, validator self-invalidate, post-renounce admin paths, owner-only revokeAllRoles) intentionally remain reachable while paused so the breaker cannot lock out the recovery it was built to enable.

Bridge facilitators are the primary consumer of this registry — see Verifying a Request on-chain on the settlement page for the integration recipe and current deployment address.