As of 22 Oct 2026, Seedify has 9 audit reports by 2 security firms on Trustblock; the latest is by Hacken, dated 10 Oct 2025. Those reports list no open critical findings. It also holds 4 deployed contracts on 3 chains.
Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Seedify.fund / MyToken Audit | Security firm: Hacken | Date: | Scope: 1 address on BNB Chain | No open critical findings | Open · hacken.io |
| Seedify.fund / BondingCurve | Security firm: Hacken | Date: | Scope: 1 address on BNB Chain | No open critical findings | Open · hacken.io |
| Seedify Fund / IDO & Linear Vesting | Security firm: Hacken | Date: | Scope: 3 addresses on Ethereum, BNB Chain and Arbitrum | No open critical findings | Open · hacken.io |
| Seedify layer zero | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Seedify NFT Launchpad | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Vesting Update | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Seedify finance Locked-Yield-Farming | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| Seedify.fund (nft-vanguards) | Security firm: CertiK | Date: | Scope: Files named in the report | 1 critical finding, fixed | Open · skynet.certik.com |
| Old IDO & NFT Launchpads Upgrades (Agency Work) | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 1 |
| High | 7 | 0 | 5 |
| Medium | 6 | 0 | 6 |
| Low | 12 | 1 | 40 |
In the `BlackBoxVang` contract, the `withdraw()` function allows a user to withdraw Ethers from the contract to its own address. There are no access control on the `withdraw()` function. That means any user can call the function and receive Ethers. The amount can be withdrawn is the input parameter of the function. There are no limits on the amount as well. Therefore any user can call the `withdraw()` function and drain all Ethers from the contract.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the report`Vanguard` is an upgradeable contracts, the proxy owner can upgrade the contract without the community's commitment. If a malicious user compromises the account, the user can change the implementation of the contract and potentially drain tokens from the contract.
Open the reportIn `buyTokens()` it is checked if the `msg.sender` is whitelisted for the input `tier`. If they are not it reverts, but if they are it then increments `buyInOneTier[msg.sender]` by the input `amount`. The name implies that it is keeping track of the amount bought for tier one, however it keeps track of the amount bought by `msg.sender` in all tiers. This can cause issues if a user buys in different tiers. For example, if a user buys 100,000 tokens in tier 1 and also buys 100,000 tokens in tier 2. After the `refundThresholdTime` the user could call `refund(1,200000)`, which will pass all checks provided the `totalBUSDInTiers[0]` is at least 200,000. This happens because both buys increment `buyInOneTier[msg.sender]` so that it is 200,000 before `refund()` is called. It decrements `totalBUSDInTiers[0]` by 200,000 when it should only decrement by 100,000. If enough users try to refund, this can cause users to be unable to refund as when `totalBUSDIntiers[tier-1]` is decremented in `refund()` it may revert due to an underflow error as the proper amount is not subtracted from each tier.
Open the reportIn the `refund()` function, the amount deducted from `totalBUSDReceivedInAllTier` would not be equal to the amount added to `totalBUSDReceivedInAllTier` whenever the value of `tokensPerBUSD` is changed before refunding and after buying the tokens. In the case when the value of `tokensPerBUSD` is decreased, the amount subtracted from the `totalBUSDReceivedInAllTier` would be more than the amount added and could lead to an underflow error even if the `projectOwner` has enough tokens to send to the `msg.sender`.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe `totalUserInTiers[]` should be capturing the number of members in these following arrays. - `whitelistTierOne` - `whitelistTierTwo` - `whitelistTierThree` - `whitelistTierFour` - `whitelistTierFive` - `whitelistTierSix` - `whitelistTierSeven` - `whitelistTierEight` - `whitelistTierNine` But the `totalUserInTiers[]` array is not updated anytime a new user is added with `addWhitelist()` function. In the `constructor()`, value at every concerned index is initialized to `2` without adding any user to these arrays the aforementioned arrays.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportAn import of `LinearVestingWritable` is commented out within the contract `LzLinearVesting`. Since the import is unused, it can be removed.
Open the reportThe smart contracts contain one or more state changes related to arithmetic operations that do not emit events to communicate the changes outside the blockchain, which can lead to difficulties in tracking or verifying these changes. ```solidity=43 destChainId = _destChainId; ``` ```solidity=44 destChainId = _destChainId; ```
Open the reportThroughout the codebase there are NatSpec comments included for some functions but not all.
Open the reportThere is a missing check that `_lzEndpoint` is not `address(0)` before setting `IDOStorage.layout().refundCaller`. If this state is set to `address(0)`, then function `autoRefund()` will always revert when called in `_nonblockingLzReceive()`.
Open the report### LzIDO.sol - In the comment above function `setRefundPeriod()`, word "override" is misspelled as "overide." ### LzLinearVesting.sol - In the comment above the constructor for parameter `_lzEndpoint`, word "endpoint" is misspelled as "ndpoint."
Open the reportPerforming division by zero would raise an error and revert the transaction. ```solidity=137 uint256 timePassedRatio = (timePassed * 1e18) / timeLeft; ``` The expression `(timePassed * 1e18) / timeLeft` may divide by zero. Its divisor has has estimated interval [-4294967294, 4294967295].
Open the reportIn the `SMD_v5` contract, certain functions require `isPaused` variable to be false, suggesting these functions may be paused under specific conditions. However, based on the current implementation, the contract will only ever be paused before the initial period is set. The sole function that can toggle `isPaused` to true is `__reset()`, which is called within `setNewPeriod()`. Immediately afterward, `setNewPeriod()` invokes `__setStartEnd()`, which in turn sets `isPaused` back to false. As a result, functions that require `isPaused` to be false will always proceed without interruption. Also, the pause behavior is inconsistent. For example: 1. The `viewOldRewards()` function is a view function that requires `isPaused` to be false. However, the other view functions lack this requirement. 2. The `withdraw()` does not directly require `isPaused` to be false, instead, it relies on `viewOldRewards()` which does. This implementation leads to ambiguity, causing one to question what the intended design it. 3. The `emergencyWithdraw()` function does not require `isPaused` to be false, it is recommended to check if this aligns with the intended design.
Open the reportThe `endAccShare` mapping in the contract captures the details of expired periods that determine the rewards users can claim from these old periods. The `endAccShare` is only updated via the `__reset()` function, which is exclusively triggered by `setNewPeriod()`. This design means that stakers must wait for a new period to start before they can claim rewards from an expired period, introducing potential delays.
Open the reportThe `__withdraw()` function only decreases `stakedBalance` when `isPause` is false. The `stakedBalance` represents the amount of `tokenAddress` staked in the contract for the current period, and it directly impacts the reward calculations. Therefor, It should be updated whether the contract is paused or not. Failing to do so could lead to skewed reward distributions. Specifically, the following scenario highlights the issue: 1. With `isPaused` being false, a user could invoke `emergencyWithdraw()` and reduce the actual staked balance without affecting the recorded `stakedBalance` in the contract. 2. When `isPaused` is set to true and `__updateShare()` function is triggered. When calculating the `accShare` (amount of rewards per wei), the formula uses the potentially inflated `stakedBalance` value, resulting in a smaller `accShare` than should be the case. This miscalculation means users might receive fewer rewards than they should. It's worth mentioning that, in the current design, `isPaused` remains true after the setting of the first period, thus avoiding the described situation. Nevertheless, the logic appears faulty and could lead to unintended consequences if modified in the future.
Open the reportThe `userDeposits()` function allows users to retrieve deposit details of a given address `from`. The `Deposits` struct contains a field name `userAccShare`, which represents the amount of rewards per wei of deposited LP token {tokenAddress} accross all periods. Notably, the `userDeposits()` function does not provide access to view the `userAccShare`, which appears to be an oversight. ```solidity=351 /// @notice get user deposit details function userDeposits( address from ) external view returns (uint256, uint256, uint256, uint256) { if (hasStaked[from]) { return ( deposits[from].amount, deposits[from].initialStake, deposits[from].latestClaim, deposits[from].currentPeriod ); } else { return (0, 0, 0, 0); } } ``` ```solidity=84 struct Deposits { uint256 amount; uint256 initialStake; uint256 latestClaim; uint256 userAccShare; uint256 currentPeriod; } ```
Open the reportThe `Deposits` struct maintains data related to the deposits made by a user. Within this struct, the `initialStake` field is intended to record the timestamp when a user renews their stake for new periods, as described in the comment. However, there seems to be a discrepancy. The `initialStake` gets updated even when a user re-stakes within the current period, diverging from its intended behavior as per the comment. As a result, users could be prevented from withdrawing their stakes, as withdrawals are permitted only after `lockDuration.mul(SECONDS_PER_HOUR)` from the `initialStake`." ```solidity=73 /** * @notice struct which should represent the deposit made by a wallet based on all period if the wallet * called {renew}. * * @param amount amount of LP {tokenAddress} deposited accross all period. * @param initialStake should be the timestamp at which the wallet renewed their stake for new periods. * @param latestClaim latest timestamp at which the wallet claimed their rewards. * @param userAccShare should be the amount of rewards per wei of deposited LP token {tokenAddress} * accross all periods. * @param currentPeriod should be the lastest periodCounter at which the wallet participated. */ struct Deposits { uint256 amount; uint256 initialStake; uint256 latestClaim; uint256 userAccShare; uint256 currentPeriod; } ```
Open the reportThe `recoverLostERC20()` function is designed to enable the `_owner` to retrieve tokens mistakenly sent to the contract. A safeguard mechanism is embedded within the function to ensure that the `_owner` cannot withdraw tokens (`tokenAddress`) that are actively staked by users. It achieves this by subtracting `stakedTotal` from the `amount` before the withdrawal. However, there's a flaw in the logic: the `stakedTotal` represents the total amount of `tokenAddress` staked in the contract over its whole existence. Since it never decreases even when users make withdrawals, it could be larger the actual current staked balance. This flawed design means that the `_owner` might not be able to recover all the excess tokens beyond the current user stakes. ```solidity=677 function recoverLostERC20(address token, address to) external onlyOwner { if (token == address(0)) revert("Token_Zero_Address"); if (to == address(0)) revert("To_Zero_Address"); uint256 amount = IERC20(token).balanceOf(address(this)); // only retrieve lost {rewardTokenAddress} if (token == rewardTokenAddress) amount -= rewardBalance; // only retrieve lost LP tokens if (token == tokenAddress) amount -= stakedTotal; IERC20(token).safeTransfer(to, amount); } ```
Open the reportIn the `findBox()` function, there is an input parameter called `allocation`, which is used for validating the merkle proof as well as for assigning the `userMints[msg.sender].allocation` value. Our understanding is that a user cannot randomly input an `allocation` value otherwise the user cannot pass the proof validation check. However, the mapping of user addresses and allowed `allocation` is not saved in the contract. We would like to learn how is the initial allocation value determined, and how can users learn about the mapping of their addresses and the legit `allocation` values for them.
Open the reportAddresses are not validated before assignment or external calls, potentially allowing the use of zero addresses and leading to unexpected behavior or vulnerabilities. For example, transferring tokens to a zero address can result in a permanent loss of those tokens. ```solidity=73 vanguardContract = _vanguardContract; ``` - `_vanguardContract` is not zero-checked before being used. --- ```solidity=78 boxContractAddress = contractAddress; ``` - `contractAddress` is not zero-checked before being used.
Open the reportOne or more logic contracts do not protect their initializers. An attacker can call the initializer and assume ownership of the logic contract, whereby the attacker can perform privileged operations that either indirectly break the proxy by destroying the logic contract or trick unsuspecting users into believing that she is the owner of the upgradeable contract. ```solidity=11 contract Vanguard is ``` - `Vanguard` is an upgradeable contract that does not protect its initializer. ```solidity=23 function initialize() external initializer { ``` - `initialize` is an unprotected initializer function.
Open the reportBoth `buyTokens()` and `refund()` call `getWhitelist()` for each tier to determine which tier a user is in.
Open the reportThe function 'refund()` is never called internally within the contract.
Open the reportIn the function `refund()`, both the fields of `fundUser[msg.sender]` are subtracted from `fundUser[msg.sender]` to update the `fundUser[msg.sender]`, which will always amount to both fields of `fundUser[msg.sender]` equal to zero.
Open the reportThe following checks are not made: - In the `constructor()` of `INOLaunchpadSmartContract`, it is not checked that `_busdPrice` is nonzero. If it is mistakingly set to zero, then the `nftPrice` will also be zero. - In the `constructor()` of `SeedifyFundBUSD`, it is not checked that `_projectOwner` is not the zero address. - In the `constructor()` of `SeedifyFundBUSD`, it is not checked if the `_saleStartTime` is less than the '_saleEndTime'. - In `seedifyFundBUSD`, the `maxAllocPerUserInTiers[i]` should be always greater than or equal to `minAllocaPerUserInTiers[i]`. In the functions `updateTierValues()`, `updateTeirUsersValue()`, and the `constructor()`, it is not checked that the `maxAllocPerUserInTiers[i]` is greater than or equal to `minAllocaPerUserInTiers[i]` for all `tiers` `i`. - In the `constructor()` of `seedifyFundBUSD`, it is not checked that if all the values in the `_tiersValue[]` array are not zero. - In the function `addWhitelist()` of `seedifyFundBUSD`, it is not checked that the input `_address` is not already whitelisted for the input `_tier`. This can cause issues if an address is added multiple times because it will cause the `maxAllocaPerUserInTiers` to be calculated incorrectly.
Open the reportThe `updateTierValues()` does not check if the input `_tiersValue[]` has the value '0' for any valid index, or not. Because of which `totalUserInTiers[i]` could possibly be '0' in the following expression: ```solidity=137 for (uint256 i = 0; i < 9; i++) { tiersMaxCap[i] = _tiersValue[i]; maxAllocaPerUserInTiers[i] = tiersMaxCap[i] / totalUserInTiers[i]; } ```
Open the reportThe variable `totalparticipants` in `SeedifyFundBUSD.sol` and 'totalSupply' in `INOLaunchPadSmartContract.sol` are declared and initialized in the constructor, but never used. In the contract SeedifyFundBUSD.sol, the following variables are declared but never used: - `buyInTwoTier` - `buyInThreeTier` - `buyInFourTier` - `buyInFiveTier` - `buyInSixTier` - `buyInSevenTier` - `buyInEightTier` - `buyInNineTier`
Open the reportThe return value of the transfer()/transferFrom() call is not checked in any case of the `refund()` function.
Open the reportThe following variables assigned in the constructor can be declared as `immutable`. - `projectOwner` Immutable state variables can be assigned during contract creation but will remain constant throughout the lifetime of a deployed contract. A big advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since they will not be stored in storage.
Open the report| Address | Chain | Source code |
|---|---|---|
| 0xffda…52320xffda10b7fd9cf172e0502a6bc0e5e355516c5232 | Chain: BNB Chain | Source code: Verified source. Powered by Etherscan.io APIs |
| 0x477b…5d120x477bc8d23c634c154061869478bce96be6045d12 | Chain: BNB Chain | Source code: Verified source |
| 0x5603…fa600x560363bda52bc6a44ca6c8c9b4a5fadbda32fa60 | Chain: Arbitrum | Source code: Verified source |
| 0x5603…fa600x560363bda52bc6a44ca6c8c9b4a5fadbda32fa60 | Chain: Ethereum | Source code: Verified source |
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="seedifyfund"></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