Default RWA Underlying
DefaultRWAUnderlying is a 1:1 ERC-4626 wrapper used as the underlying token of a Gearbox RWA pool. It connects the pool to borrower-level compliance controls without applying those controls to ordinary liquidity providers.
Purpose
RWA markets may need to freeze a borrower's Credit Account while preserving normal pool operations for LPs. DefaultRWAUnderlying provides the token-level enforcement point for that boundary.
The wrapper exists primarily to block token movements involving a frozen Credit Account, including movements used during liquidation. Other operations on a frozen Credit Account are blocked by the configured RWA factory.
It does not represent the borrower's RWA collateral. For example, Securitize DS tokens are held on the borrowing side of the market, while LPs interact with USDC, DefaultRWAUnderlying, and Gearbox pool shares.
ERC-4626 behavior
The wrapper uses the standard ERC-4626 interface:
depositandmintwrap USDC;withdrawandredeemreturn USDC; and- conversions between USDC and wrapper shares are fixed at 1:1.
uint256 wrapperShares = rwaUnderlying.previewDeposit(usdcAmount); uint256 usdcOut = rwaUnderlying.previewRedeem(wrapperShares);
The Gearbox pool has a separate exchange rate between DefaultRWAUnderlying and pool shares. See RWA Pool Deposits and Withdrawals for the complete integration flow.
Freeze scope
Before a wrapper-token transfer, DefaultRWAUnderlying checks both the sender and recipient against its configured RWA factory. The transfer reverts only if an address is both:
- a Credit Account registered by that factory; and
- marked as frozen by the factory.
This check is applied to the transfer's from and to addresses. See _beforeTokenTransfer and _revertIfFrozenCreditAccount in the implementation.
Freeze authority and storage
DefaultRWAUnderlying does not define a freezer role and does not store frozen addresses. It stores only an immutable reference to its configured RWA factory.
In the Securitize implementation, SecuritizeRWAFactory inherits Ownable2Step. Its constructor receives a securitizeAdmin address and sets that address as the initial factory owner. The active factory owner is the only address authorized to call:
setCreditAccountFrozenStatusfor one Credit Account;setAllCreditAccountsFrozenStatus(investor, frozen)for all accounts belonging to an investor; orsetAllCreditAccountsFrozenStatus(creditManager, investor, frozen)for an investor's accounts in one Credit Manager.
Ownership can subsequently be transferred through the factory's two-step ownership process. There is no separately assigned freezer role.
The state is stored in the factory's Credit Account mapping:
mapping(address creditAccount => CreditAccountInfo) internal _creditAccountInfo; struct CreditAccountInfo { address wallet; address investor; bool frozen; }
The relevant storage and authority boundary is:
| Item | Contract | Location |
|---|---|---|
| Active freeze authority | SecuritizeRWAFactory | inherited owner() state |
| Frozen status | SecuritizeRWAFactory | _creditAccountInfo[creditAccount].frozen |
| Factory used for checks | DefaultRWAUnderlying | immutable _FACTORY reference |
When wrapper tokens move, DefaultRWAUnderlying calls isCreditAccount(account) and isFrozen(account) on that factory. It does not copy the frozen status into its own storage.
LPs do not require KYC
An ordinary LP address and the Gearbox pool are not registered borrower Credit Accounts. Consequently, the additional logic in DefaultRWAUnderlying:
- does not create an LP allowlist;
- does not require LP identity registration or KYC; and
- does not provide a mechanism for marking an arbitrary LP address as frozen.
LPs use the standard ERC-4626 deposit and redemption methods. Borrower compliance is handled separately at the Credit Account level.
This boundary applies to the additional freeze logic in DefaultRWAUnderlying. It does not remove controls or risks that exist outside the wrapper, such as issuer-level controls in USDC, a paused Gearbox pool, or insufficient pool liquidity for an immediate withdrawal.
Contract relationships
An integration can verify the wrapper and pool relationship onchain:
require(rwaUnderlying.asset() == usdcAddress, "RWA underlying asset is not USDC"); require(rwaUnderlying.getFactory() == rwaFactoryAddress, "Unexpected RWA factory"); require(pool.asset() == address(rwaUnderlying), "Unexpected pool underlying");
The factory controls borrower Credit Account registration and frozen status. The wrapper only reads that state when enforcing transfers; it does not maintain a separate freeze list.