// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; /** * @title AereBridgedToken — generic bridged-asset token for AERE Warp Routes * @notice One contract, many stablecoins. Replaces the USDC.e-only USDCe.sol * with a parameterised version so that USDT.e, USDe.e, EURC.e (and * future bridged assets) share the same audited bytecode. * * The corresponding AereHypERC20Synthetic on AERE is set immutably * at construction and is the SOLE address that can mint or burn. * No admin, no pauser, no blacklist — those features add attack * surface and we publish reserve commitments via AereNavOracle * instead. * * Decimal handling: each constituent stablecoin keeps its native * decimal count (USDC.e, USDT.e, EURC.e: 6 ; USDe.e: 18). The * decimals are an immutable constructor arg, not OZ's default 18. * * AUDIT NOTE: identical surface area to USDCe.sol; same audit * reasoning applies. The only change is parametric name/symbol/ * decimals. Both contracts can coexist on chain — USDC.e remains * pointing at the deployed USDCe.sol instance for historical * continuity; new stables use AereBridgedToken. */ contract AereBridgedToken is ERC20 { /// @notice The one and only contract authorised to mint or burn. address public immutable BRIDGE; /// @notice Decimal count, captured immutably at deploy. uint8 private immutable _DECIMALS; error OnlyBridge(); modifier onlyBridge() { if (msg.sender != BRIDGE) revert OnlyBridge(); _; } constructor(string memory name_, string memory symbol_, uint8 decimals_, address bridge_) ERC20(name_, symbol_) { require(bridge_ != address(0), "AereBridgedToken: zero bridge"); BRIDGE = bridge_; _DECIMALS = decimals_; } function decimals() public view override returns (uint8) { return _DECIMALS; } function mint(address to, uint256 amount) external onlyBridge { _mint(to, amount); } /// AUDIT FIX (HIGH #15): even though BRIDGE is the only authorised caller, /// require the user to have explicitly approved the bridge for the burn /// amount. A bridge bug / compromise can no longer wipe arbitrary balances /// — only what users explicitly allowed. function burnFrom(address from, uint256 amount) external onlyBridge { if (from != msg.sender) { _spendAllowance(from, msg.sender, amount); } _burn(from, amount); } }