Wormhole security record

Arbitrum, Base, Ethereum, Fantom, Solanawormhole.comGitHub

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.

Critical findings
No open critical findings
Across 11 reports.
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.

Audits11

Newest first

AuditSecurity firmDateScopeCritical findingsReport
Multi-Gov: Cross Chain Governance Audit ReportSecurity firm: CyfrinDate: Scope: Not recorded (see the report)No open critical findingsOriginal removed
Wormhole Worldcoin World Id State Root BridgeSecurity firm: Ackee Blockchain SecurityDate: Scope: Not recorded (see the report)No open critical findingsOpen · GitHub
Wormhole Foundation EVM-NTT Diff Audit ReportSecurity firm: CyfrinDate: Scope: Not recorded (see the report)No open critical findingsOriginal removed
Wormhole Foundation EVM-NTT Audit ReportSecurity firm: CyfrinDate: Scope: Not recorded (see the report)No open critical findingsOpen · GitHub
Wormhole Foundation EVM-CCTP IntegrationSecurity firm: CyfrinDate: Scope: Not recorded (see the report)No open critical findingsOpen · GitHub
Wormhole Thermae Audit ReportSecurity firm: CyfrinDate: Scope: Not recorded (see the report)No open critical findingsOpen · GitHub
Wormhole Token Translator auditSecurity firm: Oak SecurityDate: Scope: Not recorded (see the report)No open critical findingsOpen · GitHub
Wormhole - EthereumSecurity firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
pyth2wormhole - GovernanceSecurity firm: CertiKDate: Scope: Not recorded (see the report)No open critical findingsOpen · skynet.certik.com
pyth2wormhole - SolanaSecurity firm: CertiKDate: Scope: Not recorded (see the report)No open critical findingsOpen · skynet.certik.com
Pyth-clientSecurity firm: CertiKDate: Scope: Not recorded (see the report)No open critical findingsOpen · skynet.certik.com

Findings83

Statuses are the security firm’s own, from each report.

SeverityOpenAcknowledgedFixed
Critical000
High125
Medium1510
Low281318
HighCentralization Related Riskspyth2wormhole - Governance · CertiK · 13 Dec 2022Open

In the program `staking`, the role `governance_authority` has authority over the following functions: - `update_governance_authority()` will change the `governance_authority` role of the `config_account`. - `update_freeze()` will change the `freeze` status of the `config_account`. - `create_target()` will create a voting target `target_account`. Any compromise to the `governance_authority` account may allow the hacker to take advantage of this and result in unexpected loss. Additionally, the Solana program could be upgradeable, and the upgrade authority is the deployer by default. Therefore, if the program is upgradable, and the upgrade authority account is compromised, it could lead to a malicious program upgrade, thus introducing centralization risk.

Open the report
MediumContract gains non-withdrawable tokens via the fuWormhole - Ethereum · CertiK · 8 Mar 2023Open

The `_transferTokens()` function normalizes the transferred token amount by setting the last 8 digit as zero via `deNormalizeAmount()` and `normalizeAmount()`. ```solidity=136 // don't deposit dust that can not be bridged due to the decimal shift amount = deNormalizeAmount(normalizeAmount(amount, decimals), decimals); ``` When transferring a standard ERC20 token, the normalization in L137 will ensure the transferred amount (calculated in L152) aligns with the `transferResult` specified in L171. ```solidity=152 amount = balanceAfter - balanceBefore; ``` ```solidity=168 transferResult = BridgeStructs.TransferResult({ tokenChain : tokenChain, tokenAddress : tokenAddress, normalizedAmount : normalizedAmount, normalizedArbiterFee : normalizedArbiterFee, wormholeFee : msg.value }); ``` However, when transferring tokens that charge fees during transfer (i.e., deflationary tokens), the normalizing process in L137 cannot ensure the amount calculated in L152 is normalized because of the fee charged. Therefore, normalizing the result in L160 could lead to a potential precision loss. ```solidity=160 uint256 normalizedAmount = normalizeAmount(amount, decimals); ``` In this case, a small number of deflationary tokens will be left in the contract. Since the contract does not provide a method to withdraw these locked tokens, these tokens will be locked in the contract forever.

Open the report
LowDeclaration Naming ConventionWormhole - Ethereum · CertiK · 8 Mar 2023Open

One or more declarations do not conform to the [Solidity style guide](https://solidity.readthedocs.io/en/v0.7.3/style-guide.html#naming-conventions) with regards to its naming convention. Particularly: - `UPPER_CASE`: Should be applied to `constant` variables --- ```solidity=18 bytes32 constant module = 0x00000000000000000000000000000000000000000000000000000000436f7265; ``` - Constant variable `module` is not in `UPPER_CASE`. --- ```solidity=24 bytes32 constant module = 0x00000000000000000000000000000000000000000000004e4654427269646765; ``` - Constant variable `module` is not in `UPPER_CASE`. --- ```solidity=25 bytes32 constant module = 0x000000000000000000000000000000000000000000546f6b656e427269646765; ``` - Constant variable `module` is not in `UPPER_CASE`.

Open the report
LowDifferent Solidity VersionsWormhole - Ethereum · CertiK · 8 Mar 2023Open

Multiple Solidity versions are used in the codebase. Versions used: `^0.8.0`, `^0.8.2`, `>=0.8.0<0.9.0`, `>=0.4.22<0.9.0`, `^0.8.1` Other directives used: `ABIEncoderV2` `^0.8.0` is used in ethereum/contracts/bridge/BridgeGovernance.sol file. ```solidity=4 pragma solidity ^0.8.0; ``` `^0.8.2` is used in ethereum/node_modules/@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol file. ```solidity=4 pragma solidity ^0.8.2; ``` `>=0.8.0<0.9.0` is used in ethereum/contracts/libraries/external/BytesLib.sol file. ```solidity=9 pragma solidity >=0.8.0 <0.9.0; ``` `>=0.4.22<0.9.0` is used in ethereum/contracts/Migrations.sol file. ```solidity=2 pragma solidity >=0.4.22 <0.9.0; ``` `^0.8.1` is used in ethereum/node_modules/@openzeppelin/contracts/utils/Address.sol file. ```solidity=4 pragma solidity ^0.8.1; ``` `ABIEncoderV2` is used in ethereum/contracts/Messages.sol file. ```solidity=5 pragma experimental ABIEncoderV2; ```

Open the report
LowFunction Should Be Declared ExternalWormhole - Ethereum · CertiK · 8 Mar 2023Open

The functions which are never called internally within the contract should have external visibility for gas optimization. ```solidity=100 function completeTransfer(bytes memory encodedVm) public { ``` --- ```solidity=102 function setApprovalForAll(address operator, bool approved) public override { ``` --- ```solidity=109 function transferTokens(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) public payable nonReentrant returns (uint64 sequence) { ``` --- ```solidity=113 function transferFrom( ``` --- ```solidity=114 function transferTokensWithPayload(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce, bytes memory payload) public payable nonReentrant returns (uint64 sequence) { ``` --- ```solidity=119 function mint(address account_, uint256 amount_) public onlyOwner { ``` --- ```solidity=12 function setup( ``` --- ```solidity=12 function setup( ``` --- ```solidity=12 function setup( ``` --- ```solidity=124 function safeTransferFrom( ``` --- ```solidity=131 function burn(address account_, uint256 amount_) public onlyOwner { ``` --- ```solidity=14 function implementation() public view returns (address) { ``` --- ```solidity=14 function implementation() public view returns (address) { ``` --- ```solidity=15 function publishMessage( ``` --- ```solidity=154 function updateDetails(string memory name_, string memory symbol_, uint64 sequence_) public onlyOwner { ``` --- ```solidity=16 function initialize( ``` --- ```solidity=16 function parseAndVerifyVM(bytes calldata encodedVM) public view returns (Structs.VM memory vm, bool valid, string memory reason) { ``` --- ```solidity=16 function setCompleted(uint completed) public restricted { ``` --- ```solidity=162 function mint(address to, uint256 tokenId, string memory uri) public onlyOwner { ``` --- ```solidity=17 function getGuardianSetExpiry() public view returns (uint32) { ``` --- ```solidity=177 function burn(uint256 tokenId) public onlyOwner { ``` --- ```solidity=18 function initialize() initializer public virtual { ``` --- ```solidity=18 function initialize() initializer public virtual { ``` --- ```solidity=20 function submitContractUpgrade(bytes memory _vm) public { ``` --- ```solidity=22 function initialize( ``` --- ```solidity=23 function transferNFT(address token, uint256 tokenID, uint16 recipientChain, bytes32 recipient, uint32 nonce) public payable returns (uint64 sequence) { ``` --- ```solidity=24 function attestToken(address tokenAddress, uint32 nonce) public payable returns (uint64 sequence){ ``` --- ```solidity=27 function registerChain(bytes memory encodedVM) public { ``` --- ```solidity=28 function registerChain(bytes memory encodedVM) public { ``` --- ```solidity=300 function completeTransferWithPayload(bytes memory encodedVm, address feeRecipient) public returns (bytes memory) { ``` --- ```solidity=304 function completeTransferAndUnwrapETHWithPayload(bytes memory encodedVm, address feeRecipient) public returns (bytes memory) { ``` --- ```solidity=308 function completeTransfer(bytes memory encodedVm) public { ``` --- ```solidity=312 function completeTransferAndUnwrapETH(bytes memory encodedVm) public { ``` --- ```solidity=36 function submitSetMessageFee(bytes memory _vm) public { ``` --- ```solidity=38 function name() public view returns (string memory) { ``` --- ```solidity=41 function upgrade(bytes memory encodedVM) public { ``` --- ```solidity=42 function symbol() public view returns (string memory) { ``` --- ```solidity=43 function upgrade(bytes memory encodedVM) public { ``` --- ```solidity=47 function balanceOf(address owner_) public view override returns (uint256) { ``` --- ```solidity=50 function decimals() public view returns (uint8) { ``` --- ```solidity=52 function submitNewGuardianSet(bytes memory _vm) public { ``` --- ```solidity=54 function totalSupply() public view returns (uint256) { ``` --- ```solidity=58 function chainId() public view returns (uint16) { ``` --- ```solidity=58 function name() public view override returns (string memory) { ``` --- ```solidity=62 function nativeContract() public view returns (bytes32) { ``` --- ```solidity=62 function symbol() public view override returns (string memory) { ``` --- ```solidity=64 function wrapAndTransferETH(uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) public payable returns (uint64 sequence) { ``` --- ```solidity=66 function balanceOf(address account_) public view returns (uint256) { ``` --- ```solidity=66 function tokenURI(uint256 tokenId) public view override returns (string memory) { ``` --- ```solidity=69 function wrapAndTransferETHWithPayload(u

Open the report
LowGuardian Expiration TimeWormhole - Ethereum · CertiK · 8 Mar 2023Open

When expiring a guardian set, the guardian set is set to expire after a day. ```solidity=13 function expireGuardianSet(uint32 index) internal { _state.guardianSets[index].expirationTime = uint32(block.timestamp) + 86400; } ``` However, the `WormholeState` struct has a `guardianSetExpiry` field, which decides how long it takes for guardian sets to expire. Since the value in this field may be different from a day, guardian sets may expire earlier or later than expected.

Open the report
LowIncompatibility With Deflationary TokensWormhole - Ethereum · CertiK · 8 Mar 2023Open

When transferring standard ERC20 deflationary tokens, the input amount may not be equal to the received amount due to the charged transaction fee. For example, if `toAsset` is a deflationary token and a user adds 100 deflationary tokens (with a 10% transaction fee) to the contract, 100 LP tokens are minted but only 90 tokens actually arrived in the contract. However, when the user wants to burn 100 LP tokens and withdraw 100 tokens, the program blocks due to a lack of `toAsset` tokens.

Open the report
LowInefficient require statement LocationWormhole - Ethereum · CertiK · 8 Mar 2023Open

The `parseAndVerifyVM()` method from the core bridge of Wormhole decodes `encodedVm` and returns `vm` as the output. Then `_completeTransfer` will decode `vm.payload` and check whether `vm` is completed or not by its hash attribute. ```solidity (IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVm); require(valid, reason); require(verifyBridgeVM(vm), "invalid emitter"); BridgeStructs.Transfer memory transfer = parseTransfer(vm.payload); // payload 3 must be redeemed by the designated proxy contract address transferRecipient = address(uint160(uint256(transfer.to))); if (transfer.payloadID == 3) { require(msg.sender == transferRecipient, "invalid sender"); } require(!isTransferCompleted(vm.hash), "transfer already completed"); setTransferCompleted(vm.hash); ``` If the transfer is already completed, the invocation will revert in the require statement `require(!isTransferCompleted(vm.hash), "transfer already completed");`. However, considering the gas consumption, it is recommended to perform an early revert to save gas.

Open the report
LowLack of Input Validation for `payload`Wormhole - Ethereum · CertiK · 8 Mar 2023Open

In the Wormhole guardian node, `Unmarshal(data []byte)` from `structs.go` will get the first 1000 bytes from the payload. ```go func Unmarshal(data []byte) (*VAA, error) { //... payload := make([]byte, 1000) n, err := reader.Read(payload) if err != nil || n == 0 { return nil, fmt.Errorf("failed to read payload [%d]: %w", n, err) } v.Payload = payload[:n] return v, nil } ``` In addition, there is also a size limit of 1232 bytes for transactions containing the `payload` on Solana, see [Solana Facts](https://solanacookbook.com/core-concepts/transactions.html#facts). However, there is no size limit for the input `payload` in `transferTokensWithPayload` or `wrapAndTransferETHWithPayload` and thus part of the `payload` info may be lost when doing a cross-chain transaction.

Open the report
LowLogical issue of the check of `messageFee` in funWormhole - Ethereum · CertiK · 8 Mar 2023Open

According to the following code, the check on `messageFee` in the function `publishMessage()` uses `==` to check whether the `msg.value` is equal to the `messageFee`. ```solidity function publishMessage( uint32 nonce, bytes memory payload, uint8 consistencyLevel ) public payable returns (uint64 sequence) { // check fee require(msg.value == messageFee(), "invalid fee"); sequence = useSequence(msg.sender); // emit log emit LogMessagePublished(msg.sender, sequence, nonce, payload, consistencyLevel); } ``` The function `publishMessage()` is called in the functions `Bridge.attestToken()`, `Bridge.logTransfer()`, `Bridge.logTransferWithPayload()` and `NFTBridge.logTransfer()`. In these calls, the `msg.value` is not checked or limited. As a result, if `msg.value` is not equal to `messageFee`, the call will be rolled back and all processes will be invalidated. We would like to confirm with the client if the current implementation aligns with the original project design.

Open the report
Show 10 more
LowMismatch Between Comment and CodeWormhole - Ethereum · CertiK · 8 Mar 2023Open

The comment on `splCache` describes that `splCache` is a nested `mapping`, while the code implies otherwise.

Open the report
LowMissing Check For `v` And `s`Wormhole - Ethereum · CertiK · 8 Mar 2023Open

EIP-2 still allows signature malleability for `ecrecover()`. Remove this possibility and make the signature unique. Appendix F in the [Ethereum Yellow paper](https://ethereum.github.io/yellowpaper/paper.pdf), defines the valid range for `s` in (281): `0 < s < secp256k1n ÷ 2 + 1`, and for `v` in (282): `v ∈ {27, 28}`. Most signatures from current libraries generate a unique signature with an s-value in the lower half order. If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value with `0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1` and flip v from 27 to 28 or vice versa. If your library also generates signatures with 0/1 for v instead of 27/28, add 27 to v to accept these malleable signatures as well.

Open the report
LowNot Checking Duplicated AddressesWormhole - Ethereum · CertiK · 8 Mar 2023Open

The `parseGuardianSetUpgrade()` function from `GovernanceStructs.sol` decodes `encodedUpgrade` to acquire a new guardian set. ```solidity=96 for(uint i = 0; i < guardianLength; i++) { gsu.newGuardianSet.keys[i] = encodedUpgrade.toAddress(index); index += 20; } ``` However, if many duplicate guardian addresses exist in the new guardian set and the contract successfully upgrades to the new guardian set, it might increase the risk of an attacker compromising the multi-signature validation in the `verifySignatures` function.

Open the report
LowRedundant StatementsWormhole - Ethereum · CertiK · 8 Mar 2023Open

```solidity=14 event LogMessagePublished( address emitter_address, uint32 nonce, bytes payload ); ``` - `LogMessagePublished` is declared in `Events` but never emitted. --- ```solidity=15 event GuardianSetAdded(uint32 indexed index); ``` - `GuardianSetAdded` is declared in `Governance` but never emitted. --- ```solidity=9 event LogGuardianSetChanged( uint32 oldGuardianIndex, uint32 newGuardianIndex ); ``` - `LogGuardianSetChanged` is declared in `Events` but never emitted. --- ```solidity struct Asset { uint16 chainId; bytes32 assetAddress; } ``` The linked statements above do not affect the functionality of the codebase and appear to be either leftovers from test code or older functionality.

Open the report
LowUsage of `transfer()` for sending EtherWormhole - Ethereum · CertiK · 8 Mar 2023Open

After [EIP-1884](https://eips.ethereum.org/EIPS/eip-1884) was included in the Istanbul hard fork, it is not recommended to use `.transfer()` or `.send()` for transferring ether as these functions have a hard-coded value for gas costs making them obsolete as they are forwarding a fixed amount of gas, specifically `2300`. This can cause issues in case the linked statements are meant to be able to transfer funds to other contracts instead of EOAs.

Open the report
LowVariables That Could Be Declared as ImmutableWormhole - Ethereum · CertiK · 8 Mar 2023Open

The linked variables assigned in the constructor can be declared as `immutable`. 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
LowChecked Math Not Usedpyth2wormhole - Governance · CertiK · 13 Dec 2022Open

The above linked locations are places where checked math is not used and integer overflow is possible.

Open the report
LowInapproiate Program IDpyth2wormhole - Governance · CertiK · 13 Dec 2022Open

In L43, the program is using a default program ID provided by the Anchor framework: ```rust=43 declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); ```

Open the report
LowMissing Emit Eventspyth2wormhole - Governance · CertiK · 13 Dec 2022Open

In the program `staking`, the functions that affect the status of sensitive variables should be able to emit events. - `init_config` affects the `GlobalConfig` account - `update_governance_authority` affects `governance_authority` of the `GlobalConfig` account - `update_freeze` affects `freeze` of the `GlobalConfig` account - `create_stake_account` affects `StakeAccountMetadataV2` account and `VoterWeightRecord` account - `create_position` affects `PositionData` account - `close_position` affects `PositionData` account - `update_voter_weight` affects `VoterWeightRecord` account

Open the report
LowMissing Error Messages For Account Checkpyth2wormhole - Governance · CertiK · 13 Dec 2022Open

The **#[account(address = <expr>)]** in Anchor supports throwing an exception if the address does not match. It is better to use **#[account(address = <expr> @ custom_error)]** instead of **#[account(address = <expr>)]** to provide a customized error message and pass it back to the caller. Reference:[Accounts in anchor_lang](https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html)

Open the report
Show 10 more
LowMissing Mint Checkpyth2wormhole - Governance · CertiK · 13 Dec 2022Open

When withdrawing the staked token, a `destination` TokenAccount is given but missing the mint check against `config.pyth_token_mint`.

Open the report
LowRedundant Checkpyth2wormhole - Governance · CertiK · 13 Dec 2022Open

The following two risk validations at L322 to L332 and L345 to L355 in the `withdraw_stake` instruction are redundant. ```rust=318 let remaining_balance = stake_account_custody .amount .checked_sub(amount) .ok_or_else(|| error!(ErrorCode::InsufficientWithdrawableBalance))?; if utils::risk::validate( stake_account_positions, remaining_balance, unvested_balance, current_epoch, config.unlocking_duration, ) .is_err() { return Err(error!(ErrorCode::InsufficientWithdrawableBalance)); } transfer( CpiContext::from(&*ctx.accounts).with_signer(&[&[ AUTHORITY_SEED.as_bytes(), ctx.accounts.stake_account_positions.key().as_ref(), &[stake_account_metadata.authority_bump], ]]), amount, )?; ctx.accounts.stake_account_custody.reload()?; if utils::risk::validate( stake_account_positions, ctx.accounts.stake_account_custody.amount, unvested_balance, current_epoch, config.unlocking_duration, ) .is_err() { return Err(error!(ErrorCode::InsufficientWithdrawableBalance)); } ```

Open the report
LowValidations for Proposalpyth2wormhole - Governance · CertiK · 13 Dec 2022Open

When updating a voter's weight with the `CastVote` action, a proposal account is deserialized, but not sufficiently checked. For example, since the `governance` and `governing_token_mint` fields are not checked, it is possible for the proposal account to belong to different governance. Furthermore, the `state` field is not checked, meaning the proposal may have already succeeded or failed.

Open the report
LowValidity Checks on Vesting Schedulepyth2wormhole - Governance · CertiK · 13 Dec 2022Open

When creating a stake account, the user provides the vesting schedule. If `PeriodVesting` is chosen, four parameters need to be provided: - `initial_balance` - `start_date` - `period_duration` - `num_periods` Since there are operations that involve dividing by `period_duration` or `num_periods`, there should be checks to ensure that these parameters are positive, especially since they cannot be changed after the stake account is created.

Open the report
Low`num_` Validation in the Wrong PlacePyth-client · CertiK · 30 Nov 2022Open

The check `sptr->num_ >= PC_COMP_SIZE` is after using `num_` in loop which can cause `comp_[i]` overflow. Since the incrementation of `num_ ` occurs after checking we have assigned minor severity to this finding.

Open the report
Low`size_` is not Used in the Program LogicPyth-client · CertiK · 30 Nov 2022Open

The `size_` in the `pc_map_table` structure is calculated but not used in the program logic.

Open the report
LowLack of Zero Validation for `n2->v_`Pyth-client · CertiK · 30 Nov 2022Open

In the function `pd_div` there is not a check for `n2->v_ == 0`.

Open the report
LowMissing Error MessagesPyth-client · CertiK · 30 Nov 2022Open

The **assert** can be used to check for conditions and throw an exception if the condition is not met. It is better to provide a string message containing details about the error that will be passed back to the caller.

Open the report
LowMissing input validationPyth-client · CertiK · 30 Nov 2022Open

In the `dispatch()` function, there is a check to see if the `data_len` is less than `sizeof(cmd_hdr_t)`, however there isn't a check to see if `data_len` is larger.

Open the report
LowRedundant BackslashPyth-client · CertiK · 30 Nov 2022Open

The final continuation backslash is redundant as it is on the last line of the statement: ```cpp=142 #define PC_ADD_STR \ tag = (pc_str_t*)src;\ tag_len = 1 + tag->len_;\ if ( &src[tag_len] > end ) return ERROR_INVALID_ARGUMENT;\ sol_memcpy( tgt, tag, tag_len );\ tgt += tag_len;\ src += tag_len;\ ```

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

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.

Content

Facts

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.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-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