From e415bc95361bb94d76a5edd1f06b422d4c9035ff Mon Sep 17 00:00:00 2001 From: ungaro Date: Wed, 13 Nov 2024 12:35:52 -0500 Subject: [PATCH] add vault for pUSD --- nest/src/token/pUSD.sol | 144 ++++++++++++++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 21 deletions(-) diff --git a/nest/src/token/pUSD.sol b/nest/src/token/pUSD.sol index b6e412c..65703a1 100644 --- a/nest/src/token/pUSD.sol +++ b/nest/src/token/pUSD.sol @@ -6,6 +6,9 @@ import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/I import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import { BoringVault } from "./BoringVault.sol"; +import { SafeTransferLib } from "@solmate/utils/SafeTransferLib.sol"; + /** * @title pUSD * @author Eugene Y. Q. Shen, Alp Guneysel @@ -13,14 +16,38 @@ import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC2 */ contract pUSD is Initializable, ERC20Upgradeable, AccessControlUpgradeable, UUPSUpgradeable { - // Constants + using SafeTransferLib for ERC20; + + // ========== ROLES ========== - /// @notice Role for the admin of pUSD - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - /// @notice Role for the upgrader of pUSD bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); + bytes32 public constant VAULT_ADMIN_ROLE = keccak256("VAULT_ADMIN_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + // ========== STATE VARIABLES ========== + + /// @notice The vault contract for internal accounting + IVault public vault; + + /// @notice Whether transfers are paused + bool public paused; + + // ========== EVENTS ========== + + event VaultChanged(address oldVault, address newVault); + event Paused(address account); + event Unpaused(address account); - // Initializer + // ========== MODIFIERS ========== + + modifier whenNotPaused() { + require(!paused, "PUSD: paused"); + _; + } + + // ========== CONSTRUCTOR & INITIALIZER ========== /** * @notice Prevent the implementation contract from being initialized or reinitialized @@ -35,27 +62,23 @@ contract pUSD is Initializable, ERC20Upgradeable, AccessControlUpgradeable, UUPS * @dev Give all roles to the admin address passed into the constructor * @param owner_ Address of the owner of pUSD */ - function initialize( - address owner_ - ) public initializer { - __ERC20_init("Plume USD", "pUSD"); + function initialize(address _vault, address admin) external initializer { + __ERC20_init("", ""); // Empty strings since we override name() and symbol() __AccessControl_init(); __UUPSUpgradeable_init(); - _grantRole(DEFAULT_ADMIN_ROLE, owner_); - _grantRole(ADMIN_ROLE, owner_); - _grantRole(UPGRADER_ROLE, owner_); - } + vault = IVault(_vault); - // Override Functions + // Setup roles + _grantRole(DEFAULT_ADMIN_ROLE, admin); + _grantRole(UPGRADER_ROLE, admin); + _grantRole(MINTER_ROLE, admin); + _grantRole(BURNER_ROLE, admin); + _grantRole(VAULT_ADMIN_ROLE, admin); + _grantRole(PAUSER_ROLE, admin); + } - /** - * @notice Revert when `msg.sender` is not authorized to upgrade the contract - * @param newImplementation Address of the new implementation - */ - function _authorizeUpgrade( - address newImplementation - ) internal override onlyRole(UPGRADER_ROLE) { } + // ========== METADATA OVERRIDES ========== function decimals() public pure override returns (uint8) { return 6; @@ -69,4 +92,83 @@ contract pUSD is Initializable, ERC20Upgradeable, AccessControlUpgradeable, UUPS return "pUSD"; } + // ========== ADMIN FUNCTIONS ========== + + function setVault( + address newVault + ) external onlyRole(VAULT_ADMIN_ROLE) { + address oldVault = address(vault); + vault = IVault(newVault); + emit VaultChanged(oldVault, newVault); + } + + function pause() external onlyRole(PAUSER_ROLE) { + paused = true; + emit Paused(msg.sender); + } + + function unpause() external onlyRole(PAUSER_ROLE) { + paused = false; + emit Unpaused(msg.sender); + } + + // Required override for UUPSUpgradeable + function _authorizeUpgrade( + address newImplementation + ) internal override onlyRole(UPGRADER_ROLE) { } + + // ========== ERC20 OVERRIDES ========== + + function transfer(address to, uint256 amount) public override whenNotPaused returns (bool) { + address owner = _msgSender(); + bool success = super.transfer(to, amount); + + // Perform actual transfer in vault + vault.transferFrom(owner, to, amount); + + return success; + } + + function transferFrom(address from, address to, uint256 amount) public override whenNotPaused returns (bool) { + bool success = super.transferFrom(from, to, amount); + + // Perform actual transfer in vault + vault.transferFrom(from, to, amount); + + return success; + } + + function approve(address spender, uint256 amount) public override whenNotPaused returns (bool) { + bool success = super.approve(spender, amount); + + // Approve in vault as well + vault.approve(spender, amount); + + return success; + } + + // ========== MINT/BURN ========== + + function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) { + _mint(to, amount); + + // Mint in vault + vault.enter(address(this), address(this), 0, to, amount); + } + + function burn(address from, uint256 amount) external onlyRole(BURNER_ROLE) { + _burn(from, amount); + + // Burn in vault + vault.exit(address(this), address(this), 0, from, amount); + } + + // ========== INTERFACE SUPPORT ========== + + function supportsInterface( + bytes4 interfaceId + ) public view override(AccessControlUpgradeable) returns (bool) { + return super.supportsInterface(interfaceId); + } + }