Skip to content

Commit

Permalink
[NES-218] update smart contracts to use interfaces (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
eyqs authored Sep 23, 2024
1 parent d8736b9 commit a9c8e44
Show file tree
Hide file tree
Showing 19 changed files with 81 additions and 86 deletions.
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
11 changes: 5 additions & 6 deletions smart-wallets/src/WalletUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,17 @@ contract WalletUtils {
if (msg.sender != address(this)) {
revert UnauthorizedCall(msg.sender);
}

_;
}

/**
* @notice Checks if an address is a contract.
* @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 false for externally owned accounts (EOA) and true for contracts.
* @param addr The address to check.
* @return bool Returns true if the address is a contract, and false if it's an externally owned account (EOA).
* 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) {
function isContract(address addr) internal view returns (bool hasCode) {
uint32 size;
assembly {
size := extcodesize(addr)
Expand Down
Loading

0 comments on commit a9c8e44

Please sign in to comment.