Sparkn

CodeFox Inc.
DeFiFoundryProxy
15,000 USDC
View results
Submission Details
Severity: low
Valid

LOW RISK

Number Issue Instances
[L-01] ignature use at deadlines should be allowed 5
[L-02] Unsafe downcast 1
[L-03] Missing checks for address(0x0) in the constructor 1
[L-04] Array lengths not checked 2
[L-05] Using zero as a parameter 3
[L-06] Functions calling contracts/addresses with transfer hooks are missing reentrancy guards 2
[L-07] Use Ownable2Step rather than Ownable 1
[L-08] External call recipient may consume all transaction gas 1
[L-09] Owner can renounce while system is paused 3
[L-10] Some tokens may revert when zero value transfers are made 1

[L‑01] Signature use at deadlines should be allowed

According to EIP-2612, signatures used on exactly the deadline timestamp are supposed to be allowed. While the signature may or may not be used for the exact EIP-2612 use case (transfer approvals), for consistency's sake, all deadlines should follow this semantic. If the timestamp is an expiration rather than a deadline, consider whether it makes more sense to include the expiration timestamp as a valid timestamp, as is done for deadlines.

file: src/ProxyFactory.sol
110 if (closeTime > block.timestamp + MAX_CONTEST_PERIOD || closeTime < block.timestamp) {
134 if (saltToCloseTime[salt] > block.timestamp) revert ProxyFactory__ContestIsNotClosed();
163 if (saltToCloseTime[salt] > block.timestamp) revert ProxyFactory__ContestIsNotClosed();
187 if (saltToCloseTime[salt] + EXPIRATION_TIME > block.timestamp) revert ProxyFactory__ContestIsNotExpired();
216 if (saltToCloseTime[salt] + EXPIRATION_TIME > block.timestamp) revert ProxyFactory__ContestIsNotExpired();

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/ProxyFactory.sol#L#L110

[L‑02] Unsafe downcast

When a type is downcast to a smaller type, the higher order bits are truncated, effectively applying a modulo to the original value. Without any other checks, this wrapping will lead to unexpected behavior and bugs

file: src/ProxyFactory.sol
228 proxy = address(uint160(uint256(hash)));

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/ProxyFactory.sol#L228

[L‑03] Missing checks for address(0x0) in the constructor

file: main/src/Proxy.sol
45 _implementation = implementation;

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/Proxy.sol#L45

[L-04] Array lengths not checked

If the length of the arrays are not required to be of the same length, user operations may not be fully executed due to a mismatch in the number of items iterated over, versus the number of items provided in the second array

file: src/Distributor.sol
92 function distribute(address token, address[] memory winners, uint256[] memory percentages, bytes memory data)
external
{
116 function _distribute(address token, address[] memory winners, uint256[] memory percentages, bytes memory data)
internal
{

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/Distributor.sol#L92-L94

[L-05] Using zero as a parameter

Taking 0 as a valid argument in Solidity without checks can lead to severe security issues. A historical example is the infamous 0x0 address bug where numerous tokens were lost. This happens because '0' can be interpreted as an uninitialized address, leading to transfers to the '0x0' address, effectively burning tokens. Moreover, 0 as a denominator in division operations would cause a runtime exception. It's also often indicative of a logical error in the caller's code. It's important to always validate input and handle edge cases like 0 appropriately. Use require() statements to enforce conditions and provide clear error messages to facilitate debugging and safer code.

file: src/Proxy.sol
55 calldatacopy(ptr, 0, calldatasize())
56 let result := delegatecall(gas(), implementation, ptr, calldatasize(), 0, 0)
58 returndatacopy(ptr, 0, size)

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/Proxy.sol#L55

[L‑06] Functions calling contracts/addresses with transfer hooks are missing reentrancy guards

Even if the function follows the best practice of check-effects-interaction, not using a reentrancy guard when there may be transfer hooks will open the users of this protocol up to read-only reentrancies with no way to protect against it, except by block-listing the whole protocol.

file: src/Distributor.sol
147 erc20.safeTransfer(winners[i], amount);
164 token.safeTransfer(STADIUM_ADDRESS, token.balanceOf(address(this)));

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/Distributor.sol#L147

[L‑07] Use Ownable2Step rather than Ownable

Ownable2Step and Ownable2StepUpgradeable prevent the contract ownership from mistakenly being transferred to an address that cannot handle it (e.g. due to a typo in the address), by requiring that the recipient of the owner permissions actively accept via a contract call of its own.

file: src/ProxyFactory.sol
37 contract ProxyFactory is Ownable, EIP712 {

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/ProxyFactory.sol#L37

[L‑08] External call recipient may consume all transaction gas

There is no limit specified on the amount of gas used, so the recipient can use up all of the transaction's gas,

file: src/Distributor.sol
147 erc20.safeTransfer(winners[i], amount);

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/Distributor.sol#L147

[L‑09] Owner can renounce while system is paused

The contract owner or single user with a role is not prevented from renouncing the role/ownership while the contract is paused, which would cause any user assets stored in the protocol, to be locked indefinitely

file: src/ProxyFactory.sol
105 function setContest(address organizer, bytes32 contestId, uint256 closeTime, address implementation)
public
onlyOwner
{
179 function deployProxyAndDistributeByOwner(
address organizer,
bytes32 contestId,
address implementation,
bytes calldata data
) public onlyOwner returns (address) {
205 function distributeByOwner(
address proxy,
address organizer,
bytes32 contestId,
address implementation,
bytes calldata data
) public onlyOwner {

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/ProxyFactory.sol#L105-L108

[L‑10] Some tokens may revert when zero value transfers are made

In spite of the fact that EIP-20 states that zero-valued transfers must be accepted, some tokens, such as LEND will revert if this is attempted, which may cause transactions that involve other tokens (such as batch operations) to fully revert. Consider skipping the transfer if the amount is zero, which will also save gas.

file: src/Distributor.sol
142 if (totalAmount == 0) revert Distributor__NoTokenToDistribute();
uint256 winnersLength = winners.length; // cache length
for (uint256 i; i < winnersLength;) {
uint256 amount = totalAmount * percentages[i] / BASIS_POINTS;
erc20.safeTransfer(winners[i], amount);
unchecked {
++i;
}
}

https://github.com/Cyfrin/2023-08-sparkn/blob/main/src/Distributor.sol#L142

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.