Vision Makers

CertiKFiles named in the report

CertiK reviewed Solidus Ai Tech (Vision Makers) on 12 Feb 2025: 16 findings, 0 critical; 10 marked fixed, 6 acknowledged.

No open critical findings
Open the reportOpen Solidus Ai Tech’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings16

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High021
Medium004
Low045
HighExcessive Owner PermissionsFixed

The owner has privilege to withdraw any token in the `LinearVestingPool` contract and transfer it to any `recipient` address. If a malicious actor got control of an address with the MANAGER_ROLE, entire token balance from the `LinearVestingPool` contract can be drained and users would not be able to claim any vested tokens. The MANAGER_ROLE also has the authority to change `token` address via the `changeToken()` function of the `PoolManager` contract, which could potentially cause users unable to withdraw tokens from the `LinearVestingPool` contract. Additionally, all the fees can be up to the `PERCENTAGE_DENOMINATOR`. When the refund fee equals the `PERCENTAGE_DENOMINATOR`, users would receive 0 when they call the `refund()` function.

MediumclaimFunds() Could Revert Due to Function ModifieFixed

The `claimFunds()` function of the `InvestmentPool` contract contains a `vesting.rescueTokens(projectFounder, tokensToReturn)` call. In the `LinearVestingPool` contract, the `rescueTokens()` function has a `onlyPoolManager` modifier, which restricts the caller to the `PoolManager` contract. As such, when the `InvestmentPool` contract calls `rescueTokens()`, the transaction would revert, which could make the project founder unable to call the `claimFunds()` function.

MediumIncorrect Amount of Payment Token ReleasedFixed

When users invest `paymentToken` into the `InvestmentPool` contract, the amount of token to be sold/vested is calculated as `totalTokenAmount = (investedAmount * totalTokensOnSale) / totalAllocation`. However, when users withdraw the vested tokens from the `LinearVestingPool` contract, the same `withdrawableAmount` is used to transfer the token to the user and to release `paymentToken` to the `projectFounder`, and there's no ratio based on `totalTokensOnSale / totalAllocation` being utilized. Depending on the actual ratio of `totalTokensOnSale / totalAllocation`, this means that either too much or too little `paymentToken` is being released to the project founder when users withdraw vested tokens.

MediumSome onlyOwner Functions Could Not be CalledFixed

In the `initialize()` function of the `LinearVestingPool` contract, the `investmentPool` contract is set to be the `Owner` of the `LinearVestingPool` contract in line 149. The `revoke()`, `updateStreamStatus()`, and `updateStream()` functions all have the `onlyOwner` modifiers, which means only the `investmentPool` contract can call these functions. However, in the `investmentPool` contract, there's no mechanism to call these functions. Note that in the `LinearVestingPool.js` test file, line 34 sets the `deployer` to be the `Owner` instead of the `investmentPool` contract, which is inconsistent with the actual behavior of the `PoolManager` and the `LinearVestingPool` contracts.

MediumupdatePool Function Cannot be CalledFixed

The `updatePool()` function contains the `onlyPoolManager` modifier, but the `PoolManager` contract does not contain a method to call the `updatePool()` function of the `LinearVestingPool` contract, which means that this function cannot be called.

LowEvent Not IndexedFixed

If an event is not indexed in a smart contract, it means that the event's parameters are not tagged with the `indexed` keyword. This has implications for how the event data can be searched and filtered when looking through blockchain logs. Without indexing, the event will still emit the data as part of the transaction log, but users won't be able to query for these events using the parameters. They'll have to retrieve the entire set of logs and manually sift through them to find events with the specific data. This can be less efficient and more time-consuming, especially on a blockchain with a high volume of transactions and events.

LowMissing Parameter Validation Between Vesting andFixed

In the `PoolManager` contract, when new `vestingPool` and `investmentPool` contracts are deployed and initialized, there is parameter validation within each contract individually. However, there's no parameter validation between the `investment` and `vesting` parameters, such as the start time of investment and vesting. Incorrectly configured parameters could potentially cause unintended consequences.

LowPayment Tokens are Only Released when WithdrawnAmFixed

When users call the `withdraw()` function, the `releaseFundsToProjectFounder()` function is only called when `stream.withdrawnAmount == 0`. If a user calls the `withdraw()` function when only a tiny fraction of token has vested, the function would only release a tiny amount of payment token to the project founder. When all the tokens are fully vested, no additional payment token would be released to project founder, as line 393 would not be `true` in subsequent calls to the `withdraw()` function. Furthermore, given that there's no check between the `msg.sender` and the provided `wallet` address, a malicious actor could use the trick above on all active streams even if they are not the corresponding `wallet` address.

LowPossible Loss Of the Data Due to Indexed DynamicFixed

When attempting to index dynamic data types like `string`, `bytes`, `array`, or `struct` in Solidity, they don't get stored in their original form. Instead, the Ethereum log system stores the `Keccak-256` hash of these data types. While this approach ensures efficiency and cost-effectiveness, developers must be aware of it to correctly use and interpret logs, and they cannot retrieve the original string from its hash alone.

LowTime Check in `claimFunds()` and `invest()` FunctFixed

The `claimFunds()` and `invest()` functions in the `InvestmentPool` contract contain a time range check based on the `endDate` variable. When `block.timestamp == endDate`, it satisfies the conditions of both functions.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (3)

  • contracts/LinearVestingPool.sol
  • contracts/InvestmentPool.sol
  • contracts/PoolManager.sol

Part of Solidus Ai Tech’s security record

Solidus Ai Tech
Audits4 audits by 2 firmsLast audit Feb 2025, by CertiK.
Critical findingsNo open critical findingsAcross 4 reports.
Open Solidus Ai Tech’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us