// SPDX-License-Identifier: MIT pragma solidity 0.8.35; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; contract BankV1 is Initializable, UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeable, ReentrancyGuardTransient { using SafeERC20 for IERC20; error InvalidAsset(address asset); error ZeroAmount(); error InsufficientBalance(address account, uint256 available, uint256 requested); error UnexpectedAssetDelta(uint256 expected, uint256 actual); event Deposited(address indexed account, uint256 amount); event Withdrawn(address indexed account, uint256 amount); IERC20 internal _asset; mapping(address account => uint256 balance) internal _balances; uint256 internal _totalLiabilities; uint256[47] private __gap; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize(address asset_, address initialOwner) external initializer { if (asset_ == address(0)) { revert InvalidAsset(asset_); } __Ownable_init(initialOwner); __Pausable_init(); _asset = IERC20(asset_); } function pause() external onlyOwner { _pause(); } function unpause() external onlyOwner { _unpause(); } function deposit(uint256 amount) external whenNotPaused nonReentrant { if (amount == 0) revert ZeroAmount(); uint256 reservesBefore = _asset.balanceOf(address(this)); _asset.safeTransferFrom(msg.sender, address(this), amount); uint256 reservesAfter = _asset.balanceOf(address(this)); uint256 received = reservesAfter >= reservesBefore ? reservesAfter - reservesBefore : 0; if (received != amount) revert UnexpectedAssetDelta(amount, received); _balances[msg.sender] += amount; _totalLiabilities += amount; emit Deposited(msg.sender, amount); } function withdraw(uint256 amount) external whenNotPaused nonReentrant { if (amount == 0) revert ZeroAmount(); uint256 available = _balances[msg.sender]; if (amount > available) revert InsufficientBalance(msg.sender, available, amount); _balances[msg.sender] = available - amount; _totalLiabilities -= amount; _asset.safeTransfer(msg.sender, amount); emit Withdrawn(msg.sender, amount); } function asset() external view returns (IERC20) { return _asset; } function balanceOf(address account) external view returns (uint256) { return _balances[account]; } function totalLiabilities() external view returns (uint256) { return _totalLiabilities; } function contractVersion() public pure virtual returns (uint256) { return 1; } function _authorizeUpgrade(address) internal override onlyOwner {} }