As of 3 Nov 2026, Kontos has 1 audit report on Trustblock, by CertiK, dated 12 Dec 2023. That report lists no open critical findings.
Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.
Newest first
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 2 | 0 |
| Medium | 7 | 0 | 2 |
| Low | 10 | 0 | 7 |
The creation of new SmartAccount (in the form of a `BeaconProxy`) uses the `create` opcode instead of the `create2` opcode. This deviates from a key design goal of account abstraction. According to EIP4337 (https://eips.ethereum.org/EIPS/eip-4337#first-time-account-creation): "The factory is expected to use CREATE2 (not CREATE) to create the wallet, so that the order of creation of wallets doesn’t interfere with the generated addresses." Also, the `createSmartAccount()` function should return the wallet account address even if it has already been created, so the use of the `require` statement in line 51 is incompatible with EIP4337. Separately, according to EIP4337, signature verification should take place at the account level instead of in the `EntryPoint` contract. Based on https://eips.ethereum.org/EIPS/eip-4337#specification: The account MUST validate the caller is a trusted EntryPoint. If the account does not support signature aggregation, it MUST validate the signature is a valid signature of the userOpHash, and SHOULD return SIG_VALIDATION_FAILED (and not revert) on signature mismatch. Any other error should revert. An account that works with aggregated signature, should return its signature aggregator address in the “sigAuthorizer” return value of validateUserOp.
Open the reportIn the `SmartAccountFactory` contract, the `initialize()`, `initBeacon()`, and `initEntryPoint()` functions can be called by anyone. A malicious user could potentially front run the call to these functions and set malicious `beacon`, `entryPoint`, and `kontosLightNode` addresses. In the `EntryPoint` contract, the `initGenesisBrokerAccount()` function can be called by anyone. A malicious user could potentially front run the call to this function and set malicious `broker` addresses.
Open the reportThe `isValidSignature()` function does not perform any signature verification and returns the truthy value for any input value. This deviates from a key goal of account abstraction, which is to allow users to use smart contract wallets containing arbitrary verification logic (reference: https://eips.ethereum.org/EIPS/eip-4337#motivation).
Open the reportThe `updatePubKey()` function updates the SmartAccount's public keys. There's no check that the new `_pk` is valid and corresponds to a point on the chosen elliptical curve. The lack of validation could result in invalid public keys being set for the SmartAccount, which means no signature validation can be successful afterwards.
Open the reportThe account abstraction workflow includes the prepayment of gas and the refund of unused gas fee. The prepayment of gas fee should be from the user/user's smart account (or user directed PayMaster in the original EIP4337 design) to the bundler which calls the `handleOps()` function and incurs gas cost. However, in line 177 of the `EntryPoint` contract, the prepayment workflow transfers the asset from the `payer`, which is the `broker` in line 128, to the `accountProxy` which is the user's smart account address. This is not consistent with the EIP4337 design. If the intention here is that the `broker` will subsidize users to execute their transactions, then there should be economic incentive for the broker to do so, and such logic is not included in the in-scope smart contracts. Furthermore, while the `actualGasCost` is measured in line 189, there is no actual gas refund mechanism. In EIP4337, the intended design is that the difference between the prepayment amount and the `actualGasCost` will be refunded to the user or user designated paymaster in the form of increased deposit.
Open the reportThe `SmartAccount` contract contains several variables with unclear usage. The existence of several variables and the lack of usage indicate that certain important functions are missing in the contract, such as replay protection. The `_kontosAddress` and `_kontosLightNode` variables are initialized or contain a default value, but is not used within the contract. The `_eoaOwner` is never initialized, which makes the `require` statement in line 98 always fail. THe `_slot` and `_usedProofs` variables are never used. The `_nonce` variable is updated in line 115 but there is no check that the `_nonce` has not bee used previously. The lack of usage of `_usedProofs` and `_nonce` indicate missing replay protection. In the `EntryPoint` contract, the `strandedAssets` variable in line 22 is declared but never used.
Open the reportThe variable `_eoaOwner` is never set and defaults to the zero address. This makes the `executeBatchByEOAOwner()` function uncallable due to the `require` statement in line 98, and breaks a key functionality of the `SmartAccount` contract.
Open the reportIn the `SmartAccountBeacon` contract, the value of `target` always equals `implementation()`, and is therefore duplicative.
Open the reportThe `startUpgrade()` function takes in an array of `_indexes`, and the `cancelUpgrade()` function takes in a single `_index`. However, even if a single `_index` is cancelled, the global `upgradeStatus` is set to `Idle` in line 81, which would cause the `startPreparation()` function to revert due to the check in line 90. In other words, canceling one `_index` essentially prevents all `_indexes` from being upgraded.
Open the reportThe contract has payable functions, making it able to receive native tokens. However, it does not have a function to withdraw the funds, which can lead to permanently locked tokens within the contract.
Open the reportThe `initUpgradeGateKeeper()` function in the `DeployFactory` contract indicates that the `upgradeGateKeeper` contract should be a `BaseProxy` contract: ```solidity=67 upgradeGateKeeperProxy = BaseProxy(payable(deployedAddress)); ``` However, the `upgradeGateKeeper` contract does not inherit `BaseProxy`: ```solidity=9 contract UpgradeGateKeeper is UpgradeEvents, IProxy, UpgradeableOwnable ```
Open the reportThe `addUpgradable()` function in the `DeployFactory` contract adds a contract to be upgraded by the `UpgradeGateKeeper` contract. There is no check that the `_index` already has a corresponding proxy to be upgraded, and there's no check that the `UpgradeGateKeeper` contract is the `master` of the `_proxy`. The implication is that a contract that is already in the upgrade process could be overwritten, or the `finishUpgrade()` function would revert due to the lack of `master` role in line 109 of the `UpgradeGateKeeper` contract.
Open the reportIn the `SmartAccountFactory` contract, the `createSmartAccount()` function is used to create new smart account. Due to the check in line 51, each `_nameAddr` can only have one smart account. ```solidity=50 _requireFromEntryPoint(); require(address(accounts[_nameAddr]) == address(0), "account exists"); ``` In the `EntryPoint` contract, the call to the `createSmartAccount()` function is in line 229. Note that the first function argument comes from an arbitrary user input, and is not dependent on the `_caller` address in line 217, which is the `msg.sender` in line 212. Given that `kontosLightNode` is out of scope, we'd like to understand if the kontos proof verification in line 204 ensures that the given `proofData.nameAddress` has signed the transaction being submitted. If not, a malicious user could create smart account for other user addresses and set the public key of the smart account, and deny the other users from creating their own smart accounts. ```solidity=229 aaAccount = accountFactory.createSmartAccount{gas: _proofTask.callGasLimit}(proofData.nameAddress, abi.encode(proofData.pk)); ``` Furthermore, if `hasAccount` is `true`, a malicious user could also update the `pk` of other users' existing smart account, due to the lack of constraint in the code block below ```solidity=224 if (hasAccount) { ISmartAccount account = accountFactory.getSmartAccount(proofData.nameAddress); aaAccount = address(account); account.updatePubKey{gas: _proofTask.callGasLimit}(proofData.pk); ```
Open the reportIn the `finishUpgrade()` function, line 109 calls the `upgradeTarget()` function of the specific managed contract. This assumes that the managed contract contains the `upgradeTarget()` function. Note that in multiple upgradeable contracts in the code base, an `upgrade()` function is included instead. ```solidity=109 managedContracts[index].upgradeTarget(nextTargets[index], targetsUpgradeParameters[i]); ```
Open the reportThe `FTConnector` contract is serving as the underlying entity to interact with one or more third party protocols or out of scope contracts, such as `lightNode`, `ConnectorHub`, `TokenFactory`, etc.. The scope of the audit treats them as black boxes and assumes their functional correctness. However, in the real world, third parties and out of scope contracts can be compromised and this may lead to lost or stolen assets. The `EntryPoint` contract delegates signature verification to the `sigVerifier` contract in line 124, and `kontosLightNode` in line 204. The `sigVerifier` and `kontosLightNode` contracts are out of scope, and are treated as blackboxes and assumed to be functionally correct. Importantly, because the `SmartAccount` contract does not perform signature verification, it is assumed that the `sigVerifier` correctly verifies all signatures included in the `sigInfos` individually. If such an assumption is broken, then the `EntryPoint` contract could be used to trigger unauthorized transactions on any user's Smart Account, which would be the most severe security breach of the project.
Open the reportThe smart contract contains one or more values that are not used, which can lead to unnecessary complexity and reduced maintainability. --- ```solidity=70 (address sender, uint256 sourceChainId, uint256 targetChainId, address connector, bytes memory connectedInfo) = abi.decode(proofTask.data, (address, uint256, uint256, address, bytes)); ``` - The variable `sourceChainId` is never used after this assignment. Instead, `_ftConnectorEvent.sourceChainId` is used in line 75.
Open the reportThe linked functions are equivalent to the compiler-generated getter functions for the respective variables.
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="kontos"></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