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-218] update smart contracts to use interfaces #26

Merged
merged 7 commits into from
Sep 23, 2024
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# contracts

Monorepo for all Plume contracts
11 changes: 5 additions & 6 deletions nest/script/DeployNestContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ pragma solidity ^0.8.25;

import "forge-std/Script.sol";

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

import { AggregateToken } from "../src/AggregateToken.sol";
import { FakeComponentToken } from "../src/FakeComponentToken.sol";
import { NestStaking } from "../src/NestStaking.sol";
import { IComponentToken } from "../src/interfaces/IComponentToken.sol";
import { AggregateTokenProxy } from "../src/proxy/AggregateTokenProxy.sol";
import { FakeComponentTokenProxy } from "../src/proxy/FakeComponentTokenProxy.sol";
import { NestStakingProxy } from "../src/proxy/NestStakingProxy.sol";
Expand All @@ -21,12 +20,12 @@ contract DeployNestContracts is Script {
function run() external {
vm.startBroadcast(ARC_ADMIN_ADDRESS);

IComponentToken currencyToken = IComponentToken(USDC_ADDRESS);

FakeComponentToken fakeComponentToken = new FakeComponentToken();
FakeComponentTokenProxy fakeComponentTokenProxy = new FakeComponentTokenProxy(
address(fakeComponentToken),
abi.encodeCall(
FakeComponentToken.initialize, (ARC_ADMIN_ADDRESS, "Banana", "BAN", IERC20(USDC_ADDRESS), 18)
)
abi.encodeCall(FakeComponentToken.initialize, (ARC_ADMIN_ADDRESS, "Banana", "BAN", currencyToken, 18))
);
console.log("FakeComponentTokenProxy deployed to:", address(fakeComponentTokenProxy));

Expand All @@ -39,7 +38,7 @@ contract DeployNestContracts is Script {
NEST_ADMIN_ADDRESS,
"Apple",
"AAPL",
USDC_ADDRESS,
currencyToken,
18,
15e17,
12e17,
Expand Down
28 changes: 13 additions & 15 deletions nest/src/AggregateToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ contract AggregateToken is
* @param owner Address of the owner of the AggregateToken
* @param name Name of the AggregateToken
* @param symbol Symbol of the AggregateToken
* @param currencyAddress Address of the CurrencyToken used to mint and burn the AggregateToken
* @param currencyToken CurrencyToken used to mint and burn the AggregateToken
* @param decimals_ Number of decimals of the AggregateToken
* @param askPrice Price at which users can buy the AggregateToken using CurrencyToken, times the base
* @param bidPrice Price at which users can sell the AggregateToken to receive CurrencyToken, times the base
Expand All @@ -194,7 +194,7 @@ contract AggregateToken is
address owner,
string memory name,
string memory symbol,
address currencyAddress,
IComponentToken currencyToken,
uint8 decimals_,
uint256 askPrice,
uint256 bidPrice,
Expand All @@ -208,9 +208,9 @@ contract AggregateToken is
_grantRole(UPGRADER_ROLE, owner);

AggregateTokenStorage storage $ = _getAggregateTokenStorage();
$.componentTokenList.push(IComponentToken(currencyAddress));
$.componentTokenMap[IComponentToken(currencyAddress)] = true;
$.currencyToken = IERC20(currencyAddress);
$.componentTokenList.push(currencyToken);
$.componentTokenMap[currencyToken] = true;
$.currencyToken = IERC20(currencyToken);
$.decimals = decimals_;
$.askPrice = askPrice;
$.bidPrice = bidPrice;
Expand All @@ -235,16 +235,15 @@ contract AggregateToken is
/**
* @notice Buy AggregateToken using CurrencyToken
* @dev The user must approve the contract to spend the CurrencyToken
* @param currencyToken_ CurrencyToken used to buy the AggregateToken
* @param currencyToken CurrencyToken used to buy the AggregateToken
* @param currencyTokenAmount Amount of CurrencyToken to pay for the AggregateToken
* @return aggregateTokenAmount Amount of AggregateToken received
*/
function buy(IERC20 currencyToken_, uint256 currencyTokenAmount) public returns (uint256 aggregateTokenAmount) {
function buy(IERC20 currencyToken, uint256 currencyTokenAmount) public returns (uint256 aggregateTokenAmount) {
AggregateTokenStorage storage $ = _getAggregateTokenStorage();
IERC20 currencyToken = $.currencyToken;

if (currencyToken_ != currencyToken) {
revert InvalidCurrencyToken(currencyToken_, currencyToken);
if (currencyToken != $.currencyToken) {
revert InvalidCurrencyToken(currencyToken, $.currencyToken);
}
if (!currencyToken.transferFrom(msg.sender, address(this), currencyTokenAmount)) {
revert UserCurrencyTokenInsufficientBalance(currencyToken, msg.sender, currencyTokenAmount);
Expand All @@ -259,16 +258,15 @@ contract AggregateToken is

/**
* @notice Sell AggregateToken to receive CurrencyToken
* @param currencyToken_ CurrencyToken received in exchange for the AggregateToken
* @param currencyToken CurrencyToken received in exchange for the AggregateToken
* @param currencyTokenAmount Amount of CurrencyToken to receive in exchange for the AggregateToken
* @return aggregateTokenAmount Amount of AggregateToken sold
*/
function sell(IERC20 currencyToken_, uint256 currencyTokenAmount) public returns (uint256 aggregateTokenAmount) {
function sell(IERC20 currencyToken, uint256 currencyTokenAmount) public returns (uint256 aggregateTokenAmount) {
AggregateTokenStorage storage $ = _getAggregateTokenStorage();
IERC20 currencyToken = $.currencyToken;

if (currencyToken_ != currencyToken) {
revert InvalidCurrencyToken(currencyToken_, currencyToken);
if (currencyToken != $.currencyToken) {
revert InvalidCurrencyToken(currencyToken, $.currencyToken);
}
if (!currencyToken.transfer(msg.sender, currencyTokenAmount)) {
revert CurrencyTokenInsufficientBalance(currencyToken, currencyTokenAmount);
Expand Down
20 changes: 8 additions & 12 deletions nest/src/FakeComponentToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,13 @@ contract FakeComponentToken is
/**
* @notice Buy FakeComponentToken using CurrencyToken
* @dev The user must approve the contract to spend the CurrencyToken
* @param currencyToken_ CurrencyToken used to buy the FakeComponentToken
* @param currencyToken CurrencyToken used to buy the FakeComponentToken
* @param amount Amount of CurrencyToken to pay to receive the same amount of FakeComponentToken
* @return componentTokenAmount Amount of FakeComponentToken received
*/
function buy(IERC20 currencyToken_, uint256 amount) public returns (uint256 componentTokenAmount) {
IERC20 currencyToken = _getFakeComponentTokenStorage().currencyToken;

if (currencyToken_ != currencyToken) {
revert InvalidCurrencyToken(currencyToken_, currencyToken);
function buy(IERC20 currencyToken, uint256 amount) public returns (uint256 componentTokenAmount) {
if (currencyToken != _getFakeComponentTokenStorage().currencyToken) {
revert InvalidCurrencyToken(currencyToken, _getFakeComponentTokenStorage().currencyToken);
}
if (!currencyToken.transferFrom(msg.sender, address(this), amount)) {
revert UserCurrencyTokenInsufficientBalance(currencyToken, msg.sender, amount);
Expand All @@ -172,15 +170,13 @@ contract FakeComponentToken is

/**
* @notice Sell FakeComponentToken to receive CurrencyToken
* @param currencyToken_ CurrencyToken received in exchange for the FakeComponentToken
* @param currencyToken CurrencyToken received in exchange for the FakeComponentToken
* @param amount Amount of CurrencyToken to receive in exchange for the FakeComponentToken
* @return componentTokenAmount Amount of FakeComponentToken sold
*/
function sell(IERC20 currencyToken_, uint256 amount) public returns (uint256 componentTokenAmount) {
IERC20 currencyToken = _getFakeComponentTokenStorage().currencyToken;

if (currencyToken_ != currencyToken) {
revert InvalidCurrencyToken(currencyToken_, currencyToken);
function sell(IERC20 currencyToken, uint256 amount) public returns (uint256 componentTokenAmount) {
if (currencyToken != _getFakeComponentTokenStorage().currencyToken) {
revert InvalidCurrencyToken(currencyToken, _getFakeComponentTokenStorage().currencyToken);
}
if (!currencyToken.transfer(msg.sender, amount)) {
revert CurrencyTokenInsufficientBalance(currencyToken, amount);
Expand Down
8 changes: 4 additions & 4 deletions nest/src/NestStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils

import { AggregateToken } from "./AggregateToken.sol";
import { IAggregateToken } from "./interfaces/IAggregateToken.sol";
import { IComponentToken } from "./interfaces/IAggregateToken.sol";
import { AggregateTokenProxy } from "./proxy/AggregateTokenProxy.sol";

/**
Expand Down Expand Up @@ -152,7 +153,7 @@ contract NestStaking is Initializable, AccessControlUpgradeable, UUPSUpgradeable
* @param owner Address of the owner of the AggregateToken
* @param name Name of the AggregateToken
* @param symbol Symbol of the AggregateToken
* @param currencyAddress Address of the CurrencyToken used to mint and burn the AggregateToken
* @param currencyToken CurrencyToken used to mint and burn the AggregateToken
* @param decimals_ Number of decimals of the AggregateToken
* @param askPrice Price at which users can buy the AggregateToken using CurrencyToken, times the base
* @param bidPrice Price at which users can sell the AggregateToken to receive CurrencyToken, times the base
Expand All @@ -163,7 +164,7 @@ contract NestStaking is Initializable, AccessControlUpgradeable, UUPSUpgradeable
address owner,
string memory name,
string memory symbol,
address currencyAddress,
IComponentToken currencyToken,
uint8 decimals_,
uint256 askPrice,
uint256 bidPrice,
Expand All @@ -175,8 +176,7 @@ contract NestStaking is Initializable, AccessControlUpgradeable, UUPSUpgradeable
AggregateTokenProxy aggregateTokenProxy = new AggregateTokenProxy(
address(aggregateTokenImplementation),
abi.encodeCall(
AggregateToken.initialize,
(owner, name, symbol, currencyAddress, decimals_, askPrice, bidPrice, tokenURI)
AggregateToken.initialize, (owner, name, symbol, currencyToken, decimals_, askPrice, bidPrice, tokenURI)
)
);

Expand Down
3 changes: 0 additions & 3 deletions p/src/P.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ contract P is

// Constants

/// @notice Role for the admin of P
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
/// @notice Role for the upgrader of P
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
/// @notice Role for the minter of P
Expand Down Expand Up @@ -64,7 +62,6 @@ contract P is
__UUPSUpgradeable_init();

_grantRole(DEFAULT_ADMIN_ROLE, owner);
_grantRole(ADMIN_ROLE, owner);
_grantRole(MINTER_ROLE, owner);
_grantRole(BURNER_ROLE, owner);
_grantRole(PAUSER_ROLE, owner);
Expand Down
1 change: 0 additions & 1 deletion plume/script/DeployDevnetContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity ^0.8.25;

import "forge-std/Script.sol";
import { Upgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol";

import { pUSD } from "../src/pUSD.sol";

Expand Down
24 changes: 16 additions & 8 deletions smart-wallets/src/SmartWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
pragma solidity ^0.8.25;

import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { WalletUtils } from "./WalletUtils.sol";
import { AssetVault } from "./extensions/AssetVault.sol";
import { SignedOperations } from "./extensions/SignedOperations.sol";
import { IAssetToken } from "./interfaces/IAssetToken.sol";
import { IAssetVault } from "./interfaces/IAssetVault.sol";
import { ISmartWallet } from "./interfaces/ISmartWallet.sol";
import { AssetToken } from "./token/AssetToken.sol";

/**
* @title SmartWallet
Expand All @@ -35,7 +36,7 @@ contract SmartWallet is Proxy, WalletUtils, SignedOperations, ISmartWallet {
/// @dev Address of the current user wallet implementation for each user
address userWallet;
/// @dev AssetVault associated with the smart wallet
AssetVault assetVault;
IAssetVault assetVault;
}

// keccak256(abi.encode(uint256(keccak256("plume.storage.SmartWallet")) - 1)) & ~bytes32(uint256(0xff))
Expand All @@ -58,11 +59,17 @@ contract SmartWallet is Proxy, WalletUtils, SignedOperations, ISmartWallet {

// Errors

/**
* @notice Indicates a failure because the sender is not the AssetVault
* @param sender Address of the sender that is not the AssetVault
*/
error UnauthorizedAssetVault(address sender);

/**
* @notice Indicates a failure because the AssetVault for the user already exists
* @param assetVault Existing AssetVault for the user
*/
error AssetVaultAlreadyExists(AssetVault assetVault);
error AssetVaultAlreadyExists(IAssetVault assetVault);

// Base Smart Wallet Functions

Expand All @@ -76,15 +83,16 @@ contract SmartWallet is Proxy, WalletUtils, SignedOperations, ISmartWallet {
}

/// @notice AssetVault associated with the smart wallet
function getAssetVault() external view returns (AssetVault assetVault) {
function getAssetVault() external view returns (IAssetVault assetVault) {
return _getSmartWalletStorage().assetVault;
}

/**
* @notice Get the number of AssetTokens that are currently locked in the AssetVault
* @param assetToken AssetToken from which the yield is to be redistributed
* @return balanceLocked Amount of the AssetToken that is currently locked
*/
function getBalanceLocked(AssetToken assetToken) public view returns (uint256 balanceLocked) {
function getBalanceLocked(IAssetToken assetToken) public view returns (uint256 balanceLocked) {
return _getSmartWalletStorage().assetVault.getBalanceLocked(assetToken);
}

Expand All @@ -103,9 +111,9 @@ contract SmartWallet is Proxy, WalletUtils, SignedOperations, ISmartWallet {
/**
* @notice Fallback function to the user wallet implementation if
* the function is not implemented in the base SmartWallet implementation
* @return Address of the user wallet implementation
* @return impl Address of the user wallet implementation
*/
function _implementation() internal view virtual override returns (address) {
function _implementation() internal view virtual override returns (address impl) {
return _getSmartWalletStorage().userWallet;
}

Expand Down
8 changes: 4 additions & 4 deletions smart-wallets/src/WalletFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity 0.8.25;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

import { SmartWallet } from "./SmartWallet.sol";
import { ISmartWallet } from "./interfaces/ISmartWallet.sol";

/**
* @title WalletFactory
Expand All @@ -17,15 +17,15 @@ import { SmartWallet } from "./SmartWallet.sol";
contract WalletFactory is Ownable {

/// @notice Address of the current SmartWallet implementation
SmartWallet public smartWallet;
ISmartWallet public smartWallet;

/**
* @notice Construct the WalletFactory
* @param owner_ Address of the owner of the WalletFactory
* @param smartWallet_ Initial SmartWallet implementation
* @dev The owner of the WalletFactory should be set to Plume Governance once ready
*/
constructor(address owner_, SmartWallet smartWallet_) Ownable(owner_) {
constructor(address owner_, ISmartWallet smartWallet_) Ownable(owner_) {
smartWallet = smartWallet_;
}

Expand All @@ -34,7 +34,7 @@ contract WalletFactory is Ownable {
* @dev Only the WalletFactory owner can upgrade the SmartWallet implementation
* @param smartWallet_ New SmartWallet implementation
*/
function upgrade(SmartWallet smartWallet_) public onlyOwner {
function upgrade(ISmartWallet smartWallet_) public onlyOwner {
smartWallet = smartWallet_;
}

Expand Down
4 changes: 2 additions & 2 deletions smart-wallets/src/WalletProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ contract WalletProxy is Proxy {
/**
* @notice Fallback function for the proxy implementation, which
* delegates calls to the SmartWallet through the WalletFactory
* @return Address of the SmartWallet implementation
* @return impl Address of the SmartWallet implementation
*/
function _implementation() internal view virtual override returns (address) {
function _implementation() internal view virtual override returns (address impl) {
return address(walletFactory.smartWallet());
}

Expand Down
16 changes: 15 additions & 1 deletion smart-wallets/src/WalletUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,22 @@ contract WalletUtils {
if (msg.sender != address(this)) {
revert UnauthorizedCall(msg.sender);
}

_;
}

/**
* @notice Checks if an address is a contract or smart wallet.
* @dev This function uses the `extcodesize` opcode to check if the target address contains contract code.
* It returns true for contracts and smart wallets, and false for EOAs that do not have smart wallets.
* @param addr Address to check
* @return hasCode True if the address is a contract or smart wallet, and false if it is not
*/
function isContract(address addr) internal view returns (bool hasCode) {
uint32 size;
assembly {
size := extcodesize(addr)
}
return size > 0;
}

}
Loading