FraxNet
Contracts
FraxNet Deposit Factory

FraxNetDepositFactory

Overview

FraxNetDepositFactory is a factory smart contract used to deploy FraxNetDeposit contracts in a deterministic and gas-efficient manner. It integrates with external services like LayerZero (for cross-chain messaging), Circle's CCTP (for USDC transfer), and the Frax RWA custodian system.

This contract serves as the implementation for an EIP-1967 (opens in a new tab) Transparent Proxy.

Key Features

  • Beacon Proxy-based deployment, for child contracts
  • Deterministic contract creation with CREATE2
  • Cross-chain setup using LayerZero and Circle CCTP
  • Dual-path redemption (Custodian or RWA)

Initialization

The factory must be initialized once by calling the initialize(...) function with the following parameters:

function initialize(
    IFrxUSDCustodian _frxUSDCustodian,
    IBrandedFactory _brandFactory,
    IRemoteHop _remoteHop,
    address _cctpTokenMessenger,
    IRWAUSDCRedeemer _rwaUsdcRedeemer
) external;

This sets up dependencies, deploys an initial FraxNetDeposit implementation with the FraxBeacon that deposit contracts read it from, and fills eidToCCTPDomain with default CCTP domains. It does not set cctpTokenMessengerV2, which the owner sets with setCCTPTokenMessengerV2. The Ethereum factory proxy is already initialized.

Public Variables

NameTypeDescription
wasInitializedboolEnsures factory is initialized only once
frxUSDCustodianIFrxUSDCustodianCustodian that mints frxUSD from USDC and redeems it back to USDC
remoteHopIRemoteHopCross-chain communication interface
fraxNetDepositContractsIFraxNetDeposit[]All deployed contracts
isFraxNetDepositmapping(address => bool)Verifies FraxNetDeposit contract
operatorsmapping(address => bool)Accounts that can pause and unpause deposit processing
rwaRedeemerIRWAUSDCRedeemerRWA-based USDC redemption pathway for amounts the custodian cannot cover
cctpTokenMessengeraddressCircle CCTP V1 token messenger, not used by the current deposit implementation
eidToCCTPDomainmapping(uint32 => uint32)L0 EID to CCTP domain mapping
rwaRedemptionThresholduint256Redemption threshold for using RWA, not used by the current deposit implementation
beaconContractFraxBeaconBeacon for FraxNetDeposit proxy logic
cctpTokenMessengerV2addressCircle CCTP V2 token messenger that redemptions burn USDC through
isPausedboolPauses processDeposit and processRedemption on every deposit contract
brandFactoryIBrandedFactoryBrandedFactory that registers branded stablecoins

Events

NameDescription
FraxNetDepositCreatedEmitted when a new deposit contract is deployed
FrxUSDCustodianSetEmitted on custodian address update
BrandedFactorySetEmitted on branded factory update
RemoteHopSetEmitted on remote hop update
OperatorSetEmitted on operator permission change
RwaRedemptionThresholdSetEmitted on redemption threshold update
CCTPTokenMessengerSetEmitted on CCTP V1 messenger update
RwaRedeemerSetEmitted on redeemer update
LayerZeroEidToCCTPDomainSetEmitted on EID-to-domain mapping change
CCTPTokenMessengerV2SetEmitted on CCTP V2 messenger update
IsPausedEmitted when deposit processing is paused or unpaused

Functions

Deployment

function createFraxNetDeposit(uint32 _targetEid, bytes32 _targetAddress, bytes32 _targetUsdcAtaAddress) external returns (IFraxNetDeposit);

Deploys a new FraxNetDeposit contract to a specific LayerZero destination.

function getDeploymentAddress(uint32 _targetEid, bytes32 _targetAddress, bytes32 _targetUsdcAtaAddress) external view returns (address);

Predicts the deployment address of a future contract.

_targetUsdcAtaAddress is the recipient's USDC associated token account when _targetEid is Solana (30168), and must be zero for every other chain. The call reverts with AtaAccountNotInitialized if it is zero for Solana, or AtaAccountShouldBeNull if it is set for another chain. The factory does not otherwise check the account, and the value cannot be changed after deployment. Redemptions to Solana pay USDC into it, so derive it from the recipient's wallet and the USDC mint, as shown in Solana recipients.

function createBrandedFraxNetDeposit(address _asset, uint32 _targetEid, bytes32 _targetAddress, bytes32 _targetUsdcAtaAddress) external returns (IFraxNetDeposit);

Deploys a FraxNetDeposit contract for a branded stablecoin: processed deposits deliver _asset instead of frxUSD, and redemptions take _asset. It reverts with AssetNotRecognized if _asset is not registered in the BrandedFactory, and with NoAllowedForSiloed if the asset uses a siloed custodian.

function getBrandedDeploymentAddress(address _asset, uint32 _targetEid, bytes32 _targetAddress) external view returns (address);

Predicts the deployment address of a branded deposit contract. It does not check that _asset is registered.

Configuration

function setFrxUSDCustodian(IFrxUSDCustodian _frxUSDCustodian) external onlyOwner;
function setBrandedFactory(IBrandedFactory _brandFactory) external onlyOwner;
function setRemoteHop(IRemoteHop _remoteHop) external onlyOwner;
function setOperator(address operator, bool isOp) external onlyOwner;
function setRwaRedemptionThreshold(uint256 newThreshold) external onlyOwner;
function setRwaRedeemer(IRWAUSDCRedeemer newRwaRedeemer) external onlyOwner;
function setCCTPTokenMessenger(address newTokenMessenger) external onlyOwner;
function setCCTPTokenMessengerV2(address newTokenMessenger) external onlyOwner;
function setEidToCCTPDomain(uint32 _eid, uint32 _cctpDomain) external onlyOwner;

Admin functions to update contract dependencies and behavior. Redemptions burn USDC through the messenger set with setCCTPTokenMessengerV2. setCCTPTokenMessenger sets the CCTP V1 messenger, which the current deposit implementation does not use.

function setPaused(bool state) external;

Pauses (true) or unpauses (false) processDeposit and processRedemption on every FraxNetDeposit contract. Only addresses enabled with setOperator can call it. Other callers get NotOperator, including the owner unless it is also set as an operator.

Queries

function fraxNetDepositContractsLength() external view returns (uint256);
function getCustodianAndHop() external view returns (IFrxUSDCustodian, IRemoteHop);
function isOperator(address operator) external view returns (bool);
function version() external pure returns (uint256 major, uint256 minor, uint256 patch);

Useful queries for state inspection. isOperator returns true for the owner as well as for operators.

Emergency Recovery

function recoverERC20(
    address tokenAddress,
    uint256 tokenAmount,
    IFraxNetDeposit fraxNetDepositContract,
    address recipient
) external onlyOwner;

Allows the owner to recover tokens stuck in a FraxNetDeposit contract.

Errors

NameDescription
Create2Failed()Thrown when CREATE2 deployment fails, such as when the deposit contract already exists
AlreadyInitialized()Thrown on double initialization attempt
AtaAccountNotInitialized()Thrown when _targetEid is Solana and _targetUsdcAtaAddress is zero
AtaAccountShouldBeNull()Thrown when _targetEid is not Solana and _targetUsdcAtaAddress is not zero
NotOperator()Thrown when setPaused is called by an address that is not an operator
NotSupported()Thrown when a branded deposit targets Solana
AssetNotRecognized(address)Thrown when _asset is not a branded stablecoin registered in the BrandedFactory

Deployment Behavior

createFraxNetDeposit deploys each FraxNetDeposit contract with the CREATE2 opcode. The salt depends on _targetEid.

For every chain except Solana, _targetUsdcAtaAddress must be zero and is not part of the salt:

keccak256(abi.encode(_targetEid, _targetAddress))

For Solana (_targetEid == 30168), the recipient's USDC associated token account is also part of the salt:

keccak256(abi.encode(abi.encode(_targetEid, _targetAddress), _targetUsdcAtaAddress))

Each deposit address is therefore deterministic: one per target chain and recipient, plus the USDC token account for Solana. getDeploymentAddress applies the same rules, so use it to get the address instead of computing it yourself.

createBrandedFraxNetDeposit uses a separate salt that also covers the branded asset:

keccak256(abi.encode(_asset, _targetEid, _targetAddress))

Branded deposits do not support Solana: _targetEid cannot be 30168 (NotSupported), and _targetUsdcAtaAddress must be zero (AtaAccountShouldBeNull). getBrandedDeploymentAddress applies the same salt. If the branded deposit contract already exists, createBrandedFraxNetDeposit reverts with no error data instead of Create2Failed.

Dependencies

Notes

  • The factory is Ownable and uses OpenZeppelin's Ownable2Step for ownership transfer
  • Redemptions go through the USDC custodian first and the RWA redeemer for any remainder; rwaRedemptionThreshold is not used by the current deposit implementation
  • L0 EIDs and CCTP domains must match the same chain

Author: Frax Finance License: AGPL-3.0-only