As of 20 Oct 2026, Wormhole has 11 audit reports by 4 security firms on Trustblock; the latest is by Cyfrin, dated 4 Oct 2024. Those reports list no open critical findings.
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 |
|---|---|---|---|---|---|
| Multi-Gov: Cross Chain Governance Audit Report | Security firm: Cyfrin | Date: | Scope: Not recorded (see the report) | No open critical findings | Original removed |
| Wormhole Worldcoin World Id State Root Bridge | Security firm: Ackee Blockchain Security | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · GitHub |
| Wormhole Foundation EVM-NTT Diff Audit Report | Security firm: Cyfrin | Date: | Scope: Not recorded (see the report) | No open critical findings | Original removed |
| Wormhole Foundation EVM-NTT Audit Report | Security firm: Cyfrin | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · GitHub |
| Wormhole Foundation EVM-CCTP Integration | Security firm: Cyfrin | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · GitHub |
| Wormhole Thermae Audit Report | Security firm: Cyfrin | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · GitHub |
| Wormhole Token Translator audit | Security firm: Oak Security | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · GitHub |
| Wormhole - Ethereum | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| pyth2wormhole - Governance | Security firm: CertiK | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · skynet.certik.com |
| pyth2wormhole - Solana | Security firm: CertiK | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · skynet.certik.com |
| Pyth-client | 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 | 1 | 2 | 5 |
| Medium | 1 | 5 | 10 |
| Low | 28 | 13 | 18 |
The 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 function `make_none()` is used to remove a position, which is needed when completely closing a position. By design, it will swap the last valid position with the position to be removed. ```rust=51 pub fn make_none(&mut self, i: usize, next_index: &mut u8) -> Result<()> { *next_index -= 1; self.positions[i] = self.positions[*next_index as usize]; None::<Option<Position>>.try_write(&mut self.positions[i]) } ``` The function first decreases `next_index` by 1, so it now points to the last created position. Then the function overwrites the position at index `i` with the information of the last position, and then changes position `i` to a `None` value. This eliminates position `i`, but the value of `next_index` does not point towards an empty position. A consequence of this is that if a new position was created, then this new position will overwrite the last created position due to the updated value of `next_index`, causing the user to completely lose a position.
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 reportIn connection with the [rent](https://docs.solana.com/developing/programming-model/accounts#rent) for storing data in the network, the new created accounts must have a certain number of lamports on their balance, depending on the amount of data.
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 `setup` function from `Setup`, `BridgeSetup`, and `NFTBridgeSetup` is a public function and lacks of access control, which can be called many times by anyone.
Open the reportIn the function `update_woter_weight`, both actions `CastVote` and `CreateProposal` will compare the proposal `max_voting_time` with `config.epoch_duration`. It currently checks to ensure the `max_voting_time` is at least larger than one epoch. ```rust=406 if config.epoch_duration > max_voting_time.into() { return Err(error!(ErrorCode::ProposalTooLong)); } ``` However, the error code for the `max_voting_time` when it is less than one epoch will be `ErrorCode::ProposalTooLong`. If the max voting time is meant to be larger than one epoch, the error code should be "proposal too short" instead. Otherwise, if the max voting time is meant to be shorter than an epoch, the condition should be reversed.
Open the reportWhen context for `UpdateMaxVoterWeight` has the `init_if_needed` condition for the `max_voter_record` account. Since the function `update_max_voter_weight()` fixes all fields of `max_voter_record`, there is no reason to use this function twice.
Open the reportThe position index `i`, converted from user input `index`, is not sanitized before accessing the positions in `stake_account_positions`.
Open the reportIn the checks to verify that the publisher is valid, variable `i` is initialized as 0: ```cpp=508 uint32_t i = 0; ``` However at the start of the `for` loop, `i` is set to 0 again which isn't necessary: ```cpp=510 for( i=0; i != pptr->num_; ++i ) { ```
Open the reportIn the `oracle.h` file, there are multiple assertions on struct sizes, for example: ```cpp static_assert( sizeof( pc_map_table_t ) == 20536, "" ); ``` As a result `ka->data_len >= dlen` in the functions `valid_signable_account` and `valid_writable_account` should instead be a strict check on size: ```cpp ka->data_len == dlen; ```
Open the reportThe `init_test()` and `upd_test()` functions are used for testing and are in the main application code.
Open the reportThere are multiple typos in the code base, the corrections are as follows: 1. In the file: `sort_stable.c`: "interaction" should be "iteration". 2. In the file: `price_model.h`: "srcatch" should be "scratch". "reducd" should be "reduce". [0,cnt) should be [0,cnt]. 3. In the file: `avg.h`, "implict" should be "implicit". 4. In the file: `prng.h`, "faciliate" should be "facilitate". "alingment" should be "alignment". "occassionally" should be "occasionally". 5. In the file: `oracle.c`, "ssign" should be "sign".
Open the reportThe version of `hdr->ver_` is checked in the `dispatch()` function and cannot have a value other than `PC_VERSION`. In this case, it makes sense to use `PC_VERSION` in further logic.
Open the reportThe only input validation is a check that sees if the size of the input variables satisfies `prm->data_len`. This check is not rigorous enough and as long as the data size matches, any piece of data can be inputted in the `set_min_pub()` function.
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="wormhole"></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