CaliburProp Info

TBA

CaliburProp Logo

TrustNet Score

The TrustNet Score evaluates crypto projects based on audit results, security, KYC verification, and social media presence. This score offers a quick, transparent view of a project's credibility, helping users make informed decisions in the Web3 space.

47.90
Poor Excellent

Real-Time Threat Detection

Real-time threat detection, powered by Cyvers.io, is currently not activated for this project.

This advanced feature provides continuous monitoring and instant alerts to safeguard your assets from potential security threats. Real-time detection enhances your project's security by proactively identifying and mitigating risks. For more information, click here.

Security Assessments

Select the audit
"Static Analysis Dynamic Analysis Symbolic Execution SWC Check Manual Review"
Contract address
N/A
Network N/A
License N/A
Compiler N/A
Type N/A
Language Solidity
Onboard date 2026/05/06
Revision date 2026/05/06

Summary and Final Words

No crucial issues found

The contract does not contain issues of high or medium criticality. This means that no known vulnerabilities were found in the source code.

Contract owner cannot mint

It is not possible to mint new tokens.

Contract owner cannot blacklist addresses.

It is not possible to lock user funds by blacklisting addresses.

Contract owner cannot set high fees

The fees, if applicable, can be a maximum of 25% or lower. The contract can therefore not be locked. Please take a look in the comment section for more details.

Token transfer can be locked

Owner can lock user funds with owner functions.

Token cannot be burned

There is no burning within the contract without any allowances

Ownership is not renounced

The owner retains significant control, which could potentially be used to modify key contract parameters.

Contract is upgradeable

The contract uses a proxy pattern or similar mechanism, enabling future upgrades. This can introduce risks if the upgrade mechanism is not securely managed.

Scope of Work

This audit encompasses the evaluation of the files listed below, each verified with a SHA-1 Hash. The team referenced above has provided the necessary files for assessment.

The auditing process consists of the following systematic steps:

  1. Specification Review: Analyze the provided specifications, source code, and instructions to fully understand the smart contract's size, scope, and functionality.
  2. Manual Code Examination: Conduct a thorough line-by-line review of the source code to identify potential vulnerabilities and areas for improvement.
  3. Specification Alignment: Ensure that the code accurately implements the provided specifications and intended functionalities.
  4. Test Coverage Assessment: Evaluate the extent and effectiveness of test cases in covering the codebase, identifying any gaps in testing.
  5. Symbolic Execution: Analyze the smart contract to determine how various inputs affect execution paths, identifying potential edge cases and vulnerabilities.
  6. Best Practices Evaluation: Assess the smart contracts against established industry and academic best practices to enhance efficiency, maintainability, and security.
  7. Actionable Recommendations: Provide detailed, specific, and actionable steps to secure and optimize the smart contracts.

A file with a different Hash has been intentionally or otherwise modified after the security review. A different Hash may indicate a changed condition or potential vulnerability that was not within the scope of this review.

Final Words

The following provides a concise summary of the audit report, accompanied by insightful comments from the auditor. This overview captures the key findings and observations, offering valuable context and clarity.


Smart Contract Analysis Statement

Contract Analysis

The Payout contract implements a single-tenant trader-payout distributor that is pre-funded by the team and pays out USD-denominated amounts from the supported-token list in iteration order. While the overall design is straightforward and well wired into the access-control toolkit, a few areas need attention:

  • Validator authority is broad. claim is gated by VALIDATOR_ROLE alone, with no signature recovery, no per-account cap, and no off-chain attestation. A single compromised validator key can therefore drain the entire pre-funded reserve in one transaction. The companion ReferralClaimer contract uses an EIP-712 signed-claim pattern that is materially safer; bringing Payout to parity removes the single-key-drain risk.
  • Distribution rounds down silently. The per-account ledger is set to the full accumulatedAmountUsd, while the actually-transferred amount is truncated whenever the cumulative is not aligned to the smallest supported token's scale. The lost dust is silently retained by the contract and is unrecoverable for the trader on subsequent claims. The loss per claim is small (up to about $0.000001 for 6-decimal stablecoins) but it accumulates and the ledger no longer matches what the trader received.
  • EIP712Upgradeable is in the inheritance chain and __EIP712_init runs at initialize, but no EIP-712 functionality is actually exposed. The dead inheritance occupies storage and adds gas without serving any current purpose, and it is a future-bug surface for a maintainer who assumes the domain separator is wired up correctly across upgrades.

Ownership Privileges

The ownership of the contract has been organised through OpenZeppelin's AccessControlEnumerable. DEFAULT_ADMIN_ROLE, UPGRADER_ROLE and VALIDATOR_ROLE are all granted to the deployer-supplied admin at initialize time. The owner retains full privileges including:

  • Adding or removing supported tokens.
  • Pausing and unpausing the contract.
  • Withdrawing the contract's funds to any address through withdrawTo.
  • Granting and revoking VALIDATOR_ROLE, which controls the claim function.
  • The admin cannot mint or create tokens; payouts only come from the contract's pre-funded balance.
  • The admin cannot bypass pause for the claim function.
  • The admin cannot grant a role and have it apply retroactively; revocation takes effect on the next call.
  • There is no native-asset handling; the contract cannot accept ETH and cannot lock it.

Security Features

The contract implements several positive security features:

  • nonReentrant on claim plus pause coverage that halts new claims during incident response.
  • Per-account cumulative tracking that prevents the same off-chain account from being paid more than the validator-asserted total even across many distinct claims.
  • A safeERC20 distribution path that handles tokens with non-standard return semantics safely.
  • Implementation contract calls _disableInitializers in the constructor, blocking proxy front-running on initialize.

Note - This audit report consists of a security analysis of the Payout smart contract. This analysis did not include economic analysis of the contract's tokenomics. Other contracts associated with the project (EvaluationVault, ReferralRegister, ReferralClaimer) are covered in separate reports. We recommend investors do their own research before integrating with the contract.

Files and details

Functions
public

/

State variables
public

/

Total lines
of code

/

Capabilities
Hover on items

/

Findings and Audit result

medium Issues | 1 findings

Pending

#1 medium Issue
Validator role can drain the contract to any address with no second factor
Payout.sol
L116-128
Description

claim is gated by onlyRole(VALIDATOR_ROLE) but does no signature recovery, no per-account cap, and no off-chain attestation. A single validator key can choose any trader, any accountId, and any monotonically-increasing cumulative amount up to the contract's full balance. Compromise of the validator key is therefore a single-step total drain. There is also no rate-limit or daily cap, so the loss is bounded only by the funded balance.

low Issues | 1 findings

Pending

#1 low Issue
Distribution rounds down silently and the ledger advances by full cumulative
Payout.sol
L116-128
L171-189
Description

claim writes claimedByAccountId[accountId] = accumulatedAmountUsd and then calls _distribute. Inside _distribute, transferToken = _usdToToken(transferUsd, decimals) truncates when transferUsd is not a multiple of 10**(18-decimals). The loop subtracts the full transferUsd from remainingUsd, not the rounded-back-to-USD amount, so the require remainingUsd == 0 check passes even though the trader received less than the ledger says. The lost dust stays in the contract; the trader cannot recover it because subsequent claim calls use the already-incremented ledger as the baseline. For 6-decimal stablecoins the loss per claim per token is up to ~1e-6 USD; aligned cumulatives lose nothing.

optimization Issues | 3 findings

Pending

#1 optimization Issue
Use custom errors instead of revert strings
Payout.sol
L59-188
Description

Every require uses a string literal.

Pending

#2 optimization Issue
Loop counter optimisation in _distribute
Payout.sol
L174
Description

for (uint256 i = 0; i < length && remainingUsd > 0; i++) uses a postfix increment with checked arithmetic.

Pending

#3 optimization Issue
Constructor non-payable
Payout.sol
L51
Description

The implementation constructor is non-payable.

informational Issues | 6 findings

Pending

#1 informational Issue
Inherited but unused EIP712Upgradeable
Payout.sol
L24
L63
Description

Payout inherits EIP712Upgradeable and calls __EIP712_init in initialize, but never invokes _hashTypedDataV4, _domainSeparatorV4, or any signature verification. The inheritance occupies storage slots and adds gas to deployment plus initialization without serving any current function. A future maintainer could conclude that EIP-712 is already wired up and add a signed-claim path without re-checking that __EIP712_init is actually called on a fresh proxy after upgrade.

Pending

#2 informational Issue
setSupportedToken ignores EnumerableMap return values (event/state mismatch)
Payout.sol
L83-92
Description

_supportedTokens.set returns true if a new entry was added and false if an existing one was overwritten. _supportedTokens.remove returns true only if the entry existed. Both return values are dropped, so setSupportedToken(t, false) for a token never added emits TokenSupportUpdated(t, 0, false) even though no state changed, and re-adding an existing token emits the added event. Off-chain indexers see phantom events.

Pending

#3 informational Issue
_distribute and getAllTokens loops are admin-bounded
Payout.sol
L171-189
Description

_distribute reads IERC20(token).balanceOf(address(this)) on every iteration. The token list is admin-controlled so an attacker cannot inflate it, but an admin who adds tokens with adversarial balanceOf or simply too many tokens can grief future claims and exhaust gas budgets.

Pending

#4 informational Issue
Equal-zero comparison flagged on balanceUsd (false positive)
Payout.sol
L178
Description

Slither flags if (balanceUsd == 0) continue as a dangerous strict equality. Donated balance to the contract only increases the amount this iteration can satisfy and never breaks the invariant, so the strict equality is safe here.

Pending

#5 informational Issue
Deprecated state variable _deprecated_claimed
Payout.sol
L33-35
Description

A mapping(address => uint256) slot is preserved at slot 0 with the @custom:oz-renamed-from claimed hint. This is correct upgrade hygiene for the OpenZeppelin upgrade plugin, but it occupies a storage slot that cannot be reclaimed.

Pending

#6 informational Issue
Storage layout sizing differs across the suite
Payout.sol
L194
Description

Payout reserves __gap of 49 plus 5 used slots, totalling 54. EvaluationVault reserves 55 in total and ReferralClaimer reserves 52. Harmless on a fresh deploy but inconsistent.