Dreamster ICO and Vesting

Smart Contract Audit Report

Dreamster ICO and Vesting Audit Report

Executive Summary

This report presents the outcomes of our collaborative engagement with the Dreamster team, focusing on the comprehensive evaluation of the Dreamster_Presale contract.

Our team conducted an initial security assessment from April 3rd to April 5th, 2024. On April 23rd, our team amended this report to reflect the project team's newly deployed contract on Sepolia that resolves all Findings. On May 10th, 2024, our team amended this report to reflect the contract's updated mainnet address. On June 24th, 2024, our team amended this report to reflect the contract's updated mainnet address.

The Dreamster ICO enables the purchasing of tokens via BNB or various ERC-20 tokens set by the team. A percentage of their purchase amount is received immediately, while the remaining tokens are vested throughout a period of time. We previously audited the Dreamster team's token contract here.


Audit Scope

Name

Source Code

Visualized

Dreamster_Presale

BSC Mainnet

Inheritance Chart.  Function Graph.

Name

Address/Source Code

Visualized

Dreamster_Presale

BSC Mainnet

Inheritance Chart.  Function Graph.


Audit Findings

All findings have been resolved, though some centralized aspects are present.

Finding #1

Dreamster_Presale

HighResolved

Finding #1 - Dreamster_Presale
HighResolved

Description: The buyToken() function allows the caller to specify any arbitrary duration for the vestDurationInMinutes parameter as it is not verified in the signature.

Risk/Impact: Any user can specify a short vestDurationInMinutes duration, enabling them to claim the remaining tokens due to them much earlier than intended by the team.

Recommendation: The vestDurationInMinutes value associated with the 'BuyOrder' should be verified in the signature via the verifySign() function to ensure that only vesting durations approved by the team can be used.

Resolution: The team has implemented the above recommendation.

Finding #2

Dreamster_Presale

HighResolved

Finding #2 - Dreamster_Presale
HighResolved

Description: In the claimLinearVestAmt() function, there's a risk that claimAmount can exceed availableAmount if enough time has elapsed between claims.

Risk/Impact: If claimAmount exceeds availableAmount, the transaction will revert due to an underflow error when trying to decrease availableAmount. As a result, the user will not be able to claim their remaining tokens.

Recommendation: An if-statement should be added to the claimLinearVestAmt() function that checks if claimAmount exceeds availableAmount. When true, claimAmount should be set equal to availableAmount to ensure that the user can claim all of their remaining tokens.

Resolution: The team has implemented the above recommendation.

Finding #3

Dreamster_Presale

HighResolved

Finding #3 - Dreamster_Presale
HighResolved

Description: In the if-statement in the linearVest() function, availableAmount is not updated to reflect the additional tokens being added to the user's vesting schedule.

if (linearVests[msg.sender].status == true) {
	linearVests[msg.sender].totalAmount += totalAmount;
	linearVests[msg.sender].amount += _amount;
	linearVests[msg.sender].vestDurationInMinutes += vestDurationInMinutes;
	linearVests[msg.sender].availableAmount;

Risk/Impact: These additional tokens purchased by the user can never be claimed.

Recommendation: availableAmount should be increased by _amount in the if-statement in the linearVest() function.

Resolution: The team has implemented the above recommendation.

Finding #4

Dreamster_Presale

HighResolved

Finding #4 - Dreamster_Presale
HighResolved

Description: The vestDurationInMinutes value for each vesting schedule does not represent the total duration in which tokens are vested.

Risk/Impact: The vestDurationInMinutes value is misleading and as a result could be incorrectly set, as it does not accurately reflect the expectations it implies.

Recommendation: The team should adjust the logic in the claimLinearVestAmt() function by properly following a linear vesting schedule to ensure vestDurationInMinutes represents the full duration until a user can claim 100% of the tokens they are entitled to.

Resolution: The team has modified the logic to now properly reflect vestDurationInMonths.

Finding #5

Dreamster_Presale

HighResolved

Finding #5 - Dreamster_Presale
HighResolved

Description: In the claimLinearVestAmt() function, the calculation extends the user's subsequent claim time proportionally to the delay in initiating their last claim. Consequently, the timing for the user's next claim is pushed further into the future, effectively penalizing users who delay their claims. This approach diverges from the intended linear vesting schedule.

linearVests[msg.sender].nextClaim += count * 2 minutes;

Risk/Impact: The overall vesting period is extended based on the time that a user waits in between claims, with the next claim time being set to reflect these delays.

Recommendation: We suggest that the team revisit and adjust the logic within the claimLinearVestAmt() function to guarantee a true linear vesting schedule, irrespective of the timing of user claims.

Resolution: The team has implemented the above recommendation.

Finding #6

Dreamster_Presale

MediumResolved

Finding #6 - Dreamster_Presale
MediumResolved

Description: The buyToken() function permits the caller to set the referalStatus parameter to 'refer' without conducting any verification to ensure its validity through the transaction's signature.

Risk/Impact: Any user can specify 'refer' for the referalStatus parameter and be able to receive an additional referral bonus without any verification by the team.

Recommendation: The referalStatus parameter associated with the 'BuyOrder should be verified in the signature via verifySign() function to enforce whether or not the referral status has been approved by the team.

Resolution: The team has implemented the above recommendation.

Finding #7

Dreamster_Presale

MediumResolved

Finding #7 - Dreamster_Presale
MediumResolved

Description: The verifySign() function does not include the contract address when hashing values for the transaction to verify the signature.

Risk/Impact: This could allow users to repurpose a signature meant for a different contract or chain, in the event a similar contract exists.

Recommendation: The verifySign() function should include this contract's address into the hash, ensuring signatures are specific to this contract alone.

Resolution: The team has implemented the above recommendation.

Finding #8

Dreamster_Presale

LowAcknowledged

Finding #8 - Dreamster_Presale
LowAcknowledged

Description: An inconsistency exists in the buyToken() function as, initially, 25% of the purchase amount is allocated to the recipient of the buyOrder, while the remaining 75% of tokens are vested towards the caller of the transaction.

Risk/Impact: Should the recipient and the caller not be the same address, this setup results in tokens being distributed to two separate addresses from a single purchase. This may lead to potential misalignment with the team's intended token distribution.

Recommendation: The buyToken() function should be modified to ensure tokens are both transferred and vested either to the caller or the recipient of the buyOrder, ensuring consistent token allocation on purchases.

Resolution: The team has indicated that this functionality is intended.

Finding #9

Dreamster_Presale

InformationalResolved

Finding #9 - Dreamster_Presale
InformationalResolved

Description: Although the contract inherits ReentrancyGuard, the nonReentrant modifier is not used in the contract.

Recommendation: The team could remove the ReentrancyGuard contract to reduce contract size and deployment costs.

Resolution: The team has removed the ReentrancyGuard contract.

Finding #10

Dreamster_Presale

InformationalResolved

Finding #10 - Dreamster_Presale
InformationalResolved

Description: The initialPercent state variable cannot be modified but is not declared constant.

Recommendation: The above state variable could be declared constant for additional gas savings on each reference.

Resolution: The team has added a function that allows them to update the initialPercent state variable.


System Overview

BUYING TOKENS

Any user can initiate a purchase by specifying the details of their buy order and details of a signature. Each signature nonce is marked as 'used' to prevent it from being used more than once. The returned signer address for the applicable transaction details must be the Signer address set by the team.

The total number of tokens allocated to the buyer is calculated based on the amount of BNB or tokens provided as payment and the current market price of the chosen payment type, obtained through a Chainlink AggregatorV3 contract. This calculation then incorporates the contract's token amount per USD value, set by the team. The specified amount of BNB or payment tokens are transferred from the caller to the owner. The caller must grant the contract a sufficient allowance in order for the transaction to successfully occur if the payment is not in BNB.

An initial percentage (set by the team) of the total number of purchased tokens is immediately transferred to the recipient address of the buy order. A new linear vesting schedule is created for the remaining tokens on behalf of the caller's address. An additional percentage of the total amount of purchased tokens is transferred from the contract to the provided referral address if the caller marks the transaction a referral.

LINEAR VESTING

Upon the creation of a vesting schedule via a purchase, the corresponding details of the vesting schedule are stored in the contract on behalf of the caller. Any user that has tokens vested on their behalf can initiate a claim once per 30-day period. The number of tokens transferred from the contract to the user is calculated linearly based on the total number of tokens allocated to their address in relation to their total vest duration. Once the user has claimed all of their tokens, the status of their vesting schedule is marked as inactive.

UPDATING PAYMENT DETAILS

The Admin role can update the payment details for a specified payment type at any time. Each payment type is assigned a name, the linkage to an AggregatorV3 price-fetch contract, the token address, the number of decimals, and its status of either active or inactive.

WITHDRAWING TOKENS/BNB

The Admin role can withdraw any tokens or BNB from the contract at any time. The team must ensure the contract has a sufficient token balance to support purchases and claims for all users.

SIGNER ADDRESS ROLE

Any address granted the Signer role can set the contract's Signer address to any address at any time. The original Signer address is revoked from the Signer role and the new Signer address is granted the Signer role in the process.

ADDITIONAL ADMIN ROLE CONTROLS

The Admin role can update the token amount per USD rate to any value at any time. The Admin role can update the referral percentage to any value at any time. The Admin role can update the contract's owner to any address at any time. The caller is revoked from the Admin role and the new owner is granted the Admin role in the process.


Vulnerability Analysis

Vulnerability Category Notes Result
Arbitrary Jump/Storage Write N/A PASS
Centralization of Control The Admin role can update the token amount per USD rate to any value at any time. PASS
Compiler Issues N/A PASS
Delegate Call to Untrusted Contract N/A PASS
Dependence on Predictable Variables N/A PASS
Ether/Token Theft N/A PASS
Flash Loans N/A PASS
Front Running N/A PASS
Improper Events N/A PASS
Improper Authorization Scheme N/A PASS
Integer Over/Underflow N/A PASS
Logical Issues N/A PASS
Oracle Issues N/A PASS
Outdated Compiler Version N/A PASS
Race Conditions N/A PASS
Reentrancy N/A PASS
Signature Issues N/A PASS
Sybil Attack N/A PASS
Unbounded Loops N/A PASS
Unused Code N/A PASS
Overall Contract Safety   PASS

About SourceHat

SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1800+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value!
Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!

Contact us today to get a free quote for a smart contract audit of your project!

What is a SourceHat Audit?

Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.

How Do I Interpret the Findings?

Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:

  • High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
  • Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
  • Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
  • Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.