Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NES-255] make pUSD a ComponentToken #97

Merged
merged 68 commits into from
Dec 2, 2024
Merged
Changes from 1 commit
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
2131910
[NES-254] add adapter contract for pUSD
eyqs Nov 13, 2024
e415bc9
add vault for pUSD
ungaro Nov 13, 2024
51e5d53
remove incorrect include
ungaro Nov 13, 2024
9656816
keep pUSD as ERC20 and remove unnecessary functions
ungaro Nov 13, 2024
69467dd
remove comment
ungaro Nov 13, 2024
35ca154
fmt
ungaro Nov 13, 2024
c613ac9
remove hook
ungaro Nov 13, 2024
697c4f9
make pUSD a ComponentToken
ungaro Nov 13, 2024
03dcdd1
make pUSD a ComponentToken and update Deployment Script
ungaro Nov 13, 2024
2452ef9
change storage location
ungaro Nov 13, 2024
9fc98c6
remove erc4626
ungaro Nov 13, 2024
1636d22
update pUSD storage slot
ungaro Nov 13, 2024
300f1b4
pUSD is Initializable, ERC20Upgradeable, AccessControlUpgradeable, UU…
ungaro Nov 13, 2024
9e4f883
merge main
ungaro Nov 13, 2024
00cd67b
fix overrides
ungaro Nov 13, 2024
270dd48
update description
ungaro Nov 13, 2024
5bc1b2f
add test for pUSD - WIP
ungaro Nov 13, 2024
1ce0e5a
add mockvault for pUSD
ungaro Nov 13, 2024
47cb601
change initialization modifier to onlyInitializing
ungaro Nov 14, 2024
f5773db
remove pausable
ungaro Nov 14, 2024
b00649d
finish preliminary tests, fix some bugs with MockVault and pUSD
ungaro Nov 14, 2024
013f0eb
93.9% coverage for pUSD - rest is just assembly
ungaro Nov 14, 2024
f034359
remove comments
ungaro Nov 14, 2024
5521e69
add IVault interface
ungaro Nov 14, 2024
2ab5bb8
forge fmt
ungaro Nov 14, 2024
4fdd93f
better handling of convertToShares, convertToAssets, make tests 100% …
ungaro Nov 14, 2024
030869c
forge fmt
ungaro Nov 14, 2024
ff2f659
add extra comment
ungaro Nov 14, 2024
93a2f34
add reentrancy check
ungaro Nov 14, 2024
269841d
implementing some audit findings & ComponentToken precision test checks
ungaro Nov 14, 2024
396cff3
leave conversion functions unimplemented and force integrators to imp…
ungaro Nov 14, 2024
d6537de
add NatSpec comments for functions
ungaro Nov 14, 2024
d5d2a61
remove componenttoken comments
ungaro Nov 14, 2024
999adcb
deployment script for pUSD
ungaro Nov 14, 2024
114516a
add pUSD contract update
ungaro Nov 15, 2024
5e1f0cf
add more tests from pUSD
ungaro Nov 18, 2024
354bc4d
forge install: solmate
ungaro Nov 18, 2024
9093df0
change vault to teller
ungaro Nov 18, 2024
5d827b6
change deploy&upgrade pUSD scripts, update componenttoken _authorizeU…
ungaro Nov 18, 2024
7ba1fe4
change deposit and redeem functions
ungaro Nov 19, 2024
9dae01f
final changes, tests etc.
ungaro Nov 19, 2024
3111677
confirm teller.deposit works, add working test-case on-chain (pUSDPlu…
ungaro Nov 20, 2024
e0f73a8
redeem works through AtomicRequest, change proxy comments
ungaro Nov 21, 2024
e2a9c25
add boringvault struct, change related deployment and upgrade address…
ungaro Nov 22, 2024
bb540e8
fix tests and deployment/upgrade scripts
ungaro Nov 22, 2024
38608c3
remove pUSD deployment from DeployNestContracts
ungaro Nov 22, 2024
200ac3d
remove setBeforeTransferHook from mockvault
ungaro Nov 22, 2024
1deb5b9
fix conflicts
ungaro Nov 22, 2024
bb8000c
fix conflict that doesn't exist
ungaro Nov 22, 2024
fc49f7c
100% coverage on pUSD + added missing mocks
ungaro Nov 22, 2024
72b82cd
add pragma to MockUSDC
ungaro Nov 22, 2024
d2a1785
run everything without errors even RPC address is not provided for on…
ungaro Nov 25, 2024
db3516c
change balanceof to return vault's balance
ungaro Nov 25, 2024
0a0a9a8
change balanceOf, previewDeposit and previewRedeem functions
ungaro Nov 25, 2024
761855b
add decimals to interface
ungaro Nov 27, 2024
c270097
passing tests
ungaro Nov 29, 2024
0ea586a
add usdc & usdt, update deploy scripts, add missing tests
ungaro Dec 1, 2024
fcb7a30
forge fmt
ungaro Dec 1, 2024
d85a529
add IAccountantWithRateProviders, IRateProvider, Ilens and mock contr…
ungaro Dec 1, 2024
e152ec0
add lens and accountant
ungaro Dec 1, 2024
34b4546
balanceof and balanceofinassets
ungaro Dec 1, 2024
e8cbe26
all tests pass
ungaro Dec 1, 2024
145244a
100% coverage for pUSD, real calculations for converttoshares, conver…
ungaro Dec 1, 2024
4ed12dc
add nonreentrant
ungaro Dec 1, 2024
723a392
add assetsof
ungaro Dec 1, 2024
ee714c1
balanceOfInAssets to be renamed assetsof
ungaro Dec 1, 2024
8ae47f0
merge main
ungaro Dec 1, 2024
869fd2f
formatting
ungaro Dec 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
make pUSD a ComponentToken
  • Loading branch information
ungaro committed Nov 13, 2024
commit 697c4f9d8040e3b7581dc8bde71f3e598d084fd6
155 changes: 99 additions & 56 deletions nest/src/token/pUSD.sol
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { ComponentToken } from "./ComponentToken.sol";

import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";

import { SafeTransferLib } from "@solmate/utils/SafeTransferLib.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IVault {

function enter(address from, address asset, uint256 assetAmount, address to, uint256 shareAmount) external;
function exit(address to, address asset, uint256 assetAmount, address from, uint256 shareAmount) external;
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function balanceOf(
address account
) external view returns (uint256);

}

/**
* @title pUSD
* @author Eugene Y. Q. Shen, Alp Guneysel
* @notice Unified Plume USD stablecoin
* @notice Unified Plume USD stablecoin implemented as both a ComponentToken and Vault-backed ERC20
*/
contract PUSD is Initializable, ERC20Upgradeable, AccessControlUpgradeable, UUPSUpgradeable {
contract PUSD is ComponentToken {

using SafeTransferLib for ERC20;
// ========== STORAGE ==========
/// @custom:storage-location erc7201:plume.storage.PUSD
struct PUSDStorage {
IVault vault;
bool paused;
}

// ========== ROLES ==========
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");
// Using ERC-7201 namespaced storage pattern to avoid storage collisions during upgrades
bytes32 private constant PUSD_STORAGE_LOCATION = keccak256("plume.storage.PUSD");

// ========== STATE VARIABLES ==========
IVault public vault;
bool public paused;
function _getPUSDStorage() private pure returns (PUSDStorage storage $) {
assembly {
$.slot := PUSD_STORAGE_LOCATION
}
}

// ========== EVENTS ==========
event VaultChanged(address oldVault, address newVault);
@@ -44,94 +49,132 @@ contract PUSD is Initializable, ERC20Upgradeable, AccessControlUpgradeable, UUPS

// ========== MODIFIERS ==========
modifier whenNotPaused() {
require(!paused, "PUSD: paused");
require(!_getPUSDStorage().paused, "PUSD: paused");
_;
}

// ========== CONSTRUCTOR & INITIALIZER ==========
// ========== ROLES ==========
bytes32 public constant VAULT_ADMIN_ROLE = keccak256("VAULT_ADMIN_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

// ========== CONSTRUCTOR ==========
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize(address _vault, address admin) external initializer {
__ERC20_init("", ""); // Empty strings since we override name() and symbol()
__AccessControl_init();
__UUPSUpgradeable_init();

vault = IVault(_vault);

_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(UPGRADER_ROLE, admin);
_grantRole(MINTER_ROLE, admin);
_grantRole(BURNER_ROLE, admin);
// ========== INITIALIZER ==========
function initialize(address admin, IERC20 asset_, address _vault) external initializer {
// Initialize ComponentToken with pUSD metadata
super.initialize(
admin,
"Plume USD",
"pUSD",
asset_,
false, // synchronous deposits
false // synchronous redemptions
);

PUSDStorage storage $ = _getPUSDStorage();
$.vault = IVault(_vault);

// Setup additional roles
_grantRole(VAULT_ADMIN_ROLE, admin);
_grantRole(PAUSER_ROLE, admin);
}

// ========== METADATA OVERRIDES ==========
function decimals() public pure override returns (uint8) {
return 6;
}

function name() public pure override returns (string memory) {
return "Plume USD";
}

function symbol() public pure override returns (string memory) {
return "pUSD";
_grantRole(UPGRADER_ROLE, admin);
}

// ========== ADMIN FUNCTIONS ==========
function setVault(
address newVault
) external onlyRole(VAULT_ADMIN_ROLE) {
address oldVault = address(vault);
vault = IVault(newVault);
PUSDStorage storage $ = _getPUSDStorage();
address oldVault = address($.vault);
$.vault = IVault(newVault);
emit VaultChanged(oldVault, newVault);
}

function pause() external onlyRole(PAUSER_ROLE) {
ungaro marked this conversation as resolved.
Show resolved Hide resolved
paused = true;
_getPUSDStorage().paused = true;
emit Paused(msg.sender);
}

function unpause() external onlyRole(PAUSER_ROLE) {
paused = false;
_getPUSDStorage().paused = false;
emit Unpaused(msg.sender);
}

// Required override for UUPSUpgradeable
// ========== UPGRADE FUNCTIONS ==========
function _authorizeUpgrade(
address newImplementation
) internal override onlyRole(UPGRADER_ROLE) { }

// ========== GETTERS ==========
function vault() external view returns (address) {
return address(_getPUSDStorage().vault);
}

function paused() external view returns (bool) {
return _getPUSDStorage().paused;
}

// ========== ERC20 OVERRIDES ==========
function decimals() public pure override returns (uint8) {
return 6;
}
ungaro marked this conversation as resolved.
Show resolved Hide resolved

function transfer(address to, uint256 amount) public override whenNotPaused returns (bool) {
return vault.transferFrom(msg.sender, to, amount);
return _getPUSDStorage().vault.transferFrom(msg.sender, to, amount);
}

function transferFrom(address from, address to, uint256 amount) public override whenNotPaused returns (bool) {
return vault.transferFrom(from, to, amount);
return _getPUSDStorage().vault.transferFrom(from, to, amount);
}

function approve(address spender, uint256 amount) public override whenNotPaused returns (bool) {
bool success = super.approve(spender, amount);
vault.approve(spender, amount);
_getPUSDStorage().vault.approve(spender, amount);
return success;
}

function balanceOf(
address account
) public view override returns (uint256) {
return vault.balanceOf(account);
return _getPUSDStorage().vault.balanceOf(account);
}

// ========== COMPONENT TOKEN OVERRIDES ==========
function deposit(
uint256 assets,
address receiver,
address controller
) public override whenNotPaused returns (uint256 shares) {
shares = super.deposit(assets, receiver, controller);
_getPUSDStorage().vault.enter(address(this), address(asset()), assets, receiver, shares);
return shares;
}

function redeem(
uint256 shares,
address receiver,
address controller
) public override whenNotPaused returns (uint256 assets) {
assets = super.redeem(shares, receiver, controller);
_getPUSDStorage().vault.exit(receiver, address(asset()), assets, address(this), shares);
return assets;
}

function convertToShares(
uint256 assets
) public view override returns (uint256) {
return assets; // 1:1 conversion for stablecoin
}

// ========== INTERFACE SUPPORT ==========
function supportsInterface(
bytes4 interfaceId
) public view override(AccessControlUpgradeable) returns (bool) {
return super.supportsInterface(interfaceId);
function convertToAssets(
uint256 shares
) public view override returns (uint256) {
return shares; // 1:1 conversion for stablecoin
}

}