// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; /// @title AereUSDC — Foundation-attested bridged USDC on AERE chain 2800 /// @notice Each AereUSDC is 1:1-backed by a USDC reserve held by AERE Foundation on /// Ethereum mainnet. The Foundation address (currently a Trust Wallet receiver, /// eventually a multisig) holds the real USDC. AereUSDC is minted on chain 2800 /// when the off-chain bridge watcher attests a deposit; burned when the user /// requests withdrawal (Phase 2 — currently manual). /// /// Decimals: 6 (matches Ethereum USDC, so 1 USDC = 1 AereUSDC numerically). /// Symbol: aUSDC. Name: "Aere-Bridged USDC". /// /// Roles: /// - DEFAULT_ADMIN_ROLE: Foundation, sets bridge address, can pause minting in emergency. /// - MINTER_ROLE: AereBridge contract only. Foundation grants on deploy. /// - BURNER_ROLE: AereBridge contract (Phase 2 — Foundation will add for withdrawal flow). /// /// Honest framing: this is a CUSTODIAL bridged asset. Foundation can theoretically /// mint unbacked tokens or refuse withdrawal. Phase 2 wires trustless settlement /// (Across/Hyperlane) when funds available for sister-chain deploys. contract AereUSDC is ERC20, AccessControl { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); bool public mintPaused; event MintPaused(bool paused); error MintingPaused(); /// @param initialAdmin The address that gets DEFAULT_ADMIN_ROLE at deploy time. /// Typically the deployer, who then grants MINTER to bridge, /// transfers admin to Foundation, and renounces own admin — /// all in the same deploy script. constructor(address initialAdmin) ERC20("Aere-Bridged USDC", "aUSDC") { _grantRole(DEFAULT_ADMIN_ROLE, initialAdmin); _grantRole(PAUSER_ROLE, initialAdmin); } function decimals() public pure override returns (uint8) { return 6; } /// @notice Mint AereUSDC backed by an attested Ethereum USDC deposit. /// @dev Only callable by the AereBridge contract (MINTER_ROLE). function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) { if (mintPaused) revert MintingPaused(); _mint(to, amount); } /// @notice Burn AereUSDC (for withdrawal back to Ethereum USDC). /// @dev Phase 2 — currently bridge has BURNER_ROLE for symmetry but withdrawal /// processing is manual via Foundation. function burn(address from, uint256 amount) external onlyRole(BURNER_ROLE) { _burn(from, amount); } /// @notice Emergency pause for new mints. Existing balances are unaffected. function setMintPaused(bool paused) external onlyRole(PAUSER_ROLE) { mintPaused = paused; emit MintPaused(paused); } }