Skip to content

Commit

Permalink
[NES-254] add adapter contract for pUSD (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
eyqs authored Nov 13, 2024
1 parent fffd022 commit 5ca1c06
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 6 deletions.
15 changes: 9 additions & 6 deletions nest/script/DeployNestContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { NestStaking } from "../src/NestStaking.sol";
import { IComponentToken } from "../src/interfaces/IComponentToken.sol";
import { AggregateTokenProxy } from "../src/proxy/AggregateTokenProxy.sol";
import { NestStakingProxy } from "../src/proxy/NestStakingProxy.sol";
import { pUSD } from "../src/token/pUSD.sol";

// Concrete implementation of ComponentToken
contract ConcreteComponentToken is ComponentToken {
Expand All @@ -35,15 +36,17 @@ contract ConcreteComponentToken is ComponentToken {
contract DeployNestContracts is Script, Test {

address private constant NEST_ADMIN_ADDRESS = 0xb015762405De8fD24d29A6e0799c12e0Ea81c1Ff;
address private constant PUSD_ADDRESS = 0xe644F07B1316f28a7F134998e021eA9f7135F351;
address private constant USDT_ADDRESS = 0x2413b8C79Ce60045882559f63d308aE3DFE0903d;

function test() public { }

function run() external {
vm.startBroadcast(NEST_ADMIN_ADDRESS);

IComponentToken USDT = IComponentToken(USDT_ADDRESS);
// Deploy pUSD
pUSD pUSDToken = new pUSD();
ERC1967Proxy pUSDProxy =
new ERC1967Proxy(address(pUSDToken), abi.encodeCall(pUSD.initialize, (NEST_ADMIN_ADDRESS)));
console2.log("pUSDProxy deployed to:", address(pUSDProxy));

// Deploy ConcreteComponentToken
ConcreteComponentToken componentToken = new ConcreteComponentToken();
Expand All @@ -55,7 +58,7 @@ contract DeployNestContracts is Script, Test {
NEST_ADMIN_ADDRESS, // owner
"Banana", // name
"BAN", // symbol
IERC20(USDT_ADDRESS), // asset token
IERC20(address(pUSDProxy)), // asset token
false, // async deposit
false // async redeem
)
Expand All @@ -73,7 +76,7 @@ contract DeployNestContracts is Script, Test {
NEST_ADMIN_ADDRESS,
"Apple",
"AAPL",
IComponentToken(PUSD_ADDRESS),
IComponentToken(address(pUSDProxy)),
1e17, // ask price
1e17 // bid price
)
Expand All @@ -82,7 +85,7 @@ contract DeployNestContracts is Script, Test {
console2.log("AggregateTokenProxy deployed to:", address(aggregateTokenProxy));

// Add new component tokens
AggregateToken(address(aggregateTokenProxy)).addComponentToken(USDT);
AggregateToken(address(aggregateTokenProxy)).addComponentToken(IComponentToken(address(pUSDProxy)));
AggregateToken(address(aggregateTokenProxy)).addComponentToken(IComponentToken(address(componentTokenProxy)));

// Deploy NestStaking
Expand Down
137 changes: 137 additions & 0 deletions nest/src/token/pUSD.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// 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 { 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";

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);

}

/**
* @title pUSD
* @author Eugene Y. Q. Shen, Alp Guneysel
* @notice Unified Plume USD stablecoin
*/
contract PUSD is Initializable, ERC20Upgradeable, AccessControlUpgradeable, UUPSUpgradeable {

using SafeTransferLib for ERC20;

// ========== 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");

// ========== STATE VARIABLES ==========
IVault public vault;
bool public paused;

// ========== EVENTS ==========
event VaultChanged(address oldVault, address newVault);
event Paused(address account);
event Unpaused(address account);

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

// ========== CONSTRUCTOR & INITIALIZER ==========
/// @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);
_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";
}

// ========== 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) {
return 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);
}

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

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

// ========== INTERFACE SUPPORT ==========
function supportsInterface(
bytes4 interfaceId
) public view override(AccessControlUpgradeable) returns (bool) {
return super.supportsInterface(interfaceId);
}

}

0 comments on commit 5ca1c06

Please sign in to comment.