ForceFi Info

Forcefi brings together the best brains of Web3 to support the launch and growth of your project. Get access to opportunities, talent, incentives, and a meaningful network.

ForceFi Logo

Team and KYC Verification

The team has securely submitted their personal information to SolidProof.io for verification.

In the event of any fraudulent activities, this information will be promptly reported to the relevant authorities to ensure accountability and compliance.

TrustNet Score

The TrustNet Score evaluates crypto projects based on audit results, security, KYC verification, and social media presence. This score offers a quick, transparent view of a project's credibility, helping users make informed decisions in the Web3 space.

91.46
Poor Excellent

Real-Time Threat Detection

Real-time threat detection, powered by Cyvers.io, is currently not activated for this project.

This advanced feature provides continuous monitoring and instant alerts to safeguard your assets from potential security threats. Real-time detection enhances your project's security by proactively identifying and mitigating risks. For more information, click here.

Security Assessments

Select the audit
"Static Analysis Dynamic Analysis Symbolic Execution SWC Check Manual Review"
Contract address
N/A
Network N/A
License N/A
Compiler N/A
Type N/A
Language Solidity
Onboard date 2025/06/25
Revision date 2025/06/25

Summary and Final Words

No crucial issues found

The contract does not contain issues of high or medium criticality. This means that no known vulnerabilities were found in the source code.

Contract owner cannot mint

It is not possible to mint new tokens.

Contract owner cannot blacklist addresses.

It is not possible to lock user funds by blacklisting addresses.

Contract owner cannot set high fees

The fees, if applicable, can be a maximum of 25% or lower. The contract can therefore not be locked. Please take a look in the comment section for more details.

Contract cannot be locked

Owner cannot lock any user funds.

Token cannot be burned

There is no burning within the contract without any allowances

Ownership is not renounced

The owner retains significant control, which could potentially be used to modify key contract parameters.

Contract is not upgradeable

The contract does not use proxy patterns or other mechanisms to allow future upgrades. Its behavior is locked in its current state.

Scope of Work

This audit encompasses the evaluation of the files listed below, each verified with a SHA-1 Hash. The team referenced above has provided the necessary files for assessment.

The auditing process consists of the following systematic steps:

  1. Specification Review: Analyze the provided specifications, source code, and instructions to fully understand the smart contract's size, scope, and functionality.
  2. Manual Code Examination: Conduct a thorough line-by-line review of the source code to identify potential vulnerabilities and areas for improvement.
  3. Specification Alignment: Ensure that the code accurately implements the provided specifications and intended functionalities.
  4. Test Coverage Assessment: Evaluate the extent and effectiveness of test cases in covering the codebase, identifying any gaps in testing.
  5. Symbolic Execution: Analyze the smart contract to determine how various inputs affect execution paths, identifying potential edge cases and vulnerabilities.
  6. Best Practices Evaluation: Assess the smart contracts against established industry and academic best practices to enhance efficiency, maintainability, and security.
  7. Actionable Recommendations: Provide detailed, specific, and actionable steps to secure and optimize the smart contracts.

A file with a different Hash has been intentionally or otherwise modified after the security review. A different Hash may indicate a changed condition or potential vulnerability that was not within the scope of this review.

Final Words

The following provides a concise summary of the audit report, accompanied by insightful comments from the auditor. This overview captures the key findings and observations, offering valuable context and clarity.


Ownership Privileges
ForceFiBaseContract.sol
  • The owner can set the fee amount to any arbitrary value.
  • The owner can update the forcefipackagaddress to any arbitrary address.
  • The owner can withdraw ETH from the contract. 
Fundraising.sol
  • The owner can set the successfulFundraiseFeeAddress.
  • The fundraising owner can whitelist addresses for a private fundraising campaign.
  • The fundraising owner can close their successful campaign, which triggers the distribution of funds.
  • The fundraising owner can unlock all tokens from their campaign if it fails.
  • The owner can set the referralFee percentage to not more than 10%.
  • The owner can set the forcefiStakingAddress.
  • The owner can set the curatorContractAddress.
  • The owner can whitelist any token for investment and associate it with a data feed address.
  • The owner can whitelist the native currency for investment and associate it with an arbitrary data feed address.
  • The owner can set the campaign creation feeAmount to any arbitrary value.
  • The owner can set the forcefiPackageAddress to any arbitrary address.
  • The owner can withdraw the entire native currency balance of the contract to any arbitrary address.

Note - This Audit report consists of a security analysis of the ForceFi Fundraising smart contract. This analysis did not include functional testing (or unit testing) of the contract’s logic. Moreover, we only audited one token contract for the ForceFi team. Other contracts associated with the project were not audited by our team. We recommend investors do their own research before investing.

Files and details

Functions
public

/

State variables
public

/

Total lines
of code

/

Capabilities
Hover on items

/

Functions
public

/

State variables
public

/

Total lines
of code

/

Capabilities
Hover on items

/

Findings and Audit result

high Issues | 3 findings

Resolved

#1 high Issue
Owner Can drain All Native Currency (ETH) Funds
ForcefiBaseContract.sol
L65-67
Description

The withdrawFee function in ForcefiBaseContract.sol allows the contract owner to withdraw the entire native currency (ETH) balance of the contract using receiver.transfer(address(this).balance). This function does not distinguish between collected fees and investors' contributed funds. This gives the owner a "backdoor" to drain all ETH contributed by investors across all campaigns at any time.

Resolved

#2 high Issue
Excessive Centralization of Core Parameters
Fundraising.sol
L450-460
L782-784
Description

The contract owner can modify core components of the protocol at will. This includes changing fee structures (setFeeConfig), whitelisting tokens with potentially malicious price feeds (whitelistTokenForInvestment), changing the staking contract (setForcefiStakingAddress), and altering where fees are sent (setSuccessfulFundraiseFeeAddress). This level of control means the owner could manipulate the system to their benefit, for example, by setting a malicious staking contract to drain user funds or whitelisting a token with a manipulated price feed to disrupt fundraising economics.

Resolved

#3 high Issue
Incorrect Investment Calculation Leads to Incorrect User Payments
Fundraising.sol
L496-533
L541-582
Description

The core logic used to calculate the payment amount in the invest and investWithNativeCurrency functions is mathematically incorrect, making the primary function of the contract unusable and dangerous. The formula incorrectly multiplies the total purchase cost by the price of the payment token, instead of dividing by it. For example, if an investor wants to buy 1,000 project tokens priced at $2 each (a $2,000 total cost) and wants to pay with ETH priced at $4,000, the correct amount to pay is $2,000 / $4,000 = 0.5 ETH. The contract's flawed formula, however, calculates (price of token * quantity) * price of ETH, which would result in an astronomical figure, (2 * 1000) * 4000 = 8,000,000 ETH, an impossibly large amount that guarantees the transaction will fail.

medium Issues | 8 findings

Resolved

#1 medium Issue
closeCampaign is Vulnerable to Cross-Function Reentrancy
Fundraising.sol
L561-690
Description

The closeCampaign function makes multiple external calls (e.g., to referral, staking, and curator addresses) before all state changes are finalized. The nonReentrant modifier only prevents closeCampaign from re-entering itself, not another function like reclaimTokens. A malicious referral contract, for example, could receive its fee and immediately call reclaimTokens before closeCampaign is finished, allowing the attacker to illegitimately "reclaim" an investment from a successful campaign.

Resolved

#2 medium Issue
Incorrect Logic in reclaimTokens
Fundraising.sol
L740-776
Description

The reclaimTokens function contains a critical logic flaw that allows investors to withdraw their funds from a campaign that has already successfully met its funding goal. The function incorrectly permits reclamation during the reclaimWindow even if the campaign was successful, which fundamentally undermines the commitment of a fundraising event. For example, consider a successful campaign that ends on November 15th with a 7-day reclaim window. An investor can call reclaimTokens on November 17th. The function's check if (hasReachedLimit) will be true, and the subsequent check require(block.timestamp <= fundraising.endDate + feeConfig.reclaimWindow) will also pass, as November 17th is within the reclaim period. The contract then proceeds to return the investor's full contribution. This allows investors to back out of a successful project, creating uncertainty for the project owner and potentially causing the total funds raised to drop below the minimum threshold after the fact.

Resolved

#3 medium Issue
Lack of Stale Price Check in Chainlink Oracle
Fundraising.sol
L830-858
Description

The getChainlinkDataFeedLatestAnswer function fetches the price from a Chainlink data feed but fails to check if the price is recent or if the round was valid. If an oracle reports a stale price or a price of zero, the invest functions will use this incorrect price, which can lead to catastrophic miscalculations of investment amounts.For example, an oracle for USDC/USD stops updating. Its last price was $1. The real price of USDC crashes to $0.10. The contract will use the stale price of $1 and require 10x more ETH from an investor than it should, causing a significant loss for the user.

Resolved

#4 medium Issue
Unsafe Owner Initialization with tx.origin
ForcefiBaseContract.sol
L39
Description

The ForcefiBaseContract constructor sets the owner using Ownable(tx.origin). If the deployment is performed through a contract (like a multisig wallet), the owner will be set to the original user who started the transaction, not the deploying contract. This can lead to loss of ownership or ownership being assigned to an unintended, single address.

Resolved

#5 medium Issue
Missing validation for tgePercent
Fundraising.sol
L301-363
Description

The createFundraising function does not validate that _tgePercent is less than or equal to 100. If a value > 100 is used, the vesting calculation in computeReleasableAmount will attempt an integer underflow (invested - tgeCalculatedAmount), which will always revert. This makes it impossible for any investor in that campaign to ever claim their vested tokens.

Resolved

#6 medium Issue
Missing input validation
Fundraising.sol
L301-363
Description

The createFundraising function lacks essential validation checks on the _fundraisingData input, which allows a project owner to create a campaign that is logically flawed or non-functional from its inception. Specifically, the function fails to ensure that critical parameters make sense in relation to each other or to fundamental rules. For example, a user can set the _rateDelimiter to zero, which will cause a division-by-zero error in every investment transaction, permanently blocking any funds from being raised. Similarly, the _campaignMinTicketLimit can be set higher than the _campaignMaxTicketLimit, creating an impossible condition where no valid investment can ever be made. The function also fails to verify that the _startDate occurs before the _endDate, allowing for the creation of campaigns with illogical timelines.

Resolved

#7 medium Issue
Failed Fee Transfers Incorrectly Benefit Campaign Owner
Fundraising.sol
L561-690
Description

The function uses try/catch blocks to handle the transfer of native currency (ETH) fees to the staking and curator contracts. The comment states that if the transfer fails, "the fee remains with campaign owner." This logic is implemented by only incrementing distributedFees on a successful transfer. The final payout to the campaign owner is totalFundraisedInWei - distributedFees. Therefore, if a fee transfer fails, the distributedFees amount is lower, and the amountToSender (the campaign owner's payout) becomes larger, effectively redirecting the failed fee payment to the owner. This creates a perverse incentive for a campaign owner to use staking or curator contracts that are intentionally designed to not accept ETH, allowing the owner to pocket those fees.

Acknowledged

#8 medium Issue
Fee-on-Transfer Tokens Break Accounting and Lock Funds
Fundraising.sol
L589-718
Description

The closeCampaign function's accounting logic will fail if any investment is made with a fee-on-transfer token, leading to all funds from that token being permanently locked in the contract. The contract incorrectly assumes that its internal record of funds received (fundraisingBalance) is identical to its actual token balance. This is not true for fee-on-transfer tokens. For example, if an investor sends 100 tokens that have a 10% transfer tax, the contract's fundraisingBalance will be updated by 100, but its actual balance will only increase by 90. When closeCampaign is called, it will calculate all fee distributions based on the inflated, incorrect balance of 100. This will cause the final transfer to the project owner to fail with an "insufficient funds" error, because the contract is trying to distribute more tokens than it actually holds. Since this final transfer will always fail, the closeCampaign function can never successfully complete, and the funds will be trapped in the contract forever.

low Issues | 3 findings

Resolved

#1 low Issue
Missing zero or dead address check
ForcefiBaseContract.sol
L56-58
Description

It is recommended to check that the address cannot be set to zero or dead address.

Resolved

#2 low Issue
Missing events arithmetic
Fundraising.sol
L424-426
L450-460
L782-784
L791-793
L800-802
L810-813
L820-823
Description

Emit all the critical parameter changes.

Resolved

#3 low Issue
Missing zero or dead address check
Fundraising.sol
L424-426
L782-784
L791-793
L800-802
L810-813
L820-823
Description

It is recommended to check that the address cannot be set to zero or dead address.