Camp security record

As of 30 Oct 2026, Camp has 3 audit reports by 1 security firm on Trustblock; the latest is by Quantstamp, dated 15 Sep 2025. Those reports list no open critical findings.

Critical findings
No open critical findings
Across 3 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.

Audits3

Newest first

1 other security report listed, not counted

AuditSecurity firmDateScopeCritical findingsReport
Camp - NFTSecurity firm: QuantstampDate: Scope: Not recorded (see the report)No open critical findingsOpen · certificate.quantstamp.com
Camp - Re-Audit

Audit

Version of an earlier report: counted once with it

Security firm: QuantstampDate: Scope: Not recorded (see the report)No open critical findingsOpen · certificate.quantstamp.com
Camp Network - 2Security firm: QuantstampDate: Scope: Not recorded (see the report)No open critical findingsOpen · certificate.quantstamp.com
CampSecurity firm: QuantstampDate: Scope: Files named in the reportNo open critical findingsOpen · certificate.quantstamp.com

Findings30

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

SeverityOpenAcknowledgedFixed
Critical000
High006
Medium104
Low7012
HighPossible Frontrunning of Buy Actions by Changing Token TermsCamp - NFT · Quantstamp · 15 Sep 2025Fixed

Token access can be bought via the marketplace according to the conditions outlined in the token `terms`. These terms can be updated by the token owner at any point using the `updateTerms()` function. Despite classical frontrunning being mitigated by the private mempool of the Camp network, this can be exploited by speculatively or accidentally frontrunning a call to `buyAccess()` and changing the subscription price, duration, or payment token. Payment can be made in either native or wrapped wCAMP tokens. In the case of wrapped tokens, the `buyAccess()` function in the marketplace does not check if native tokens were sent as well. If the payment token is changed to use wCAMP before the buy operation is executed, the transaction may consume both native and wrapped assets if the marketplace has sufficient approval for payment in wrapped tokens. The transaction will not revert, and the native tokens are lost in the marketplace. Similarly, the duration of the subscription could be lowered to extract more value per timeframe, or the price updated to the current approval between buyer and marketplace. Finally, when purchasing with ERC-20 tokens, the `_routeTokenPayment()` function transfers tokens directly from the buyer. IP owners may be able to expect incoming buy transactions, and exploit them by calling `updateTerms()` to increase the price to match the buyer's full token approval, effectively stealing all approved tokens rather than just the expected subscription price.

Open the report
HighTransferred NFTs May Be Missing Royalty VaultCamp - NFT · Quantstamp · 15 Sep 2025Fixed

The token owners receive royalty payments for subscriptions, and may also be rewarded for dispute settlements. These rewards are paid to the token owner, but not directly to avoid DoS attack vectors. Instead, each token owner is supposed to have a `RoyaltyVault` contract to receive these funds. These contracts are currently created during the minting process. However, when a token is transferred to a new owner, they may not have a vault registered in the `royaltyVaults` mapping. In this case, royalty payments will be transferred to the zero address, resulting in a loss of funds due to the following code: ``` solidity if (token == address(0)) { SafeTransferLib.safeTransferETH(ipToken.royaltyVaults(recipient), share); } else { SafeTransferLib.safeTransferFrom(token, payer, ipToken.royaltyVaults(recipient), share); } ``` Furthermore, there's no way for the new owner or the contract owner to create and register a vault to solve this issue.

Open the report
High`unstakeDelay` Can Be Exploited to DoS WithdrawsCamp · Quantstamp · 7 Aug 2025Fixed

This fork of the Synthetix `StakingRewards` contract introduces a `unstakeDelay` variable that is intended to enforce a minimum duration between `stake` and `withdraw` operations. For this purpose the contract records the last deposit timestamp for each user. However, the `stake()` function can be executed by a delegator, and does not require a minimum deposit. This way a user can stake a small amount on behalf of another user before their `unstakeDelay` passes, extending the lock and thus blocking them from withdrawing their tokens.

Open the report
HighMalicious Deposit with Dust-Sensitive `minAmount` Can Cause Denial of Service on `batchRelease()` FunctionCamp · Quantstamp · 7 Aug 2025Fixed

The `batchRelease()` function in `CampTimelockEscrowNativeOFT` processes deposits sequentially in a loop. When processing each deposit, it calls `_bridgeNativeTokens()`, which internally triggers LayerZero's dust removal mechanism via `_removeDust()`. This function rounds down the amount by removing decimal precision dust to ensure compatibility across chains with different token decimals. However, a malicious user can exploit this mechanism by setting a `_minAmount` parameter that becomes incompatible with the dust-adjusted amount. The LayerZero `OFTCore` contract performs a slippage check that reverts if the dust-adjusted amount falls below the user-specified `_minAmount`: ```solidity // In OFTCore._debitView() amountSentLD = _removeDust(_amountLD); amountReceivedLD = amountSentLD; // Check for slippage - this will revert if dust removal reduces amount below minAmount if (amountReceivedLD < _minAmountLD) { revert SlippageExceeded(amountReceivedLD, _minAmountLD); } ``` When this revert occurs during the `batchRelease` loop, it causes the entire transaction to revert. Since deposits are processed sequentially and there's no error handling around individual deposit processing, a single malicious deposit can permanently block the processing of all subsequent deposits in the queue, effectively creating a Denial of Service condition. Another simpler attack could be that `minAmount` is simply larger than the `_originalAmount`, and since there is no validation, it will always fail too. The fix with dust adjusted value should fix both scenarios.

Open the report
HighMissing Check of `msg.value` in `bridgeOut()` Function Allows DoS or Stealing FundsCamp · Quantstamp · 7 Aug 2025Fixed

The `bridgeOut()` function acts as the deposit entrypoint to the OFT bridge. It is supposed to receive native tokens, which are later bridged. The amount of tokens to be bridged is provided by the user with the `_originalAmount` parameter, however it is never verified against the actual supplied `msg.value`. This allows users to effectively initiate a bridging operation with debt, which then consumes tokens provided by other depositors. This is particularly exploitable by whitelisted users, who can drain the contract immediately. Other users cannot affect already initiated deposits, and only misuse tokens from those initiated after the malicious one. This could be exploited in combination with frontrunning to steal funds while ensuring that sufficient funds are available for the validations in the `send()` function. Alternatively, creating a deposit with very high debt would effectively DoS the bridge as a whole, as this deposit cannot be skipped in the `batchRelease()` function.

Open the report
HighUser Deposit May Not Cover Bridging FeesCamp · Quantstamp · 7 Aug 2025Fixed

The `send()` operation incurs additional fees exceeding the `_originalAmount` recorded during the initial deposit using the `bridgeOut()` function. This total value is calculated in the `_bridgeNativeTokens()` functions, and then paid from the contract balance. However, there is no validation that this fee is paid during the initial deposit. In its current form, the contract may run out of funds while still having unprocessed queued deposits.

Open the report
MediumExtreme Term Values Can Have Unintended ConsequencesCamp - NFT · Quantstamp · 15 Sep 2025Fixed

Token terms designate the conditions for buying access and minting derivative tokens. The input validation for these values is very lax, therefore extreme values can be assigned with potentially unintended consequences. 1. The price can be set to 0. In this case parents do not receive royalties anymore. This may be exploitable by creating derivative tokens to offer similar content as the original at a much lower price. Similarly, a very high price will make subscriptions impossible. 2. The duration can be set very low, for example 1 second. This would make subscription management near impossible, as access can only be bought for 1 period at a time. 3. Setting the royalty fee to 100% blocks the creation of derivative tokens, as a fee of 0 is invalid and the total must not be higher than 100%. Even other, very high values may make the creation of derivative tokens with multiple parents very challenging. ``` solidity if (newTerms.duration == 0) revert InvalidDuration(); if (newTerms.duration > maxTermDuration) revert InvalidDuration(); if (newTerms.royaltyBps == 0 || newTerms.royaltyBps > LibBPS.DENOMINATOR) revert InvalidRoyalty(); if (newTerms.paymentToken != address(0) && newTerms.paymentToken != wCAMP) revert InvalidPaymentToken(); ```

Open the report
MediumMaximizing Royalties by Frontrunning Token MintingCamp - NFT · Quantstamp · 15 Sep 2025Fixed

Any token can have up to 8 parent tokens, whose owners receive a share of all generated profit. This revenue split is set in stone during the minting process according to the royalty terms of the involved tokens. The `RoyaltyModule` ensures that the total sum of royalties does not exceed 100%. If however the total is less than 100%, the token owner of a listed parent is incentivized to frontrun the minting transaction to maximize their royalty. It may therefore seem reasonable to suggest users to ensure that all relevant royalties add up to 100%, but this may not be desired by the minter to ensure usability for derivative tokens.

Open the report
MediumGriefing Users Can Cause Deposits to Fail on the Destination Chain Due to Lack of Minimum `_gaslimit` ValidationCamp - Re-Audit · Quantstamp · 14 Aug 2025Fixed

The `bridgeOut()` and `release()` functions allow the caller to specify a `_gaslimit` argument. This value is the maximum amount of gas units the executor should use on the destination chain when completing the bridge. However, in the case of the `CampTimelockEscrow` contract, there is never any validation that some minimum amount of gas is available on the destination chain to ensure the bridge transaction is successful. Therefore, User A could process User B's deposit via the `release()` function, specifying a very small `_gaslimit`, causing the transaction to fail on the destination chain. This means the affected user would have directly to call the `lzReceive()` function on the target chain's implementation of the Camp OFT contract in order to complete their bridge action successfully.

Open the report
MediumSmall Deposits Can Increase Cost of `batchRelease()` FunctionCamp · Quantstamp · 7 Aug 2025Fixed

This escrow bridge relies on a queue to manage deposits. All new deposits are appended to the queue and share the same `holdPeriod` before the bridging operation can commence. The queue is checked and consumed using the `batchRelease()` function, which increments a global cursor indicating the cutoff index of processed deposits in the queue. For each batch, some supplied maximum amount of entries is checked, bridging deposits where the `unlockTimestamp` has passed. No deposits in the queue can be skipped, and the `bridgeOut()` function is likely substantially cheaper in gas than the `send()` function and does not enforce a minimum deposit. Therefore, small deposits could be use to extend the size of the queue substantially, requiring someone to pay the gas cost of initiating all those bridging transactions using the `batchRelease()` function before any deposits later in the queue can be executed.

Open the report
Show 10 more
LowDeleted and Disputed Tokens Still Receive RoyaltiesCamp - NFT · Quantstamp · 15 Sep 2025Fixed

Royalties are paid out when access to associated tokens in purchased. The `_distributeRoyalty()` function is responsible for calculating and distributing the generated profit among the token owner and the parent tokens. Currently, the state of the parent token is not checked, therefore `DELETED` and `DISPUTED` tokens keep earning royalties through derivatives.

Open the report
LowJudgement Can Be Cast for Nonexistant DisputesCamp - NFT · Quantstamp · 15 Sep 2025Fixed

Disputes can be raised by anyone for a token suspected of infringing on intellectual property. The token owner is then given time to assert counterevidence. After some cooldown period, the contract administrator is able to cast judgement on the dispute, deciding either in favor of the initiator or the token owner. However, due to bad input validation and enum default states, the `setDisputeJudgement()` function can be called for any token ID, those without associated dispute and even unminted tokens. ```solidity if (dispute.status != DisputeStatus.Raised && dispute.status != DisputeStatus.Asserted) revert InvalidDisputeStatus(); ``` Further, casting judgement pays out some reward with funds associated with other disputes. In turn, the contract may eventually run out of funds, causing a DoS on judgements and requiring the owner to equalize these losses.

Open the report
LowMarketplace Can Receive Native Tokens without Ability to RescueCamp - NFT · Quantstamp · 15 Sep 2025Fixed

The marketplace contract exposes an empty `receive()` function that is capable of receiving native tokens. However, these tokens are then stuck in the contract, as there is no way to rescue them. Further, the contract does not require a way to receive native tokens without associated action.

Open the report
LowParent Tagging Is Slower than MintingCamp - NFT · Quantstamp · 15 Sep 2025Fixed

The dispute module allows users to accuse token owners of intellectual property infringement, and get the token blocked after judgement by the contract owner. Judgement can be cast after some cooldown period has elapsed, which allows the token owner to assert counterevidence. Disputes should then propagate to derived tokens, this is initiated by users notifying the contract using the `tagChildIp()` function. In its current form, this raises a new dispute for the child token. However, a new derivative tokens could be minted before the cooldown period of this dispute has elapsed, leading to a potential scenario where the dispute module cannot catch up with minting.

Open the report
LowTokens Can Be Minted with Already Disputed ParentsCamp - NFT · Quantstamp · 15 Sep 2025Fixed

During the token minting process, the requirements for parent tokens are checked in the `setParentIpsAndRoyaltyPercents()` function. Currently, this function only reverts if a parent token has been deleted, but not if it has been disputed. Since the `DisputeModule` contract offers functionality to dispute tokens based on disputed parents, it seems reasonable to block such minting actions.

Open the report
LowVery Small Prices Can Be Rounded to 0Camp - NFT · Quantstamp · 15 Sep 2025Fixed

During the `buyAccess()` operation, royalty calculations are applied to the price. For very low prices, and very low bps the result could be rounded to 0, resulting in empty transfers. ``` solidity uint16 constant DENOMINATOR = 10_000; function applyBps(uint256 amount, uint16 bps) internal pure returns (uint256) { return (amount * bps) / DENOMINATOR; // <-- If amount * bps < DENOMINATOR, this will round to 0 } ```

Open the report
LowBridge Token Refund Address May Lock Refunded Tokens in EscrowCamp - Re-Audit · Quantstamp · 14 Aug 2025Fixed

In the `_bridgeNativeTokens` function, the LayerZero `nativeOFTAdapter.send` call uses `address(this)` (the `CampTimelockEscrow` contract) as the refund address. This can result in refunded tokens becoming stuck in the escrow contract instead of being returned to the intended recipient.

Open the report
LowMissing Recipient Address ValidationCamp - Re-Audit · Quantstamp · 14 Aug 2025Fixed

In `bridgeOut()`, the `_to` parameter specifies the recipient address. However, there is no check to ensure `_to` is not the zero address.

Open the report
LowStrict Equality Check for `_fee.nativeFee` May Cause Soft DosCamp - Re-Audit · Quantstamp · 14 Aug 2025Fixed

The `release()` function contains the following check to ensure the user provides the necessary LayerZero fees: ```solidity if (msg.value != _fee.nativeFee) revert InvalidNativeBridgeDeposit(); ``` However, due to the dynamic nature of LayerZero fees, it may be difficult for the frontend to accurately predict what the exact fee will be when the transaction is executed. This may lead to a soft DoS where users' transactions unexpectedly fail due to slight changes in the LayerZero fee.

Open the report
LowMissing Documented Access Control for `batchRelease()` FunctionCamp · Quantstamp · 7 Aug 2025Fixed

The README document suggests that the contract allows the batched release of matured deposits by a authorized keeper. However, the corresponding `batchRelease()` function is not access controlled, and no `keeper` role is assigned to any user. However, this function may not need to rely on a privileged user for execution, and instead may exploit the natural incentive of queued depositors to ensure smooth bridging operations.

Open the report
Show 2 more
LowNo Method to Handle Funds Sent via the Empty `receive()` Function in the `CampStaker` ContractCamp · Quantstamp · 7 Aug 2025Fixed

The `CampStaker` contract has an empty `receive()` function, allowing to receive native tokens. However there is no method to withdraw any native tokens if they are sent to the contract.

Open the report
LowUnchecked Uint256 to Uint128 Down-Cast May Truncate Very Large DepositsCamp · Quantstamp · 7 Aug 2025Fixed

The `bridgeOut()` function performs unchecked down-casting from `uint256` to `uint128` when storing deposit amounts in the `Deposit` struct. Both `_originalAmount` and `_minAmount` parameters are cast to `uint128` without validation that they fit within the target type's range. ```solidity function bridgeOut(address _to, uint128 _gaslimit, uint256 _originalAmount, uint256 _minAmount) external payable nonReentrant { // ... code ... else { uint256 _totalDeposits = totalDeposits; pending[_totalDeposits].to = _to; pending[_totalDeposits].amount = uint128(_originalAmount); // Unchecked downcast pending[_totalDeposits].minAmount = uint128(_minAmount); // Unchecked downcast // ... rest of function } } ``` If either `_originalAmount` or `_minAmount` exceeds `type(uint128).max` (approximately 3.4 × 10^38), the values will silently wrap around to a much smaller number due to integer overflow. This could result in users unintentionally bridging significantly fewer tokens than intended, leading to a loss of funds without any explicit revert or warning.

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="camp"
></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