Skip to content

Commit

Permalink
[PDE-251] [Fix] ottersec advisory check issues (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
ungaro authored Dec 19, 2024
1 parent 3f45371 commit d0fb773
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 280 deletions.
2 changes: 1 addition & 1 deletion smart-wallets/lib/openzeppelin-contracts-upgradeable
71 changes: 70 additions & 1 deletion smart-wallets/src/interfaces/IYieldDistributionToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,84 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IYieldDistributionToken is IERC20 {

function getCurrencyToken() external returns (IERC20 currencyToken);
/**
* @notice Emitted when yield is deposited into the YieldDistributionToken
* @param user Address of the user who deposited the yield
* @param currencyTokenAmount Amount of CurrencyToken deposited as yield
*/
event Deposited(address indexed user, uint256 currencyTokenAmount);

/**
* @notice Emitted when yield is claimed by a user
* @param user Address of the user who claimed the yield
* @param currencyTokenAmount Amount of CurrencyToken claimed as yield
*/
event YieldClaimed(address indexed user, uint256 currencyTokenAmount);

/**
* @notice Emitted when yield is accrued to a user's balance
* @param user Address of the user who accrued the yield
* @param currencyTokenAmount Amount of CurrencyToken accrued as yield
*/
event YieldAccrued(address indexed user, uint256 currencyTokenAmount);

/// @notice Indicates a failure because a yield deposit is made in the same block as the last one
error DepositSameBlock();

/**
* @notice Indicates a failure because the transfer of CurrencyToken failed
* @param user Address of the user whose transfer failed
* @param currencyTokenAmount Amount of CurrencyToken to be transferred
*/
error TransferFailed(address user, uint256 currencyTokenAmount);

/**
* @notice Get the token used for yield distribution
* @return currencyToken The ERC20 token used for yield payments
*/
function getCurrencyToken() external view returns (IERC20 currencyToken);

/**
* @notice Claim accumulated yield for a user
* @param user Address of the user to claim yield for
* @return currencyToken Token in which yield is paid
* @return currencyTokenAmount Amount of yield claimed
*/
function claimYield(
address user
) external returns (IERC20 currencyToken, uint256 currencyTokenAmount);

/**
* @notice Update and accrue yield for a specific user
* @dev Anyone can call this function to update a user's yield
* @param user Address of the user to accrue yield for
*/
function accrueYield(
address user
) external;

/**
* @notice Request yield distribution from a specific address
* @dev Implementation depends on the specific yield source
* @param from Address to request yield from
*/
function requestYield(
address from
) external;

/**
* @notice Get the URI for the token metadata
* @return The URI string pointing to the token's metadata
*/
function getTokenURI() external view returns (string memory);

/**
* @notice Calculate the pending yield for a user that hasn't been accrued yet
* @param user Address of the user to check pending yield for
* @return Amount of pending yield in CurrencyToken
*/
function pendingYield(
address user
) external view returns (uint256);

}
10 changes: 10 additions & 0 deletions smart-wallets/src/mocks/MockAssetToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ contract MockAssetToken is IAssetToken, ERC20Upgradeable, OwnableUpgradeable {
return (_currencyToken, 0);
}

function pendingYield(
address user
) external override view returns (uint256) {
// Mock implementation for testing
}

function getTokenURI() external view override returns (string memory) {
// Mock implementation for testing
}

function getBalanceAvailable(
address user
) external view override returns (uint256) {
Expand Down
10 changes: 10 additions & 0 deletions smart-wallets/src/mocks/MockInvalidAssetToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ contract MockInvalidAssetToken is IAssetToken {
address
) external pure override { }

function pendingYield(
address user
) external override view returns (uint256) {
// Mock implementation for testing
}

function getTokenURI() external view override returns (string memory) {
// Mock implementation for testing
}

function totalSupply() external pure override returns (uint256) {
return 0;
}
Expand Down
97 changes: 18 additions & 79 deletions smart-wallets/src/token/AssetToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ contract AssetToken is WalletUtils, YieldDistributionToken, IAssetToken {
struct AssetTokenStorage {
/// @dev Total value of all circulating AssetTokens
uint256 totalValue;
/// @dev Whitelist of users that are allowed to hold AssetTokens
address[] whitelist;
/// @dev Mapping of whitelisted users
mapping(address user => bool whitelisted) isWhitelisted;
/// @dev List of all users that have ever held AssetTokens
Expand Down Expand Up @@ -90,6 +88,13 @@ contract AssetToken is WalletUtils, YieldDistributionToken, IAssetToken {
*/
error AddressNotWhitelisted(address user);

/**
* @notice Indicates holder status change event
* @param holder Address of Holder
* @param isHolder true when becomes holder, false when stops being holder
*/
event HolderStatusChanged(address indexed holder, bool isHolder);

// Constructor

/**
Expand Down Expand Up @@ -125,7 +130,6 @@ contract AssetToken is WalletUtils, YieldDistributionToken, IAssetToken {
if (owner == address(0)) {
revert InvalidAddress();
}
$.whitelist.push(owner);
$.isWhitelisted[owner] = true;
emit AddressAddedToWhitelist(owner);
}
Expand Down Expand Up @@ -157,11 +161,19 @@ contract AssetToken is WalletUtils, YieldDistributionToken, IAssetToken {
if (getBalanceAvailable(from) < value) {
revert InsufficientBalance(from);
}

if (balanceOf(from) == value) {
// Will have zero balance after transfer
emit HolderStatusChanged(from, false);
}
}

if (!$.hasHeld[to]) {
$.holders.push(to);
$.hasHeld[to] = true;
if (to != address(0)) {
// Check if to address will become a new holder
if (balanceOf(to) == 0) {
// Currently has zero balance
emit HolderStatusChanged(to, true);
}
}

super._update(from, to, value);
Expand Down Expand Up @@ -196,7 +208,6 @@ contract AssetToken is WalletUtils, YieldDistributionToken, IAssetToken {
if ($.isWhitelisted[user]) {
revert AddressAlreadyWhitelisted(user);
}
$.whitelist.push(user);
$.isWhitelisted[user] = true;
emit AddressAddedToWhitelist(user);
}
Expand All @@ -219,15 +230,6 @@ contract AssetToken is WalletUtils, YieldDistributionToken, IAssetToken {
if (!$.isWhitelisted[user]) {
revert AddressNotWhitelisted(user);
}
address[] storage whitelist = $.whitelist;
uint256 length = whitelist.length;
for (uint256 i = 0; i < length; ++i) {
if (whitelist[i] == user) {
whitelist[i] = whitelist[length - 1];
whitelist.pop();
break;
}
}
$.isWhitelisted[user] = false;
emit AddressRemovedFromWhitelist(user);
}
Expand Down Expand Up @@ -280,11 +282,6 @@ contract AssetToken is WalletUtils, YieldDistributionToken, IAssetToken {
return _getAssetTokenStorage().totalValue;
}

/// @notice Whitelist of users that are allowed to hold AssetTokens
function getWhitelist() external view returns (address[] memory) {
return _getAssetTokenStorage().whitelist;
}

/**
* @notice Check if the user is whitelisted
* @param user Address of the user to check
Expand Down Expand Up @@ -340,62 +337,4 @@ contract AssetToken is WalletUtils, YieldDistributionToken, IAssetToken {
}
}

/// @notice Total yield distributed to all AssetTokens for all users
function totalYield() public view returns (uint256 amount) {
AssetTokenStorage storage $ = _getAssetTokenStorage();
uint256 length = $.holders.length;
for (uint256 i = 0; i < length; ++i) {
amount += _getYieldDistributionTokenStorage().userStates[$.holders[i]].yieldAccrued;
}
}

/// @notice Claimed yield across all AssetTokens for all users
function claimedYield() public view returns (uint256 amount) {
AssetTokenStorage storage $ = _getAssetTokenStorage();
address[] storage holders = $.holders;
uint256 length = holders.length;
for (uint256 i = 0; i < length; ++i) {
amount += _getYieldDistributionTokenStorage().userStates[$.holders[i]].yieldWithdrawn;
}
}

/// @notice Unclaimed yield across all AssetTokens for all users
function unclaimedYield() external view returns (uint256 amount) {
return totalYield() - claimedYield();
}

/**
* @notice Total yield distributed to a specific user
* @param user Address of the user for which to get the total yield
* @return amount Total yield distributed to the user
*/
function totalYield(
address user
) external view returns (uint256 amount) {
return _getYieldDistributionTokenStorage().userStates[user].yieldAccrued;
}

/**
* @notice Amount of yield that a specific user has claimed
* @param user Address of the user for which to get the claimed yield
* @return amount Amount of yield that the user has claimed
*/
function claimedYield(
address user
) external view returns (uint256 amount) {
return _getYieldDistributionTokenStorage().userStates[user].yieldWithdrawn;
}

/**
* @notice Amount of yield that a specific user has not yet claimed
* @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) {
UserState memory userState = _getYieldDistributionTokenStorage().userStates[user];
return userState.yieldAccrued - userState.yieldWithdrawn;
}

}
Loading

0 comments on commit d0fb773

Please sign in to comment.