# PYUSDx Compliance & Security

> PYUSDx compliance primitives and security model: roles, account freezing, forced transfers, pausing, upgradeability, and reentrancy protection.

## Roles

Access to PYUSDx operations is governed by role-based access control:

<table>
<thead>
  <tr>
    <th>
      Role
    </th>
    
    <th>
      Holder
    </th>
    
    <th>
      Powers
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        DEFAULT_ADMIN_ROLE
      </code>
    </td>
    
    <td>
      Admin
    </td>
    
    <td>
      Grant/revoke all roles, set earner manager
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ISSUER_ROLE
      </code>
    </td>
    
    <td>
      <code>
        IssuerGateway
      </code>
    </td>
    
    <td>
      <code>
        mint
      </code>
      
      , <code>
        burn
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        PAUSER_ROLE
      </code>
    </td>
    
    <td>
      Pauser
    </td>
    
    <td>
      Pause/unpause all transfers
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        FREEZE_MANAGER_ROLE
      </code>
    </td>
    
    <td>
      Freeze manager
    </td>
    
    <td>
      Freeze/unfreeze accounts
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        FORCED_TRANSFER_MANAGER_ROLE
      </code>
    </td>
    
    <td>
      Forced transfer manager
    </td>
    
    <td>
      Seize funds from frozen accounts
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        RATE_LIMIT_MANAGER_ROLE
      </code>
    </td>
    
    <td>
      Rate limit manager
    </td>
    
    <td>
      Set per-issuer rate limits
    </td>
  </tr>
</tbody>
</table>

The earner manager is a single address (not a role) that controls the yield system; see [Token & yield](/protocol/pyusdx/token-and-yield#earner-manager).

### Access-control hierarchy

- `DEFAULT_ADMIN_ROLE` can grant or revoke all roles, set the earner manager (PYUSDx), set the fallback recipient (Portal), and set the mint delay/TTL (IssuerGateway).
- `ISSUER_ROLE` (held by the `IssuerGateway`) can `mint()` and `burn()` on PYUSDx.
- The earner manager can `setAccountInfo()` to configure earners, `distributeReward()` to mint to any account, and receives yield fees.

## Freeze system

- Accounts can be frozen and unfrozen by `FREEZE_MANAGER_ROLE`.
- Frozen accounts cannot transfer, receive, or claim yield, and cannot be party to an allowance grant: `approve` and EIP-2612 `permit` revert when either the owner or the spender is frozen.
- Freezing an earner automatically claims yield and stops earning first.
- Forced transfers can seize funds from frozen accounts.

## Forced transfer

`_forceTransfer(frozenAccount, recipient, amount)`:

- Is only callable by `FORCED_TRANSFER_MANAGER_ROLE`.
- Requires `frozenAccount` to be frozen and `recipient` to not be frozen.
- Frozen accounts are always non-earning (freeze stops earning first).
- Subtracts from the frozen account's balance and adds to the recipient (earner-aware).
- The seizable amount is the full `balance`, including any yield that materialized into the account at freeze time via `_beforeFreeze` (see [pause/freeze-time yield behavior](/protocol/pyusdx/token-and-yield#pause-freeze-time-yield-behavior)).

## Pause system

- **PYUSDx**: a single global pause blocks all transfers, mints, burns, and claims. `setAccountInfo` remains callable as an emergency lever (with yield materialization to the earner's own balance).
- **Portal**: separate send and receive pause states (see [Cross-chain (Portal)](/protocol/pyusdx/bridging#pause-model)).
- **SwapFacility / Extensions**: a single global pause per contract.

### Known pause-time behaviors

1. `setAccountInfo` while paused: yield materializes to the earner's balance, and the fee is forfeited.
2. `claimYield` on `YieldToOne`: succeeds while paused, but the minted tokens cannot transfer until unpause.
3. `Portal` send and receive are independently pausable.
4. All standard transfers are blocked during pause.

## Upgradeability

All core contracts (PYUSDx, `IssuerGateway`, `Portal`, `SwapFacility`, `ExtensionFactory`, `ExtensionBeacon`, `LayerZeroBridgeAdapter`) sit behind OpenZeppelin's `TransparentUpgradeableProxy`, deployed deterministically through CREATE3. Each proxy has a dedicated `ProxyAdmin` that owns the upgrade entry point; calls from the admin are routed to the proxy's admin surface, and all other calls fall through to the implementation.

- Implementation contracts call `_disableInitializers()` in the constructor.
- Initializer functions use the `initializer` or `onlyInitializing` modifiers and explicitly chain every inherited `__X_init` initializer (for example `__AccessControl_init`), so no parent initialization is skipped.
- Storage uses ERC-7201 namespaced locations to avoid collisions across upgrades.

Extensions (`YieldToOne`, `MultiMint`) use the beacon proxy pattern instead. Each extension type has its own beacon, so a beacon upgrade atomically rolls all instances of that type forward (subject to per-extension version pinning; see [Extensions](/protocol/pyusdx/extensions#beacon-upgrade-and-pinning)).

## Reentrancy protection

Two mechanisms protect against reentrancy:

1. **ReentrancyLock** (transient storage): used by `Portal` and `SwapFacility`. It stores `msg.sender` as the lock, enabling caller resolution through the guard.
2. **Checks-Effects-Interactions**: all state changes occur before external calls.

## Cross-chain security

- Message IDs prevent replay attacks across chains: each `messageId` can be processed at most once on the destination chain.
- Bridge adapters must be explicitly registered per destination chain.
- The fallback recipient prevents frozen-account reverts from stranding bridged funds.
- Failed wraps gracefully degrade to a direct PYUSDx transfer.

Deployed contract addresses are listed on the [PYUSDx platform deployments](/resources/addresses/pyusdx-platform) page.
