HCOW Info
HCOW is a GameFi ecosystem built around one idea: games should be provably fair, and the value they create should flow back to the people who play and hold.
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.
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
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.
Ownership is not renounced
The owner retains significant control, which could potentially be used to modify key contract parameters.
Contract is not upgradeable
The contract does not use proxy patterns or other mechanisms to allow future upgrades. Its behavior is locked in its current state.
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:
- Specification Review: Analyze the provided specifications, source code, and instructions to fully understand the smart contract's size, scope, and functionality.
- Manual Code Examination: Conduct a thorough line-by-line review of the source code to identify potential vulnerabilities and areas for improvement.
- Specification Alignment: Ensure that the code accurately implements the provided specifications and intended functionalities.
- Test Coverage Assessment: Evaluate the extent and effectiveness of test cases in covering the codebase, identifying any gaps in testing.
- Symbolic Execution: Analyze the smart contract to determine how various inputs affect execution paths, identifying potential edge cases and vulnerabilities.
- Best Practices Evaluation: Assess the smart contracts against established industry and academic best practices to enhance efficiency, maintainability, and security.
- 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
Re-audit Scope
This statement replaces the one issued on 28 August 2026 and covers the vesting contract as it stands at the pinned commit. The contract grew from four hundred and sixteen lines to seven hundred, with three hundred and fifty eight lines changed, so it was reviewed as new code function by function rather than as a patch set. All ten findings from the first round were re-verified against the new source and five new observations are recorded.
Contract Analysis
The contract holds a table of vesting schedules, each with an immediate unlock at a launch timestamp, then a cliff, then linear release. Schedules are non-revocable once sealed. The owner may only add schedules, only before sealing, and never at or after the launch timestamp. Release is permissionless and always pays the beneficiary rather than the caller, so a beneficiary who loses access to gas can still be paid. Time is measured from a launch timestamp fixed at construction, so a schedule added late still vests correctly for elapsed time.
Three structural changes since the first round account for most of the new code. The four figures the loaded table must reach are now constructor arguments held as immutables, and the sealing function takes no parameters at all. Funding and sealing happen in one transaction, with the contract computing the amount it needs rather than accepting a figure from the caller. And the entire table can be cleared and reloaded in a single call while the contract is unsealed and before the launch timestamp, which makes a mistyped row recoverable without a redeployment.
Ownership Privileges
The owner's powers end permanently at sealing. Before sealing the owner can:
- Add schedules, and replace the entire table in one call, until the launch timestamp
- Nominate a successor owner, who must accept before the handover takes effect, and withdraw that nomination
- Decline to seal, which is the one remaining way an owner can deny the outcome; it cannot take the money, because the seal is bound to four figures fixed before any token existed
After sealing the owner can do nothing at all. Ownership cannot be renounced at any point, by design, because renouncing before sealing would make sealing impossible forever and after sealing there is nothing to renounce. Sealing itself becomes permissionless once the launch timestamp passes, deliberately, so that an owner key which is lost, frozen or simply unwilling cannot hold every beneficiary's tokens hostage.
Two functions are permissionless by design: releasing to a beneficiary, and pushing a foreign token sent here by mistake to an address fixed at deployment. Neither can reduce what any beneficiary is owed, and the second refuses the vesting token by name.
Security Features
- Funding and sealing are atomic, and the contract computes the amount itself, so there is no figure to type and therefore no wrong figure to type. This closes the total loss path the first round measured, where sending a balance rather than an amount left the contract permanently unsealable and everything in it stranded
- The four commitments are immutable and fixed before any schedule exists, so a table that diverts tokens cannot be sealed however the caller states the figures. We confirmed this directly against a table diverting half the supply while keeping the count, the total and the unlock figure identical
- The table can be replaced in one call but never left empty, so the correction path cannot itself become a loss path. A contract dusted with one wei by a third party is still correctable, which an earlier version of the fix was not
- A plausibility bound of one hundred and twenty months is applied to the sum of the two period fields at the moment of entry, so a transposed value fails immediately rather than at the seal
- Release writes state and emits before its transfer, so re-entering finds nothing releasable
- The rounding in the vesting calculation was re-derived and floors in both directions, favouring the contract during the vesting period, and both terminal branches return the stored total so no dust is stranded at maturity
- Fourteen machine-searched invariant properties now cover the arithmetic and the state machine
Findings Summary
Both medium-severity findings from the first round are fixed. The funding path no longer has a total loss branch, and a mistyped schedule is now correctable in one call. Six of the remaining eight are fixed as recommended, one was fixed differently and better than recommended, and one is partially fixed.
Five new observations, all low or below, and none of them a route to loss for a beneficiary. Three concern the boundaries of the fixes rather than defects in them. The atomic funding call pulls from its own caller and is owner-only until the launch timestamp, so it is available only where the vesting owner is also the address holding the supply; with the custody split the token's design notes describe, a hardware wallet treasury and a separate operational owner, the operator falls back to the two step sequence the atomic call was written to replace. Because the published allocation commits one hundred percent of supply, any holder can still make the contract permanently unsealable by burning one wei, which was the state the first round asked to become unreachable; what changed, and it matters, is that the atomic funding means nothing is lost when it happens, so the cost is a redeployment rather than stranded tokens. And the surplus sweep that was omitted on the reasoning that a surplus can no longer arise is still needed, because a plain transfer by any holder at any time still strands tokens permanently, which we measured using a beneficiary returning tokens to the address they came from.
The remaining two are documentation and ordering. The named error that reports a committed total above live supply is unreachable on the funding path an operator is told to use, because the transfer runs first and the token's own error fires instead. And the atomic funding call writes state after an external call while this contract, alone among the three that move value, carries no reentrancy guard; that is not exploitable against a token with no transfer hooks, and every re-entrant path is closed anyway, but it is the opposite of the ordering discipline applied to the profit sharing contract in the same round.
Note - This Audit report consists of a security analysis of the HCOWVesting smart contract. This analysis did not include economic analysis of the vesting schedule or the allocation table, both of which are business decisions. We recommend investors do their own research before investing.
Files and details
Findings and Audit result
medium Issues | 2 findings
Resolved
#1 medium Issue
Committing the entire supply makes funding brittle, and a non-exact transfer loses everything
Releases are gated on sealing, and sealing requires the contract to hold the full committed total. The published allocation commits the entire two hundred million supply, so the contract must hold every token in existence to become operational, and the committed total is checked only against a supply figure snapshotted at deployment rather than against live supply. Peer review correctly identified that the outcome depends on how the funding transfer is performed, and testing settles it. Where the operator follows the documented instruction and transfers the exact committed total, a shortfall makes that transfer revert atomically, no tokens move, the treasury keeps everything and redeploying against a corrected total recovers fully. Where the operator instead sends the balance it holds, which is an ordinary way to move a full position and is offered as a single action by common multisig interfaces, the transfer succeeds and the contract can never be sealed: releases fail permanently, there is no sweep, and the entire amount transferred in is unrecoverable by anyone. The severity reflects that the loss is total and irreversible but requires the operator to deviate from a written instruction, and that only the treasury holds tokens at that stage so no outsider can trigger it.
Resolved
#2 medium Issue
Schedule entry cannot be corrected once written
Adding a schedule writes to four pieces of state and none of them can be undone. There is no function to remove or amend an entry, and an existing beneficiary cannot be overwritten. The only bound applied at entry is that the running total must not exceed the supply figure snapshotted at deployment, so a mistyped amount well inside that limit is accepted silently; testing confirmed a single mistyped entry inflating the committed total by ninety million tokens with no available path to reduce it. Immutability of a written schedule is a deliberate and correct property that the contract advertises, so on its own this is a usability hazard rather than a loss. It becomes a loss only in combination with the funding ordering reported separately, where the inflated total is not discovered until after the tokens have moved.
low Issues | 7 findings
Resolved
#1 low Issue
Seal commitment is self-referential and does not bind the owner on its own
The seal function checks the beneficiary count, the committed total, the unlock figure and a running commitment hash against four values, and all four are passed in by whoever calls it. An owner who loaded the table it intended satisfies the check by reading the live values back off the contract, which turns it into a restatement rather than a verification. Testing confirmed an owner sealing a table that diverted twenty million tokens to itself while the beneficiary count and the committed total both matched the published figures. Three things keep this at a low rating. The commitment hash does move, and the deployment runbook already instructs the operator to compute it independently from the signed-off allocation and to read every schedule back field by field, and either check catches the substitution. The contract's own documentation states plainly that before sealing the owner key can write itself a full unlock and take the balance. And the runbook assigns ownership to the treasury address, which already holds the entire supply, so an owner intent on taking the money would simply not fund the contract at all - diverting through the table is a harder route to less. The residual risk is presentational: a sealed contract whose headline figures match the published ones while the underlying table does not.
Pending
#2 low Issue
Surplus tokens and foreign assets are permanently stranded
The funding check accepts a balance greater than the amount owed, so anything transferred in above the scheduled total is locked for the life of the chain. The same is true of any other token sent to the address. There is no sweep and no rescue. This interacts badly with the funding problem described elsewhere in this report, because the natural reaction to a shortfall message under time pressure is to send more than the figure requested. Native currency cannot arrive through an ordinary transfer since the contract has no receive function, and nothing reads its own balance, so that case is harmless.
Resolved
#3 low Issue
No plausibility bound on cliff and linear periods
Both period fields accept any value up to sixty five thousand months, which is over five thousand years. Nothing overflows, because the arithmetic stays far inside the available range, but a mistyped value is invisible to the scheduled total, to the beneficiary count and to the reported unlock figure whenever the immediate unlock is zero. The running commitment hash does catch it, but only at the point of sealing, by which time the contract is funded and the table cannot be corrected.
Resolved
#4 low Issue
Misleading revert reason and unchecked array index
The renounce function is declared pure and therefore cannot read the sealed flag, so it always reverts with an error that says the contract is already sealed, including in the ordinary case where it is not. It also drops its access control, so any caller reaches the same message. Neither costs anything, but a revert reason that asserts a false fact about the contract is worth correcting in code that is published for review. Separately, the beneficiary accessor performs no bounds check and reverts with a low-level panic, which is harder for callers to attribute than a named error.
Pending
#5 low Issue
The atomic funding path is unavailable to the custody split the design notes describe
The atomic funding call pulls the shortfall from its own caller, and the authorisation helper restricts the caller to the owner until the launch timestamp. The two together mean the atomic path is available only where the owner is itself the address holding the committed total. The token's design notes describe a different arrangement, a hardware wallet or multisig treasury holding the supply and a vesting owner that loads the table, and in that arrangement the treasury cannot call the atomic path because it is not the owner, while the owner can call it but holds nothing to pull. Measured on the pinned commit: with the supply on a separate treasury key the treasury's call reverts for lack of authorisation and the owner's reverts for lack of balance. The only route left before the launch timestamp is to transfer and then seal as two transactions, which reopens the funded and unsealed window that the atomic call was introduced to close, and in that window every owner power is still live; we confirmed the table can be replaced while the contract holds the whole supply. Past the launch timestamp the restriction lifts and any address may fund and seal atomically, so the atomic path exists but only after the deadline the runbook says to seal before. Every one of the project's own tests sets the vesting owner equal to the token treasury, so the separated configuration is exercised nowhere. Severity is low because no funds are lost on either path and the workaround is a documented procedure rather than a hazard, but it is the fix for two first round findings being unavailable in the configuration those findings were written about.
Pending
#6 low Issue
A one wei burn still makes the contract permanently unsealable
The first round asked for the one wei burn to become unreachable, on the reasoning that the table load itself should fail once live supply had fallen below the committed total. The shipped design keeps the deployment snapshot as the bound at entry, for a stated and sound reason, and moves the live comparison to the seal. The consequence is that the brick is still reachable: because the published allocation commits one hundred percent of supply, any holder of any HCOW can make the contract permanently unsealable by burning one wei, with no owner override and no path back. Measured on the pinned commit: after a one wei burn the table loads normally, the atomic funding call reverts, sealing reverts by name, sealing after the launch timestamp reverts identically even though it is permissionless by then, replacing the table is closed by the launch timestamp, and release is gated on the seal so no beneficiary can ever be paid. Replacing the table earlier does not help either, because the four commitments are immutable and no table can satisfy a scheduled total above live supply. What genuinely improved is the loss: the funding is atomic, so a failing seal unwinds the transfer and nothing is stranded. The cost is a redeploy and re-collecting every address in the table rather than a loss of funds, which is why this stays at low. Exposure is small during a prepared launch window, when only the treasury holds HCOW, and becomes general the moment any HCOW is distributed while the contract is still unsealed.
Pending
#7 low Issue
Anyone can strand HCOW on the contract permanently, which the omitted sweep was for
The response omits the vesting token sweep on the reasoning that the atomic funding path computes the amount itself, so sending more than requested is no longer an action anyone can take, and that what remains is a manual transfer outside the runbook. The first half is right about the operator and wrong about everyone else. A plain token transfer to the contract address is available to any holder at any time, before or after sealing, requires no privilege and involves no runbook, and the resulting balance is unreachable forever: the foreign token rescue refuses the vesting token by name, and release pays only what a schedule owes. Measured on the pinned commit: a beneficiary paid at maturity returns five thousand tokens to the address they came from, which is the ordinary wrong-address mistake rather than a deviation from a procedure; every schedule is then paid in full, every release path reverts with nothing left to release, and the five thousand tokens remain on the contract for the life of the chain. Nobody but the sender loses anything, and no beneficiary's entitlement is affected, which is why this is low rather than higher. It is recorded because the premise the omission rests on is stated in the response as a property of the contract and is not one.
optimization Issues | 3 findings
Resolved
#1 optimization Issue
Minor gas and consistency improvements
Three small items. The batch schedule loader uses a string revert message where the rest of the file uses custom errors, which costs deployment bytecode. A local interface declares a total supply function that the already-imported token interface provides. The ownership transfer override applies an access modifier that the parent function it delegates to applies again, costing one storage read per call. None of these affect behaviour.
Resolved
#2 optimization Issue
Unlock total recomputed on the failure path
The seal function computes the total unlock figure by looping over every beneficiary, and the error raised on mismatch recomputes it as one of its arguments. Because revert arguments are only evaluated when the revert is actually reached, and the condition chain short-circuits, the loop runs once on the success path and twice only when the seal fails. At the two hundred beneficiary ceiling the measured cost stays well inside a single block on the target chain, so this is not a denial-of-service concern, only redundant work on a path that reverts anyway.
Pending
#3 optimization Issue
State is written after an external call in the atomic funding path
The atomic funding call performs the incoming transfer and then writes state inside the seal, and this contract carries no reentrancy guard. Three Slither detectors report it and all three are new in this round, which makes it a direct consequence of the fix for the funding findings. It is not exploitable, for two independent reasons that were both checked. HCOW is a plain OpenZeppelin token with no transfer hook, so nothing can re-enter. And every re-entrant path is closed anyway: adding or replacing schedules is owner-only and the re-entrant caller would be the token, release is gated on the sealed flag which is still false, and a re-entrant seal or funding call sets the flag so that the outer seal then reverts and takes the whole transaction with it. It is recorded as an optimisation rather than a defect because the ordering discipline applied to the profit share contract's settlement in this same round, moving every transfer to the end so that no state write follows an external call, was applied here in reverse.
informational Issues | 3 findings
Resolved
#1 informational Issue
Funded and unsealed window cannot be eliminated by ordering alone
The design notes acknowledge that before sealing, the owner key can add a schedule for itself at a full immediate unlock and take what the contract holds, and recommend funding, verifying and sealing in one session as the mitigation. That mitigation cannot close the window, because sealing requires the contract to be funded first, so a funded and unsealed state is structurally unavoidable. It can only be reduced to zero by making the two steps atomic.
Resolved
#2 informational Issue
Vesting arithmetic verified correct in both rounding directions
The release calculation was checked for precision loss and rounding direction. Division floors in both places, which favours the contract during the vesting period, and the fully-vested branch returns the stored total directly rather than re-deriving it from the formula, so the accumulated flooring error is discarded at maturity rather than stranding dust. The sum of all releases can never exceed the schedule total, because the linear term is strictly bounded below the remainder, and it reaches the total exactly once vesting completes. The unchecked narrowing of the release amount was also checked and is provably safe, since the amount is bounded by the schedule total which is itself far below the limit of the narrower type.
Pending
#3 informational Issue
The named live supply error is unreachable on the intended funding path
The response states that the live supply comparison is raised as its own named error at the seal before the transfer decision, so that a shortfall is discovered by name rather than as a generic funding failure. Within the seal helper that ordering is correct. On the atomic funding path, which the runbook names as the only supported route, the transfer runs before the helper is called, so the comparison is never reached: if the caller cannot cover the committed total the token's own insufficient balance error fires first, and if it can then the contract's balance is part of live supply and the comparison can no longer be true. Measured on the pinned commit: after a one wei burn the atomic call reverts with the token's error and the plain seal call on the same contract reverts with the named one carrying both figures. The named error is therefore reachable and useful, but not on the path an operator is told to use, which is the path the response describes it as protecting.