Platform: Code4rena
Start Date: 01/09/2023
Pot Size: $36,500 USDC
Total HM: 4
Participants: 70
Period: 6 days
Judge: kirk-baird
Id: 281
League: ETH
Rank: 62/70
Findings: 1
Award: $7.08
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: adriro
Also found by: 0x6980, 0xStalin, 0xanmol, 0xmystery, 0xpanicError, Arz, Aymen0909, BenRai, Breeje, Lalanda, MohammedRizwan, Raihan, SovaSlava, Stormreckson, Udsen, ast3ros, bin2chen, castle_chain, catellatech, codegpt, dev0cloo, gkrastenov, hals, klau5, kutugu, ladboy233, matrix_0wl, nirlin, ohm, peanuts, pipidu83, sandy, wahedtalash77
7.08 USDC - $7.08
TransferShares
event parameter is not correctEmit TransferShares
with wrong value at wrap
function.
TransferShares
should emit sharesValue
. But at wrap
function, TransferShares
emits _USDYAmount
.
event TransferShares( address indexed from, address indexed to, uint256 sharesValue ); function wrap(uint256 _USDYAmount) external whenNotPaused { require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens"); _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR); usdy.transferFrom(msg.sender, address(this), _USDYAmount); emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount)); emit TransferShares(address(0), msg.sender, _USDYAmount); }
Manual Review
function wrap(uint256 _USDYAmount) external whenNotPaused { require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens"); _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR); usdy.transferFrom(msg.sender, address(this), _USDYAmount); emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount)); - emit TransferShares(address(0), msg.sender, _USDYAmount); + emit TransferShares(address(0), msg.sender, _USDYAmount * BPS_DENOMINATOR); }
Imports and inherits useless contract
rUSDY contract imports and inherits ContextUpgradeable contract, but there is no usage of this. There is no code using _msgData()
or _msgSender()
. This only increase deploy gas, so it would be better to remove unused import/inheritance.
import "contracts/external/openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; contract rUSDY is Initializable, ContextUpgradeable,
Manual Review
Remove import and inheritance of ContextUpgradeable.
Unused roles are defined and granted.
There are some roles which is defined/granted but not used.
This contract does not use role based access control.
Manual Review
Remove useless role and grant codes.
rUSDYDeployed
event parameter is not correctAt rUSDYDeployed
event emitting, name of rUSDY token is not same with real rUSDY name.
rUSDYDeployed
emits Ondo Rebasing U.S. Dollar Yield
as rUSDY name.
emit rUSDYDeployed( address(rUSDYProxy), address(rUSDYProxyAdmin), address(rUSDYImplementation), "Ondo Rebasing U.S. Dollar Yield", "rUSDY" );
But the rUSDY token name is Rebasing Ondo U.S. Dollar Yield
function name() public pure returns (string memory) { return "Rebasing Ondo U.S. Dollar Yield"; }
Manual Review
emit rUSDYDeployed( address(rUSDYProxy), address(rUSDYProxyAdmin), address(rUSDYImplementation), - "Ondo Rebasing U.S. Dollar Yield", + "Rebasing Ondo U.S. Dollar Yield", "rUSDY" );
Useless error defined
The InvalidPrice
error and PriceNotSet
error is defined but never used at RWADynamicOracle contract.
Manual Review
Remove InvalidPrice
and PriceNotSet
error at RWADynamicOracle contract.
The role and hashed string is not same at rUSDY contract.
bytes32 public constant USDY_MANAGER_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant BURNER_ROLE = keccak256("BURN_ROLE");
The role and hashed string is not same at rUSDY contract. This could make confusing.
And USDY_MANAGER_ROLE
should be changed to RUSDY_MANAGER_ROLE
Manual Review
Modify role name or hashed string.
preRebaseTokenAmount
and postRebaseTokenAmount
will be same value at SharesBurnt event
SharesBurnt
event emits preRebaseTokenAmount
and postRebaseTokenAmount
. But this two values are always same because oracle.getPrice()
is not changed.
/** * @notice An executed `burnShares` request * * @dev Reports simultaneously burnt shares amount * and corresponding rUSDY amount. * The shares amount is calculated twice: before and after the burning incurred rebase. * * @param account holder of the burnt shares * @param preRebaseTokenAmount amount of rUSDY the burnt shares (USDY) corresponded to before the burn * @param postRebaseTokenAmount amount of rUSDY the burnt shares (USDY) corresponded to after the burn * @param sharesAmount amount of burnt shares */ event SharesBurnt( address indexed account, uint256 preRebaseTokenAmount, uint256 postRebaseTokenAmount, uint256 sharesAmount ); function _burnShares( address _account, uint256 _sharesAmount ) internal whenNotPaused returns (uint256) { ... uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount); totalShares -= _sharesAmount; shares[_account] = accountShares - _sharesAmount; uint256 postRebaseTokenAmount = getRUSDYByShares(_sharesAmount); emit SharesBurnt( _account, preRebaseTokenAmount, postRebaseTokenAmount, _sharesAmount ); ... } function getRUSDYByShares(uint256 _shares) public view returns (uint256) { return (_shares * oracle.getPrice()) / (1e18 * BPS_DENOMINATOR); }
Manual Review
Emit just tokenAmount
instead of preRebaseTokenAmount
and postRebaseTokenAmount
.
#0 - c4-pre-sort
2023-09-08T08:36:44Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-09-21T10:14:45Z
kirk-baird marked the issue as grade-b