A security review of the YouDonate-Protocol smart contract protocol was done by Parth and Rahul.
This audit report includes all the vulnerabilities, issues and code improvements found during the security review.
"Audits are a time, resource and expertise bound effort where trained experts evaluate smart
contracts using a combination of automated and manual techniques to find as many vulnerabilities
as possible. Audits can show the presence of vulnerabilities but not their absence."
- Secureum
Project Name | Crypta Digital |
Repository | https://github.com/YouDonate-Protocol/YouDonate-Protocol |
Commit hash | d849dfb80015331976f247cdee46630a8f2a11cd |
Documentation | Not provided |
Methods | Manual review |
Severity | Count |
---|---|
High risk | 6 |
Medium risk | 2 |
Low risk | 0 |
Informational | 5 |
Gas | 8 |
viewRewardsForTicketId
The above require condition will never be satisfied and always evaluate to false.
This condition does not require an &&
operator but rather an ||
operator
_calculateTotalPriceForBulkTickets
The problem could arise in the following cases:
_numberTickets > 1 + _discountDivisor
_numberTickets = 100
_discountDivisor = 300
Normally this won't be an issue. However, if someone calls the function setMaxNumberTicketsPerBuy
, then this function will start reverting since the price will become negative and all related transactions will revert.
Come with a new formula for the discounted price or account for the above mentioned cases
getPriceData
will not work in certain casesWhen the token in question has less than 3 decimal places or do not have a chainLinkFeed, the function would fail. This function would not work if the chainLinkFeed
for the particular token does not exist.
Include checks for the cases described above.
processProposal
before duration.The function can be called when the proposal voting time has expired. To either act on the proposal or cancel if not a majority yes vote.
Consider the following line of code:
This is wrongly checking the start of the voting period rather than its expiration.
The condition to check should be
In the function claimTickets
, if 4 last digits are matching and user gets a reward, the block of code will revert and not give user any rewards.
If the protocol wanted to impose the condition of only users with all last 5 digits matching, that should have been checked earlier.
If both the above mentioned code blocks exist, it would not be possible for this function to work properly without reverting.
Reconsider the reward mechanism or remove this condition.
claimTickets
function.Since this function claimTickets
is only callable by the user and they'll be the one passing their function params, this function refuses to give out ANY LEGITIMATE reward even if only one of the ticketIds
is passed incorrectly.
For example
The require
used in the for loop will revert for Any wrong ticketId and the user will not get ANY reward.
Make sure that the users can retrieve their funds even if one entry of their ticketsIds
is correct.
modifier notContract
breaks EIP4337 walletsThe issue is that the msg.sender == tx.origin
check forces people to use EOA wallets instead of smart contract wallets or EIP-4337-style account abstraction.
Also, for the other check of _isContract
, the following things need to be kept in mind:
It is unsafe to assume that an address for which this function returns false is an externally-owned account (EOA) and not a contract.
* Among others, isContract
will return false for the following types of addresses:
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
Furthermore, isContract
will also return true if the target contract within the same transaction is already scheduled for destruction by SELFDESTRUCT
, which only has an effect at the end of a transaction.
Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract constructor.
Either make a conscious decision to leave out people using the EIP-4337 style wallets or redo the design of the lottery itself.
constructor
initialized params.Incorrectly initialized constructor can lead to loss of time and economic resources. It would be better to include sanity checks to avoid them.
Add sanity checks for _YDTtokenAddress
and _randomGeneratorAddress
Consider using _randomGeneratorAddress != address(0)
and require(_isContract(randomGeneratorAddress))
In function startLottery
, the parameter uint256 _priceTicketInYDT
as it is. It is recommended for the ease of the user to create a mechanism to enter dollar value and convert it to equivalent YDT tokens.
In YDTSwapLottery
, there is a line of code which says the following: uint256 public constant MAX_LENGTH_LOTTERY = 7 days; // 4 days
Recommendation: Remove the 4 days comment and change it to 7 days.
safeTransfer
instead of transfer
YDTSwapLottery
uses SafeERC20 for ERC20 operations.
Recommendation: can use transfer since token YDT is trusted.
The names of userNumber
and winningTicketNumber
are swapped in function _calculateRewardsForTicketId
In function donateToProject
, there are hardcoded values of 95% and 5%. No way to adjust the percentages of splitting the amount.
Save _ticketNumber.length
as uint256 numTicketLength in the function buyTickets
Consider using custom errors instead of require strings to save gas.
For simple mathematical operations that are guaranteed not to overflow, consider using unchecked blocks.
Do not initialize variables that are being initialized now with their current value. For example: uint256 baseYield = 0;
can be changed to uint256 baseYield;
Pre-increment operators are cheaper than post-increment operators
Do not read values for length inside of a loop to save gas.
Consider changing the public functions which aren’t called anywhere else inside the contracts to external to save on gas costs.
In function setMinAndMaxTicketPriceInYDT
, consider replacing <= with < .