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

Fixes for minor audit issues #116

Merged
merged 12 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
67 changes: 40 additions & 27 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,46 +19,45 @@ import {ProgressiveCurve} from "src/ProgressiveCurve.sol";
import {LinearCurve} from "src/LinearCurve.sol";

contract DeployEthMultiVault is Script {
address deployer;

// Multisig addresses for key roles in the protocol
address admin = 0xa28d4AAcA48bE54824dA53a19b05121DE71Ef480;
address protocolMultisig = 0xC03F0dE5b34339e1B968e4f317Cd7e7FBd421FD1;
address atomWarden = 0xC35DFCFE50da58d957fc47C7063f56135aFF61B8;
address public admin = 0xa28d4AAcA48bE54824dA53a19b05121DE71Ef480;
address public protocolMultisig = 0xC03F0dE5b34339e1B968e4f317Cd7e7FBd421FD1;
address public atomWarden = 0xC35DFCFE50da58d957fc47C7063f56135aFF61B8;

// Constants from Base
IPermit2 permit2 = IPermit2(address(0x000000000022D473030F116dDEE9F6B43aC78BA3)); // Permit2 on Base
address entryPoint = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; // EntryPoint on Base
IPermit2 public permit2 = IPermit2(address(0x000000000022D473030F116dDEE9F6B43aC78BA3)); // Permit2 on Base
address public entryPoint = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; // EntryPoint on Base

// Contracts to be deployed
AtomWallet atomWallet;
UpgradeableBeacon atomWalletBeacon;
EthMultiVault ethMultiVault;
TransparentUpgradeableProxy ethMultiVaultProxy;
TimelockController timelock;
AtomWallet public atomWallet;
UpgradeableBeacon public atomWalletBeacon;
EthMultiVault public ethMultiVault;
TransparentUpgradeableProxy public ethMultiVaultProxy;
TimelockController public timelock;

// Bonding Curves
BondingCurveRegistry bondingCurveRegistry;
LinearCurve linearCurve; // <-- Not used in this edition of EthMultiVault
ProgressiveCurve progressiveCurve;
TransparentUpgradeableProxy public bondingCurveRegistryProxy;
BondingCurveRegistry public bondingCurveRegistry;
LinearCurve public linearCurve; // <-- Not used in this edition of EthMultiVault
ProgressiveCurve public progressiveCurve;

function run() external {
// Begin sending tx's to network
vm.startBroadcast();

// TimelockController parameters
uint256 minDelay = 3 days;
uint256 minDelay = 1 minutes; // 1 minute during the setup stage; should be updated to 7 days later on
address[] memory proposers = new address[](1);
address[] memory executors = new address[](1);

proposers[0] = admin;
executors[0] = admin;
executors[0] = address(0);

// deploy TimelockController
timelock = new TimelockController(
minDelay, // minimum delay for timelock transactions
proposers, // proposers (can schedule transactions)
executors, // executors
executors, // executors (can execute transactions) - open role
address(0) // no default admin that can change things without going through the timelock process (self-administered)
);
console.logString("deployed TimelockController.");
Expand All @@ -79,7 +78,7 @@ contract DeployEthMultiVault is Script {
minShare: 1e6, // Minimum share amount (e.g., for vault initialization)
atomUriMaxLength: 250, // Maximum length of the atom URI data that can be passed when creating atom vaults
decimalPrecision: 1e18, // decimal precision used for calculating share prices
minDelay: 1 days // minimum delay for timelocked transactions
minDelay: 3 days // minimum delay for timelocked transactions
});

IEthMultiVault.AtomConfig memory atomConfig = IEthMultiVault.AtomConfig({
Expand Down Expand Up @@ -108,10 +107,23 @@ contract DeployEthMultiVault is Script {

// ------------------------------- Bonding Curves----------------------------------------

// Deploy BondingCurveRegistry and take temporary ownership to add the curves
// Prepare data for initializer function of BondingCurveRegistry
bytes memory bondingCurveRegistryInitData = abi.encodeWithSelector(
BondingCurveRegistry.initialize.selector,
msg.sender // take temporary ownership of the BondingCurveRegistry contract to add the required curves
);

// Deploy BondingCurveRegistry implementation contract
bondingCurveRegistry = new BondingCurveRegistry();
console.logString("deployed BondingCurveRegistry.");
bondingCurveRegistry.initialize(msg.sender);
console.logString("deployed BondingCurveRegistry implementation.");

// Deploy TransparentUpgradeableProxy with BondingCurveRegistry logic contract
bondingCurveRegistryProxy = new TransparentUpgradeableProxy(
address(bondingCurveRegistry), // BondingCurveRegistry logic contract address
address(timelock), // Timelock controller address, which will be the owner of the ProxyAdmin contract for the proxy
bondingCurveRegistryInitData // Initialization data to call the `initialize` function in BondingCurveRegistry
);
console.logString("deployed BondingCurveRegistry proxy.");

// Deploy LinearCurve
linearCurve = new LinearCurve("Linear Curve");
Expand All @@ -125,8 +137,9 @@ contract DeployEthMultiVault is Script {
bondingCurveRegistry.addBondingCurve(address(linearCurve));
bondingCurveRegistry.addBondingCurve(address(progressiveCurve));

// Transfer ownership of BondingCurveRegistry to admin
bondingCurveRegistry.setAdmin(admin);
// Transfer ownership of BondingCurveRegistry to the timelock
bondingCurveRegistry.transferOwnership(address(timelock));
// NOTE: TimelockController needs to accept the ownership of the BondingCurveRegistry in order to become a new owner

IEthMultiVault.BondingCurveConfig memory bondingCurveConfig = IEthMultiVault.BondingCurveConfig({
registry: address(bondingCurveRegistry),
Expand All @@ -135,7 +148,7 @@ contract DeployEthMultiVault is Script {

// -------------------------------------------------------------------------------------

// Prepare data for initializer function
// Prepare data for initializer function of EthMultiVault
bytes memory initData = abi.encodeWithSelector(
EthMultiVault.init.selector,
generalConfig,
Expand All @@ -148,15 +161,15 @@ contract DeployEthMultiVault is Script {

// Deploy EthMultiVault implementation contract
ethMultiVault = new EthMultiVault();
console.logString("deployed EthMultiVault.");
console.logString("deployed EthMultiVault implementation.");

// Deploy TransparentUpgradeableProxy with EthMultiVault logic contract
ethMultiVaultProxy = new TransparentUpgradeableProxy(
address(ethMultiVault), // EthMultiVault logic contract address
address(timelock), // Timelock controller address, which will be the owner of the ProxyAdmin contract for the proxy
initData // Initialization data to call the `init` function in EthMultiVault
);
console.logString("deployed TransparentUpgradeableProxy.");
console.logString("deployed EthMultiVault proxy.");

// stop sending tx's
vm.stopBroadcast();
Expand Down
58 changes: 32 additions & 26 deletions src/BaseCurve.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import {UD60x18, ud60x18} from "@prb/math/UD60x18.sol";
import {Errors} from "src/libraries/Errors.sol";
import {IBaseCurve} from "src/interfaces/IBaseCurve.sol";

/**
* @title BaseCurve
Expand All @@ -13,16 +14,28 @@ import {UD60x18, ud60x18} from "@prb/math/UD60x18.sol";
* accomodating for the effect of fees, supply burn, airdrops, etc) are handled by the EthMultiVault instead
* of the curves themselves.
*/
abstract contract BaseCurve {
abstract contract BaseCurve is IBaseCurve {
/// @notice The name of the curve
string public name;

/// @notice Construct the curve with a unique name
///
/// @param _name Unique name for the curve
constructor(string memory _name) {
if (bytes(_name).length == 0) {
revert Errors.BaseCurve_EmptyStringNotAllowed();
}

name = _name;
}

/// @notice The maximum number of shares that this curve can handle without overflowing.
/// @dev Checked by the EthMultiVault before transacting
function maxShares() public view virtual returns (uint256);
function maxShares() external view virtual returns (uint256);

/// @notice The maximum number of assets that this curve can handle without overflowing.
/// @dev Checked by the EthMultiVault before transacting
function maxAssets() public view virtual returns (uint256);
function maxAssets() external view virtual returns (uint256);

/// @notice Preview how many shares would be minted for a deposit of assets
///
Expand All @@ -31,19 +44,19 @@ abstract contract BaseCurve {
/// @param totalShares Total quantity of shares already awarded by the curve
/// @return shares The number of shares that would be minted
function previewDeposit(uint256 assets, uint256 totalAssets, uint256 totalShares)
public
external
view
virtual
returns (uint256 shares);

/// @notice Preview how many assets would be returned for burning a specific amount of shares
/// @notice Preview how many assets would be required to mint a specific amount of shares
///
/// @param shares Quantity of shares to burn
/// @param shares Quantity of shares to mint
/// @param totalShares Total quantity of shares already awarded by the curve
/// @param totalAssets Total quantity of assets already staked into the curve
/// @return assets The number of assets that would be returned
function previewRedeem(uint256 shares, uint256 totalShares, uint256 totalAssets)
public
/// @return assets The number of assets that would be required to mint the shares
function previewMint(uint256 shares, uint256 totalShares, uint256 totalAssets)
external
view
virtual
returns (uint256 assets);
Expand All @@ -55,19 +68,19 @@ abstract contract BaseCurve {
/// @param totalShares Total quantity of shares already awarded by the curve
/// @return shares The number of shares that would need to be redeemed
function previewWithdraw(uint256 assets, uint256 totalAssets, uint256 totalShares)
public
external
view
virtual
returns (uint256 shares);

/// @notice Preview how many assets would be required to mint a specific amount of shares
/// @notice Preview how many assets would be returned for burning a specific amount of shares
///
/// @param shares Quantity of shares to mint
/// @param shares Quantity of shares to burn
/// @param totalShares Total quantity of shares already awarded by the curve
/// @param totalAssets Total quantity of assets already staked into the curve
/// @return assets The number of assets that would be required to mint the shares
function previewMint(uint256 shares, uint256 totalShares, uint256 totalAssets)
public
/// @return assets The number of assets that would be returned
function previewRedeem(uint256 shares, uint256 totalShares, uint256 totalAssets)
external
view
virtual
returns (uint256 assets);
Expand All @@ -79,7 +92,7 @@ abstract contract BaseCurve {
/// @param totalShares Total quantity of shares already awarded by the curve
/// @return shares The number of shares equivalent to the given assets
function convertToShares(uint256 assets, uint256 totalAssets, uint256 totalShares)
public
external
view
virtual
returns (uint256 shares);
Expand All @@ -91,7 +104,7 @@ abstract contract BaseCurve {
/// @param totalAssets Total quantity of assets already staked into the curve
/// @return assets The number of assets equivalent to the given shares
function convertToAssets(uint256 shares, uint256 totalShares, uint256 totalAssets)
public
external
view
virtual
returns (uint256 assets);
Expand All @@ -100,12 +113,5 @@ abstract contract BaseCurve {
///
/// @param totalShares Total quantity of shares already awarded by the curve
/// @return sharePrice The current price of a share
function currentPrice(uint256 totalShares) public view virtual returns (uint256 sharePrice);

/// @notice Construct the curve with a unique name
///
/// @param _name Unique name for the curve
constructor(string memory _name) {
name = _name;
}
function currentPrice(uint256 totalShares) external view virtual returns (uint256 sharePrice);
}
46 changes: 26 additions & 20 deletions src/BondingCurveRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
pragma solidity ^0.8.27;

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

import {IBaseCurve} from "src/interfaces/IBaseCurve.sol";
import {IBondingCurveRegistry} from "src/interfaces/IBondingCurveRegistry.sol";
import {Errors} from "src/libraries/Errors.sol";

/**
Expand All @@ -21,14 +23,11 @@ import {Errors} from "src/libraries/Errors.sol";
* You can think of the registry as a concierge the EthMultiVault uses to access various
* economic incentive patterns.
*/
contract BondingCurveRegistry is Initializable {
contract BondingCurveRegistry is IBondingCurveRegistry, Initializable, Ownable2StepUpgradeable {
/* =================================================== */
/* STATE VARIABLES */
/* =================================================== */

// Admin has the right to add curves to the registry
address public admin;

// Quantity of known curves, used to assign IDs
uint256 public count;

Expand All @@ -38,16 +37,19 @@ contract BondingCurveRegistry is Initializable {
// Mapping of curve addresses to curve IDs, for reverse lookup
mapping(address => uint256) public curveIds;

// Mapping of the registered curve names, used to enforce uniqueness
mapping(string => bool) public registeredCurveNames;

/* =================================================== */
/* MODIFIERS */
/* EVENTS */
/* =================================================== */

modifier onlyAdmin() {
if (msg.sender != admin) {
revert Errors.BondingCurveRegistry_OnlyOwner();
}
_;
}
/// @notice Emitted when a new curve is added to the registry
///
/// @param curveId The ID of the curve
/// @param curveAddress The address of the curve
/// @param curveName The name of the curve
event BondingCurveAdded(uint256 indexed curveId, address indexed curveAddress, string indexed curveName);

/* =================================================== */
/* INITIALIZER */
Expand All @@ -57,33 +59,37 @@ contract BondingCurveRegistry is Initializable {
/// @param _admin Address who may add curves to the registry
/// NOTE: This function is called only once (during contract deployment)
function initialize(address _admin) external initializer {
require(_admin != address(0), "BondingCurveRegistry: requires owner");
admin = _admin;
__Ownable_init(_admin);
}

/* =================================================== */
/* RESTRICTED FUNCTIONS */
/* =================================================== */

/// @notice Set the admin address
/// @param _admin Address of the new admin
function setAdmin(address _admin) external onlyAdmin {
admin = _admin;
}

/// @notice Add a new bonding curve to the registry
/// @param bondingCurve Address of the new bonding curve
function addBondingCurve(address bondingCurve) external onlyAdmin {
function addBondingCurve(address bondingCurve) external onlyOwner {
if (curveIds[bondingCurve] != 0) {
revert Errors.BondingCurveRegistry_CurveAlreadyExists();
}

// Enforce curve name uniqueness
string memory curveName = IBaseCurve(bondingCurve).name();
if (registeredCurveNames[curveName]) {
revert Errors.BondingCurveRegistry_CurveNameNotUnique();
}

// 0 is reserved to safeguard against uninitialized values
++count;

// Add the curve to the registry, keeping track of its address and ID in separate tables
curveAddresses[count] = bondingCurve;
curveIds[bondingCurve] = count;

// Mark the curve name as registered
registeredCurveNames[curveName] = true;

emit BondingCurveAdded(count, bondingCurve, curveName);
}

/* =================================================== */
Expand Down
Loading
Loading