Skip to content

Commit

Permalink
[NES-219] implement claimYield and version
Browse files Browse the repository at this point in the history
  • Loading branch information
eyqs committed Sep 25, 2024
1 parent 534fb0c commit 0f72241
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 11 deletions.
61 changes: 53 additions & 8 deletions nest/src/AggregateToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ contract AggregateToken is
uint256 bidPrice;
/// @dev URI for the AggregateToken metadata
string tokenURI;
/// @dev Version of the AggregateToken
uint256 version;
}

// keccak256(abi.encode(uint256(keccak256("plume.storage.AggregateToken")) - 1)) & ~bytes32(uint256(0xff))
Expand All @@ -57,6 +59,8 @@ contract AggregateToken is

// Constants

/// @notice Role for the admin of the AggregateToken
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
/// @notice Role for the upgrader of the AggregateToken
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

Expand Down Expand Up @@ -169,6 +173,13 @@ contract AggregateToken is
*/
error UserCurrencyTokenInsufficientBalance(IERC20 currencyToken, address user, uint256 amount);

/**
* @notice Indicates a failure because the given version is not higher than the current version
* @param invalidVersion Invalid version that is not higher than the current version
* @param version Current version of the AggregateToken
*/
error InvalidVersion(uint256 invalidVersion, uint256 version);

// Initializer

/**
Expand Down Expand Up @@ -205,6 +216,7 @@ contract AggregateToken is
__UUPSUpgradeable_init();

_grantRole(DEFAULT_ADMIN_ROLE, owner);
_grantRole(ADMIN_ROLE, owner);
_grantRole(UPGRADER_ROLE, owner);

AggregateTokenStorage storage $ = _getAggregateTokenStorage();
Expand Down Expand Up @@ -279,14 +291,29 @@ contract AggregateToken is
emit AggregateTokenSold(msg.sender, currencyToken, currencyTokenAmount, aggregateTokenAmount);
}

/**
* @notice Claim yield for the given user
* @dev Anyone can call this function to claim yield for any user
* @param user Address of the user for which to claim yield
*/
function claimYield(address user) external returns (uint256 amount) {
AggregateTokenStorage storage $ = _getAggregateTokenStorage();
IComponentToken[] storage componentTokenList = $.componentTokenList;
uint256 length = componentTokenList.length;
for (uint256 i = 0; i < length; ++i) {
amount += componentTokenList[i].unclaimedYield(user);
}
$.currencyToken.transfer(user, amount);
}

// Admin Functions

/**
* @notice Add a ComponentToken to the component token list
* @dev Only the owner can call this function, and there is no way to remove a ComponentToken later
* @param componentToken ComponentToken to add
*/
function addComponentToken(IComponentToken componentToken) external onlyRole(DEFAULT_ADMIN_ROLE) {
function addComponentToken(IComponentToken componentToken) external onlyRole(ADMIN_ROLE) {
AggregateTokenStorage storage $ = _getAggregateTokenStorage();
if ($.componentTokenMap[componentToken]) {
revert ComponentTokenAlreadyListed(componentToken);
Expand All @@ -306,7 +333,7 @@ contract AggregateToken is
function buyComponentToken(
IComponentToken componentToken,
uint256 currencyTokenAmount
) public onlyRole(DEFAULT_ADMIN_ROLE) {
) public onlyRole(ADMIN_ROLE) {
AggregateTokenStorage storage $ = _getAggregateTokenStorage();

if (!$.componentTokenMap[componentToken]) {
Expand All @@ -333,7 +360,7 @@ contract AggregateToken is
function sellComponentToken(
IComponentToken componentToken,
uint256 currencyTokenAmount
) public onlyRole(DEFAULT_ADMIN_ROLE) {
) public onlyRole(ADMIN_ROLE) {
IERC20 currencyToken = _getAggregateTokenStorage().currencyToken;

uint256 componentTokenAmount = componentToken.sell(currencyToken, currencyTokenAmount);
Expand All @@ -343,12 +370,25 @@ contract AggregateToken is

// Admin Setter Functions

/**
* @notice Set the version of the AggregateToken
* @dev Only the owner can call this setter
* @param version New version of the AggregateToken
*/
function setVersion(uint256 version) external onlyRole(ADMIN_ROLE) {
AggregateTokenStorage storage $ = _getAggregateTokenStorage();
if (version <= $.version) {
revert InvalidVersion(version, $.version);
}
$.version = version;
}

/**
* @notice Set the CurrencyToken used to mint and burn the AggregateToken
* @dev Only the owner can call this setter
* @param currencyToken New CurrencyToken
*/
function setCurrencyToken(IERC20 currencyToken) external onlyRole(DEFAULT_ADMIN_ROLE) {
function setCurrencyToken(IERC20 currencyToken) external onlyRole(ADMIN_ROLE) {
_getAggregateTokenStorage().currencyToken = currencyToken;
}

Expand All @@ -357,7 +397,7 @@ contract AggregateToken is
* @dev Only the owner can call this setter
* @param askPrice New ask price
*/
function setAskPrice(uint256 askPrice) external onlyRole(DEFAULT_ADMIN_ROLE) {
function setAskPrice(uint256 askPrice) external onlyRole(ADMIN_ROLE) {
_getAggregateTokenStorage().askPrice = askPrice;
}

Expand All @@ -366,7 +406,7 @@ contract AggregateToken is
* @dev Only the owner can call this setter
* @param bidPrice New bid price
*/
function setBidPrice(uint256 bidPrice) external onlyRole(DEFAULT_ADMIN_ROLE) {
function setBidPrice(uint256 bidPrice) external onlyRole(ADMIN_ROLE) {
_getAggregateTokenStorage().bidPrice = bidPrice;
}

Expand All @@ -375,12 +415,17 @@ contract AggregateToken is
* @dev Only the owner can call this setter
* @param tokenURI New token URI
*/
function setTokenURI(string memory tokenURI) external onlyRole(DEFAULT_ADMIN_ROLE) {
function setTokenURI(string memory tokenURI) external onlyRole(ADMIN_ROLE) {
_getAggregateTokenStorage().tokenURI = tokenURI;
}

// Getter View Functions

/// @notice Version of the AggregateToken
function getVersion() external view returns (uint256) {
return _getAggregateTokenStorage().version;
}

/// @notice CurrencyToken used to mint and burn the AggregateToken
function getCurrencyToken() external view returns (IERC20) {
return _getAggregateTokenStorage().currencyToken;
Expand Down Expand Up @@ -469,7 +514,7 @@ contract AggregateToken is
* @param user Address of the user for which to get the unclaimed yield
* @return amount Amount of yield that the user has not yet claimed
*/
function unclaimedYield(address user) external view returns (uint256 amount) {
function unclaimedYield(address user) public view returns (uint256 amount) {
return totalYield(user) - claimedYield(user);
}

Expand Down
39 changes: 38 additions & 1 deletion nest/src/FakeComponentToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ contract FakeComponentToken is
IERC20 currencyToken;
/// @dev Number of decimals of the FakeComponentToken
uint8 decimals;
/// @dev Version of the FakeComponentToken
uint256 version;
}

// keccak256(abi.encode(uint256(keccak256("plume.storage.FakeComponentToken")) - 1)) & ~bytes32(uint256(0xff))
Expand Down Expand Up @@ -74,6 +76,13 @@ contract FakeComponentToken is

// Errors

/**
* @notice Indicates a failure because the given version is not higher than the current version
* @param invalidVersion Invalid version that is not higher than the current version
* @param version Current version of the FakeComponentToken
*/
error InvalidVersion(uint256 invalidVersion, uint256 version);

/**
* @notice Indicates a failure because the given CurrencyToken does not match actual CurrencyToken
* @param invalidCurrencyToken CurrencyToken that does not match the actual CurrencyToken
Expand Down Expand Up @@ -187,8 +196,31 @@ contract FakeComponentToken is
componentTokenAmount = amount;
}

/**
* @notice Claim yield for the given user
* @dev Anyone can call this function to claim yield for any user
* @param user Address of the user for which to claim yield
*/
function claimYield(address user) external returns (uint256 amount) {
amount = unclaimedYield(user);
_getFakeComponentTokenStorage().currencyToken.transfer(user, amount);
}

// Admin Setter Functions

/**
* @notice Set the version of the FakeComponentToken
* @dev Only the owner can call this setter
* @param version New version of the FakeComponentToken
*/
function setVersion(uint256 version) external onlyRole(DEFAULT_ADMIN_ROLE) {
FakeComponentTokenStorage storage $ = _getFakeComponentTokenStorage();
if (version <= $.version) {
revert InvalidVersion(version, $.version);
}
$.version = version;
}

/**
* @notice Set the CurrencyToken used to mint and burn the FakeComponentToken
* @dev Only the owner can call this setter
Expand All @@ -200,6 +232,11 @@ contract FakeComponentToken is

// Getter View Functions

/// @notice Version of the FakeComponentToken
function getVersion() external view returns (uint256) {
return _getFakeComponentTokenStorage().version;
}

/// @notice CurrencyToken used to mint and burn the FakeComponentToken
function getCurrencyToken() external view returns (IERC20) {
return _getFakeComponentTokenStorage().currencyToken;
Expand Down Expand Up @@ -243,7 +280,7 @@ contract FakeComponentToken is
* @param user Address of the user for which to get the unclaimed yield
* @return amount Amount of yield that the user has not yet claimed
*/
function unclaimedYield(address user) external view returns (uint256 amount) {
function unclaimedYield(address user) public view returns (uint256 amount) {
return totalYield(user) - claimedYield(user);
}

Expand Down
6 changes: 4 additions & 2 deletions nest/src/NestStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ contract NestStaking is Initializable, AccessControlUpgradeable, UUPSUpgradeable

// Constants

/// @notice Role for the admin of the Nest Staking protocol
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
/// @notice Role for the upgrader of the Nest Staking protocol
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

Expand Down Expand Up @@ -114,7 +116,7 @@ contract NestStaking is Initializable, AccessControlUpgradeable, UUPSUpgradeable
* @dev Only the owner can call this function
* @param aggregateToken AggregateToken to be featured
*/
function featureToken(IAggregateToken aggregateToken) external onlyRole(DEFAULT_ADMIN_ROLE) {
function featureToken(IAggregateToken aggregateToken) external onlyRole(ADMIN_ROLE) {
NestStakingStorage storage $ = _getNestStakingStorage();
if ($.isFeatured[aggregateToken]) {
revert TokenAlreadyFeatured(aggregateToken);
Expand All @@ -129,7 +131,7 @@ contract NestStaking is Initializable, AccessControlUpgradeable, UUPSUpgradeable
* @dev Only the owner can call this function
* @param aggregateToken AggregateToken to be unfeatured
*/
function unfeatureToken(IAggregateToken aggregateToken) external onlyRole(DEFAULT_ADMIN_ROLE) {
function unfeatureToken(IAggregateToken aggregateToken) external onlyRole(ADMIN_ROLE) {
NestStakingStorage storage $ = _getNestStakingStorage();
if (!$.isFeatured[aggregateToken]) {
revert TokenNotFeatured(aggregateToken);
Expand Down
4 changes: 4 additions & 0 deletions nest/src/interfaces/IComponentToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ interface IComponentToken is IERC20 {

function buy(IERC20 currencyToken, uint256 currencyTokenAmount) external returns (uint256 componentTokenAmount);
function sell(IERC20 currencyToken, uint256 currencyTokenAmount) external returns (uint256 componentTokenAmount);
function claimYield(address user) external returns (uint256 amount);

function getVersion() external view returns (uint256 version);
function getCurrencyToken() external view returns (IERC20 currencyToken);
function totalYield() external view returns (uint256 amount);
function claimedYield() external view returns (uint256 amount);
function unclaimedYield() external view returns (uint256 amount);
Expand Down

0 comments on commit 0f72241

Please sign in to comment.