CertiK reviewed Iskra (Iskra - Launchpad) on 12 Jun 2023: 23 findings, 0 critical; 16 marked fixed, 7 open.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 5 | 0 | 0 |
| Medium | 0 | 0 | 2 |
| Low | 2 | 0 | 14 |
The contract `LaunchpadInitializer` does not protect its initializer. An attacker can call the initializer and assume ownership of the logic contract, whereby she 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. Since the `LaunchpadInitializer` is meant to update important state variables before using the `Launchpad` implementation, a lack of security in this contract could have unexpected effects on the protocol.
In `StructuredSignVerifier`, the state variable `_TYPE_HASH` is shadowing another component defined in `EIP712`. \ This variable is used to perform sensitive tasks related to the cryptographic protocol, therefore it is not recommended to shadow any variable or interfere with the underlying mechanism.
In the contract `Launchpad`, the function `_deployIGO()` has the following *for* loop: ```solidity=119 for (uint256 i = 0; i < saleAmounts.length; ++i) { totalAmounts += saleAmounts[i]; require(saleAmounts[i] % amountPerTicket == 0, "invalid amountPerTicket."); } ``` The **require** statement could be placed before the update of `totalAmounts`, so in case of an invalid `amountPerTicket` the execution reverts before updating a variable.
A reentrancy attack can occur when the contract includes a function that makes an external call to another untrusted contract before resolving any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the external call resolved the effects.
Boolean constants can be used directly and do not need to be compared to true or false. ```solidity=249 require(_isPaymentToken[token] == false, "already registered payment token."); ``` --- ```solidity=257 require(_isPaymentToken[token] == true, "not registered payment token."); ```
There is no storage gap preserved in the logic contract. Any logic contract that acts as a base contract that needs to be inherited by other upgradeable child should have a reasonable size of storage gap preserved for any new state variables introduced by future upgrades. This finding is for informational purposes only because from context it is understood that likely all new state variables would be added to `LaunchpadBase`, which holds all state variables related to `Launchpad`.
In `InitialGameOffering.sol`, the function `initialize()` sets the values of `_saleInfo[SaleKind.PIONEER].saleAmount` and `_saleInfo[SaleKind.EXPLORER].saleAmount`. There is a missing requirement that these values are not set as 0.
In `StructuredSignVerifier`, the function `changeSignLifespan()` allows modifying the validity period of a signature. However, there is no upper bound on the value it is set to.
In `InitialGameOffering.sol`, many functions are depending on the `status()` of the contract to execute correctly. The `status()` depends heavily on the time marks and periods defined using the function `_changeSaleTimeline()`. This function allows the time intervals to have arbitrarily large values. This can cause the state of the contract to be stuck for a crucial amount of time. Since `refund()` and `claim()` functions are also dependent on the status of the contract, this could lead to a loss of assets for users.
Addresses should be checked before assignment or external call to make sure they are not zero addresses. ```solidity=66 token = token_; ``` - `token_` is not zero-checked before being used. --- ```solidity=112 operator = operator_; ``` - `operator_` is not zero-checked before being used.
In `InitialGameOffering.sol`, the function `initialize()` does not perform an upper bound check when setting `sellerFeePerMille` and `buyerFeePerMille` although those values cannot be set higher than 1000 in any other functions.
The function `_verifySignature()` does not perform enough checks to prevent someone from reusing the same parameter more than once. \ This could potentially be used to perform a replay attack. In the scope of this audit, the signature mechanism seems to be used only to ensure a user participating in an `InitialGameOffering` is authorized to do so. In this case, the replay allowed by the signature verification seems to be fine since it will only allow a specific user to participate.
A local variable is shadowing another component defined elsewhere. ```solidity=72 string memory symbol = abi.decode(queriedSymbol, (string)); ``` - Local variable `symbol` in `IGOVesting.initialize` shadows: - Function `symbol` in `ERC20Upgradeable` - Function `symbol` in `IERC20MetadataUpgradeable` --- ```solidity=16 constructor(address signer) StructuredSignVerifier("LaunchpadQualificationChecker", signer, 5 minutes) {} ``` - Local variable `signer` in `LaunchpadQualificationChecker.constructor` shadows the variable `signer` in `StructuredSignVerifier`.
In the linked locations, a function is creating a local variable `prev` whose only purpose is to be used in an event.
One or more logic contracts do not protect their initializers. An attacker can call the initializer and assume ownership of the logic contract, whereby she can perform privileged operations that trick unsuspecting users into believing that she is the owner of the upgradeable contract. ```solidity=15 contract IGOVesting is OwnableUpgradeable, IIGOVesting, ERC20Upgradeable, ERC165 { ``` - `IGOVesting` is an upgradeable contract that does not protect its initializer. ```solidity=53 function initialize( ``` - `initialize` is an unprotected initializer function. --- ```solidity=16 contract InitialGameOffering is OwnableUpgradeable, IInitialGameOffering { ``` - `InitialGameOffering` is an upgradeable contract that does not protect its initializer. ```solidity=90 function initialize( ``` - `initialize` is an unprotected initializer function.
The return value of an external call is not stored in a local or state variable. ```solidity=433 IERC20Upgradeable(_vesting.token()).approve(address(_vesting), amount); ```
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (8)
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething 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