Skip to content

Commit

Permalink
[NES-218] update smart contracts to use interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
eyqs committed Sep 23, 2024
1 parent 6ce71b7 commit 77a2b03
Show file tree
Hide file tree
Showing 16 changed files with 93 additions and 75 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
41 changes: 35 additions & 6 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,18 +83,40 @@ 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
*/
function getBalanceLocked(AssetToken assetToken) public view returns (uint256 balanceLocked) {
function getBalanceLocked(IAssetToken assetToken) public view returns (uint256 balanceLocked) {
return _getSmartWalletStorage().assetVault.getBalanceLocked(assetToken);
}

/**
* @notice Transfer yield to the given beneficiary
* @dev Only the AssetVault can call this function
* @param assetToken AssetToken for which the yield is to be transferred
* @param beneficiary Address of the beneficiary to receive the yield transfer
* @param currencyToken Token in which the yield is to be transferred
* @param currencyTokenAmount Amount of currencyToken that is to be transferred
*/
function transferYield(
IAssetToken assetToken,
address beneficiary,
IERC20 currencyToken,
uint256 currencyTokenAmount
) public {
IAssetVault assetVault = _getSmartWalletStorage().assetVault;
if (msg.sender != address(assetVault)) {
revert UnauthorizedAssetVault(msg.sender);
}
currencyToken.approve(beneficiary, currencyTokenAmount);
// TODO
}

// User Wallet Functions

/**
Expand Down
9 changes: 5 additions & 4 deletions smart-wallets/src/extensions/AssetVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { IAssetToken } from "../interfaces/IAssetToken.sol";
import { IAssetVault } from "../interfaces/IAssetVault.sol";
import { ISmartWallet } from "../interfaces/ISmartWallet.sol";

/**
* @title AssetVault
Expand Down Expand Up @@ -184,7 +185,6 @@ contract AssetVault is IAssetVault {
if (msg.sender != wallet) {
revert UnauthorizedCall(msg.sender);
}

_;
}

Expand Down Expand Up @@ -214,7 +214,7 @@ contract AssetVault is IAssetVault {
address beneficiary,
uint256 amount,
uint256 expiration
) public onlyWallet {
) external onlyWallet {
if (address(assetToken) == address(0) || beneficiary == address(0)) {
revert ZeroAddress();
}
Expand Down Expand Up @@ -257,7 +257,8 @@ contract AssetVault is IAssetVault {
while (amountLocked > 0) {
if (distribution.yield.expiration > block.timestamp) {
uint256 yieldShare = (currencyTokenAmount * amountLocked) / amountTotal;
// TODO: transfer yield from the user wallet to the beneficiary
// TODO ISmartWallet(wallet).transferYield(assetToken, distribution.beneficiary,
// currencyToken, yieldShare);
emit YieldRedistributed(assetToken, distribution.beneficiary, currencyToken, yieldShare);
}

Expand All @@ -272,7 +273,7 @@ contract AssetVault is IAssetVault {
* @notice Get the number of AssetTokens that are currently locked in the AssetVault
* @param assetToken AssetToken from which the yield is to be redistributed
*/
function getBalanceLocked(IAssetToken assetToken) public view returns (uint256 balanceLocked) {
function getBalanceLocked(IAssetToken assetToken) external view returns (uint256 balanceLocked) {
// Iterate through the list and sum up the locked balance across all yield distributions
YieldDistributionListItem storage distribution = _getAssetVaultStorage().yieldDistributions[assetToken];
uint256 amountLocked = distribution.yield.amount;
Expand Down
2 changes: 1 addition & 1 deletion smart-wallets/src/extensions/SignedOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ contract SignedOperations is EIP712, WalletUtils, ISignedOperations {
$.nonces[nonce] = 1;
}

for (uint256 i = 0; i < length; i++) {
for (uint256 i = 0; i < length; ++i) {
{
(bool success,) = targets[i].call{ value: values[i] }(calls[i]);
if (success) {
Expand Down
10 changes: 4 additions & 6 deletions smart-wallets/src/interfaces/ISmartWallet.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

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

import { AssetVault } from "../extensions/AssetVault.sol";
import { AssetToken } from "../token/AssetToken.sol";
import { IAssetToken } from "./IAssetToken.sol";
import { IAssetVault } from "./IAssetVault.sol";
import { ISignedOperations } from "./ISignedOperations.sol";

interface ISmartWallet is ISignedOperations {

function deployAssetVault() external;
function getAssetVault() external view returns (AssetVault assetVault);
function getBalanceLocked(AssetToken assetToken) external view returns (uint256 balanceLocked);
function getAssetVault() external view returns (IAssetVault assetVault);
function getBalanceLocked(IAssetToken assetToken) external view returns (uint256 balanceLocked);
function upgrade(address userWallet) external;

}
5 changes: 2 additions & 3 deletions smart-wallets/src/interfaces/IYieldDistributionToken.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

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

interface IYieldDistributionToken is IERC20 {

function getCurrencyToken() external returns (ERC20 currencyToken);
function claimYield(address user) external returns (ERC20 currencyToken, uint256 currencyTokenAmount);
function getCurrencyToken() external returns (IERC20 currencyToken);
function claimYield(address user) external returns (IERC20 currencyToken, uint256 currencyTokenAmount);
function accrueYield(address user) external;

}
Loading

0 comments on commit 77a2b03

Please sign in to comment.