Camp

QuantstampFiles named in the report

Quantstamp reviewed Camp on 7 Aug 2025: 13 findings, 0 critical; 8 marked fixed, 5 open.

No open critical findings
Open the reportOpen Camp’s security record

Report hosted on certificate.quantstamp.com.

Trustblock does not review code.

Findings13

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

SeverityOpenAcknowledgedFixed
Critical000
High004
Medium001
Low503
High`unstakeDelay` Can Be Exploited to DoS WithdrawsFixed

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.

HighMalicious Deposit with Dust-Sensitive `minAmount` Can Cause Denial of Service on `batchRelease()` FunctionFixed

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.

HighMissing Check of `msg.value` in `bridgeOut()` Function Allows DoS or Stealing FundsFixed

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.

HighUser Deposit May Not Cover Bridging FeesFixed

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.

MediumSmall Deposits Can Increase Cost of `batchRelease()` FunctionFixed

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.

LowMissing Documented Access Control for `batchRelease()` FunctionFixed

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.

LowNo Method to Handle Funds Sent via the Empty `receive()` Function in the `CampStaker` ContractFixed

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.

LowUnchecked Uint256 to Uint128 Down-Cast May Truncate Very Large DepositsFixed

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.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (24)

  • src/interfaces/ICampStakingVault.sol
  • src/interfaces/Events.sol
  • src/CampStakingVault.sol
  • src/CampStaker.sol
  • broadcast/Interact.s.sol/123420001114/run-1743437475.json
  • broadcast/Interact.s.sol/123420001114/run-latest.json
  • broadcast/Deploy.s.sol/123420001114/run-latest.json
  • test/mock/MockERC20.sol
  • github/workflows/test.yml
  • src/utils/Errors.sol
  • src/interfaces/IWCAMP.sol
  • script/NotifyRewards.s.sol
  • README.md
  • broadcast/Deploy.s.sol/325000/run-latest.json
  • test/CampStakingVaultTest.t.sol
  • src/WrappedCAMP.sol
  • test/WrappedCAMP.t.sol
  • gitignore
  • foundry.toml
  • lcov.info
  • script/Deploy.s.sol
  • test/utils/WrappedCAMP.sol
  • gitmodules
  • broadcast/Deploy.s.sol/325000/run-1749053324.json

Part of Camp’s security record

Camp
Audits3 audits by 1 firmLast audit Sep 2025, by Quantstamp.
Critical findingsNo open critical findingsAcross 3 reports.
Open Camp’s security record

Security firm

Quantstamp

236 projects reviewed · 332 reports · latest Oct 2026

See Quantstamp’s audits

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