As of 3 Nov 2026, LiquiFi has 1 audit report on Trustblock, by Quantstamp, dated 27 Jan 2022. That report lists no open critical findings.
Record last updated . It holds audits and findings, contracts and incidents up to Nov 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| LiquiFi | Security firm: Quantstamp | Date: | Scope: Files named in the report | No open critical findings | Open · certificate.quantstamp.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 0 | 2 |
| Medium | 0 | 1 | 1 |
| Low | 2 | 0 | 15 |
The function accepts a parameter called `_totalSupply` but does not utilize it properly. It neither checks whether `_cadenceSeconds * _cadenceAmount` is less than the `_totalSupply`, nor whether there are rounding issues that will lead to lost tokens. In case the company owner made a mistake with the inputs, the tokens might vest a lot faster than intended or require manual intervention of the contract owner to retrieve the locked tokens.
Open the reportThe contract does not have any functionality to allow a company to stop vesting a former employee. There are two kinds of employees, `activeEmployees` and `inactiveEmployees`. The contract keeps track of them but never utilizes this information in regard to enabling/disabling of vesting.
Open the reportFunctions `Escrow.deposit()` and `Escrow.withdraw()` are using the _unsafe_ ERC20 interface functions of OpenZeppelin (`transferFrom()` and `transfer()`). Since [some ERC20 tokens do not return a value for these functions](https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca) these calls might revert.
Open the reportThe `createPlan` function in the modules contracts does not check whether a plan with the provided ID already exists, allowing the owner to overwrite any existing plan.
Open the reportAccording to the comments, the function `EmployeeRegistry.isOwner()` should return `true` only if the provided address parameter is the owner of of the provided company id parameter and `false` otherwise. However, as the `exists` return value of `EnumerableMap.tryGet(companyOwners, _companyId)` is not checked, providing `address(0)` to any company id would lead to returning `owner == _employee` equal to `true` and therefore violating the comments assumption.
Open the reportThere are two issues: 1. The function `transferOwner` does not check if `_newOwner` is an employee of the `_companyId`. The `_newOwner` could be an inactive employee, or even not an employee of that company. This would violate the assumption that a company owner should be an active employee of that company. 1. The function `transferOwner` does not check if `_newOwner` is not equal to `0x0`. This could lead to a disastrous result: A company has no owner to modify its data.
Open the reportAlthough `Escrow.sol` inherits from `ReentrancyGuardUpgradeable`, it does not make use of the `nonReentrant` modifier. Further, functions `deposit()` and `withdraw()` do not comply with the ["Checks-Effects-Interactions" pattern](https://docs.soliditylang.org/en/v0.8.11/security-considerations.html#use-the-checks-effects-interactions-pattern), by performing state variable changes **after** external function calls and not before (`escrowBalance[][]` being changed after calling `transferFrom()`, `balanceOf()` and `transfer()`). As these lower level functions are only callable by `owner` or `vestingControllerAddress` the potential impact is limited.
Open the reportThe functions `createVestPlan` and `vest` are declared `payable`, allowing ether to be sent along the function calls. Since there is no function to retrieve the ether, it will lead to locked funds.
Open the reportWhen smart contracts are deployed or their associated functions are invoked, the execution of these operations always consumes a certain quantity of gas, according to the amount of computation required to accomplish them. Modifying an unknown-size array that grows in size over time can result in a Denial-of-Service attack. Simply by having an excessively huge array, users can exceed the gas limit, therefore preventing the transaction from ever succeeding. For the loop on `L242`, the code does not restrict the number of vest plans that an employee in a company can have. This function might run out of gas and cannot be recovered from that state when the employee has too many vest plans. This could happen in case the company owner wants to deny an employee from accessing their vested tokens by adding many low-value vest plans.
Open the reportThe function `EmployeeRegistry.addEmployee()` does not check the return value of the call `EnumerableSet.add(companyIdToEmployees[_companyId].activeEmployees, _employee);`, which would indicate whether the added employee was already in the set. Therefore, calling the function multiple times would lead to duplicate company id entries in `employeeToCompanyIds` after executing `employeeToCompanyIds[_employee].push(_companyId);`.
Open the reportSome contracts implement the `IVest` or `IModule` interface without explicitly inheriting from them. This can lead to mismatched function signatures as is the case here. Some of the functions are missing the `view` specifier, none of them specify a return type and none of the modules implement a `getPlan` function.
Open the reportIt is important to validate inputs, even if they only come from trusted addresses in order to avoid human error. The following functions do not have proper validation of input parameters: - `Escrow.initialize` - `Vesting.initialize` - `{Escrow,CliffModule,LinearModule,StreamModule}.setVestingControllerAddress`
Open the reportNot all of the initializers from the inherited contracts are called. While this might not lead to issues with the current version, this can change in the future.
Open the reportThe `EmployeeRegistry` contract has several functions related to company ownership that are simply referred to as `owner`. Since the contract also inherits from `Ownable`, this might lead to confusion.
Open the reportThe `EmployeeRegistry` contract does not employ any access control for adding or archiving employees, enabling anyone to add arbitrary addresses as *active employees* to arbitrary company ids or mark them as inactive. As a result, the distinction between active and inactive employees is currently pointless.
Open the reportThere are several member variables that contain identical values, one typed and one `address`. This is redundant since the address can be accessed by casting it like this: `address(cliffModule)`. List of redundant variables: ```solidity address public employeeRegistryAddress; address public cliffModuleAddress; address public linearModuleAddress; address public realtimeModuleAddress; address public escrowAddress; ```
Open the reportStarting from solidity version 0.8, overflows and underflows revert by default (see [docs](https://docs.soliditylang.org/en/v0.8.11/080-breaking-changes.html#silent-changes-of-the-semantics)), rendering the use of SafeMath unnecessary.
Open the reportThe `getVestingControllerAddress` view function has the `onlyOwner` modifier. Since the `vestingControllerAddress` variable is public anyway, this serves no purpose.
Open the reportThe reports name a repository or files. Open a report to see its scope.
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="liquifi"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
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