As of 3 Nov 2026, Ternoa has 1 audit report on Trustblock, by CertiK, dated 19 May 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
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Ternoa - New Deal | Security firm: CertiK | Date: | Scope: Not recorded (see 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 | 0 |
| High | 0 | 0 | 0 |
| Medium | 0 | 0 | 1 |
| Low | 0 | 0 | 10 |
The `transmit_nft()` function returns a `DispatchResult`, which is used to indicate success or failure. In the hook `on_initialize()`, the return value of `transmit_nft()` function is ignored. In other words, even if there is an error in `transmit_nft()`, the iteration on the `on_initialize` function will continue and the state of the system will be inconsistent. The error in `transmit_nft` could come from two sides: - If the `nft_id` is not available in the Transmission storage of the `transmission` pallet. - If the `nft_id` is not available in the NFT storage of the `nft` pallet. The latter is difficult to determine because it depends on all the mechanisms manipulating the NFT state, where the majority is outside the scope of this audit.
Open the reportThere is an issue in the `on_initialize` function, where the number of initial storage reads is inaccurately declared. The `read` variable is initialized with a value of 1, but in reality, two storage reads are being executed. This discrepancy can lead to potential problems, as the weight calculation may not be accurate, possibly affecting the performance and stability of the system.
Open the reportThe `Queue` struct used for `AtBlockQueue` transmission pallet storage implements the `pop_next` method to get the next element on the queue (only if the block number is lower or equal to now). After getting the element, this is deleted from the queue and returned to the method caller. To remove the element the function uses the `remove` method that using the `token_id` can search the queue and find the index where the element is, so it can be deleted. However, the index is already known in the `pop_next` function (index 0), so all the logic in the `remove` function is unnecessary. Considering this function is called on each iteration of a main loop in the pallet hook (called on each block), the current implementation add substantial inefficiency to the system.
Open the reportThe extinct `set_transmission_protocol()` receives an NFT ID, a recipient, a transmission protocol, and a cancellation period. This transmission protocol can be canceled according to the cancellation period. There are three options for this: - Never accept cancellations - Always accept cancellations - Accept cancelations only until a specific block (this option receives a block number) However, there is no validation of the block number passed in the `CancellationPeriod::UntilBlock(BlockNumber)`. A user or the UI could commit a mistake and send a block number in the past which will disable the possibility to cancel it.
Open the reportIn the function `register_enclave()`, there is no check on `apiUri` validity. The user can register an enclave with e.g., an empty apiUri.
Open the reportThe indicated expression creates a reference which is immediately dereferenced by the compiler.
Open the reportThe indicated locations show the usage of the `clone()` method when the cloned value is not consumed or mutated.
Open the reportIn the indicated locations there is a redundant usage of pattern matching.
Open the reportThe functions `remove_registration` and `remove_update` are responsible for removing an enclave registration and an enclave update request from the storage, respectively. Although, it is observed that these functions do not return an error or fail even if the operator does not exist. Specifically, the functions utilizes `try_mutate` to modify the storage value associated with the `operator_address`. If an update or registration exists for the given operator, it sets the value to None. In cases where the operator does not exist or has no associated value, the function still proceeds without error, simply not modifying the storage. Therefore, the call will not return an error and it will be silently omitted if the extrinsic is used to delete, by mistake, an operator that doesn't exists.
Open the reportThe `remove_transmission_protocol` extrinsic is used to remove the transmission protocol of an NFT. It manages the four different types of transmission protocols. However, on some of the protocols, it can have an inefficient performance because it tries to remove data from certain storage that do not contain the NFT. The first case happens with the transmission protocol `OnConsent`. It requires the approval of N accounts but doesn't require activation on a specific block, then it's not stored in the `AtBlockQueue` but the `remove_transmission_protocol` extrinsic will try to remove it anyway, leading to extra unnecessary storage read that could be avoided. The second case happens with the transmission protocols `AtBlock` and `AtBlockWithReset`. They don't require approvals, then they are not stored in the storage `OnConsentData` but the `remove_transmission_protocol` extrinsic will try to remove them anyways, leading to extra unnecessary storage read that could be avoided.
Open the reportThe TEE pallet provides the logic to manage the enclaves and their API URIs. When the operator of an enclave wants to remove their own enclave from the pallet, they need to call the `unregister_enclave` extrinsic that will create a request to remove the enclave from the pallet, which can later be activated by the call of the `remove_enclave` extrinsic. However, the latter extrinsic can only be called by root and not by the enclave operator and the extrinsic doesn't require the enclave to be in the list of enclaves to remove (`EnclaveUnregistrations`). In other words, an enclave can only be removed by root and this does not require the enclave to be added to the list of enclaves to unregister (`EnclaveUnregistrations`). As a consequence, half of the logic present in the `unregister_enclave` extrinsic doesn't need to be on-chain.
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="ternoa"></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