As of 30 Oct 2026, Spheron Network has 3 audit reports by 1 security firm on Trustblock; the latest is by QuillAudits, dated 27 Mar 2025. Those reports list no open critical findings.
Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Spheron change review Audit · firm’s term: review | Security firm: QuillAudits | Date: | Scope: Files named in the report | No open critical findings | Open · quillaudits.com |
| Spheron V2 | Security firm: QuillAudits | Date: | Scope: Files named in the report | No open critical findings | Open · quillaudits.com |
| Spheron V1 | Security firm: QuillAudits | Date: | Scope: Files named in the report | No open critical findings | Open · quillaudits.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 0 | 3 |
| Medium | 6 | 0 | 5 |
| Low | 11 | 0 | 3 |
accureInterest() should be called whenever totalDebt is being used because it updates the totalDebt. Now debtOf(agent) uses totalDebt to calculate the debtOf(agent), which means totaltotalDebt should be updated before calling debtOf(agent) in startLiquidation(). ie missing accureIntest()<br><code>solidity function startLiquidation(address agent) external nonReentrant whenNotPaused { //@audit this should also call accureIntest() if (msg.sender != core) revert CallerNotCore(); @> uint256 liquidatingAmount = debtOf(agent); removeDebt(agent, liquidatingAmount); totalLiquidatingAmount += liquidatingAmount; liquidatingAmounts[agent] += liquidatingAmount; invariantCheck(); emit LiquidationStarted(agent, liquidatingAmount); } </code>
Open the reportIn verifySignature, the check if (nonce != core.signatureNonces(agent)) revert InvalidNonce(); was to help prevent replays as the purpose of nonce logic is to prevent signature replay however this nonce is also a user argument, what that means is user specifies this value and not the protocol. The issue is the nonce specified by the user is not included when generating the hash to be verified allowing user to specify a different nonces but on the same signature.
Open the reportIn the bootstrapOffchainCollateral(), you'll notice it checks if (block.timestamp > deadline) revert ExpiredSignature(deadline); for expired signature however also note this deadline is a user argument, the issue here is that the deadline was not included when generating the hash to verify, so even if the signature signed by the core signers is expired, user can just pass in a future time which will always pass since the deadline won't get validated
Open the reportThe below function can be initiated by anyone by passing isFizz = true and _fizzId of anyone to initiate withdrawal without consent.The first if block will just transferEarnedTokens to the rewardWallet. No loss of tokens since the transfer is done to the rewardWallet but withdrawals can be initiated without consent.
Open the reportThe changeAgentOwner() function allows owner to change it's agent owner, it has the isAgentCall modifier what this means is that only the Agent can call this function and not the owner, also meaning the owner would have to call this through the agent contract which is the intended design. However, an issue arises in the corelogic.changeAgentOwner, notice the check if (agentDetail.owner != msg.sender) revert CallerNotAgentOwner(); this means caller must be the owner itself and not the agent, this is a conflict check to the isAgentCall modifier causes the entire function to always revert
Open the reportUser can initiate withdrawRequest on behalf of other user by passing _to, and this will increase the<br>unlockTime of withdrawRequest unlockTime is checked in withdraw().<br><code>solidity function withdrawRequest(uint256 _amount, address _to) external whenNotPaused { require(_to != address(0), "Invalid recipient"); require(_amount > 0, "Invalid amount"); WithdrawRequest storage request = withdrawRequests[_to]; pToken.transferFrom(msg.sender, address(this), _amount); uint256 shares = pToken.convertToShares(_amount); request.shares += shares; //@audit this can be used to grief other users by initiating a withdrawRequest and below line will increase the unlockTime by vestingTime @> request.unlockTime = block.timestamp + vestingTime; emit WithdrawRequested(_to, shares); } function withdraw() external whenNotPaused { address _to = msg.sender; WithdrawRequest storage request = withdrawRequests[_to]; require(request.shares > 0, "No withdraw request found"); @> require(block.timestamp >= request.unlockTime, "Vesting time not reached"); uint256 amount = pToken.convertToAssets(request.shares); delete withdrawRequests[_to]; pToken.approve(address(delegationPool), amount); delegationPool.withdraw(amount, _to); emit Withdraw(_to, amount); } </code>
Open the reportDelegationPool.sol.updateFee() doesn't accrue old fee before updating it to the new one<br><code>solidity function updateFeeRate(uint256 _newFeeRate) external onlyOwner { feeRate = _newFeeRate; emit FeeRateUpdated(_newFeeRate); } </code><br>as the updateFeeRate function doesn't accrue fees before the update, there will be problem in distributeLiquidationSurplus and accrueInterest functions as both distribute pTokens w.r.t fee. Although<br>distributeLiquidationSurplus won't be affected much since it is only called when liquidation is called but accrued interest will be affected since it is dependent on the fee.<br><code>solidity function distributeLiquidationSurplus(uint256 amount) internal { uint256 fee = (amount * feeRate) / 10_000; pToken.mint(feeReceiver, fee); pToken.distribute(amount - fee); } function accrueInterest() public { uint256 timeElapsed = block.timestamp - lastAccrueTimeStamp; if (timeElapsed == 0) return; uint256 totalInterestIncrement = ( interestRateModel.getBorrowRate(pToken.totalSupply(), totalDebt + totalLiquidatingAmount) * totalDebt * timeElapsed ) / SECONDS_PER_YEAR / 1e18; totalDebt += totalInterestIncrement; uint256 fee = (totalInterestIncrement * feeRate) / 10_000; lastAccrueTimeStamp = block.timestamp; pToken.mint(feeReceiver, fee); pToken.distribute(totalInterestIncrement - fee);//@audit if fee is increased then less pTokens will be distributed here invariantCheck(); } </code><br>The correct way would be to collect the fee first and then update the fee, or call the accrueInterest()<br>before updating the fee.<br>
Open the reportThere should be slippage protection for users otherwise while withdrawing they could lose significant amount if exchange rate is high/low<br><br><code>solidity function withdrawRequest(uint256 _amount, address _to) external whenNotPaused { require(_to != address(0), "Invalid recipient"); require(_amount > 0, "Invalid amount"); WithdrawRequest storage request = withdrawRequests[_to]; pToken.transferFrom(msg.sender, address(this), _amount); @> uint256 shares = pToken.convertToShares(_amount); request.shares += shares; request.unlockTime = block.timestamp + vestingTime; emit WithdrawRequested(_to, shares); } function withdraw() external whenNotPaused { address _to = msg.sender; WithdrawRequest storage request = withdrawRequests[_to]; require(request.shares > 0, "No withdraw request found"); require(block.timestamp >= request.unlockTime, "Vesting time not reached"); uint256 amount = pToken.convertToAssets(request.shares); delete withdrawRequests[_to]; pToken.approve(address(delegationPool), amount); delegationPool.withdraw(amount, _to); emit Withdraw(_to, amount); } </code><br>
Open the reportIn AirdropPool.sol the constructor sets the claimDeadline as claimDeadline = _launchTime + (8 * 30 days); which is hardcoded as 240 days from the launchTime, However the function claimVestingAssets has a check : require(block.timestamp <= claimDeadline, "Claim period has ended"); Which means the claim can only be made when the claimDeadline is not passed but the claim deadline is hardcoded to 240 days in the constructor, and for the periods of more than 240 days vesting schedule users won't be able to claim vesting assets via claimVestingAssets function.<br><code>solidity function claimVestingAssets() external whenNotPaused { UserInfo storage user = claimInfo[msg.sender]; require(user.claimed, "Not claimed"); require(block.timestamp <= claimDeadline, "Claim period has ended"); require(user.totalShares > user.claimedShares, "No shares to claim"); uint256 claimableShares = _claimAfterLockTime(); user.lastClaimTime = block.timestamp; pToken.transfer(msg.sender, pToken.convertToAssets(claimableShares)); emit AirdropClaimed(msg.sender, pToken.convertToAssets() </code>
Open the reportThe repay() function in delegation pool emits wrong event<br><code>diff- emit Repaid(agent, payingAmount, payingAmount); + emit Repaid(agent, tokenAmount, payingAmount); </code>
Open the reportThe check if (getAgentAddress(minerId, _msgSender()) != address(0)) revert AgentAlreadyCreated(); in registerMiner() is implemented wrongly, notice getAgentAddress(minerId, _msgSender()), it passes minerId first then _msgSender() , However this is the getAgentAddress() function: function getAgentAddress(address _owner, address _minerId) public view returns (address) {<@ note owner first then minerId<br>} As we can see the parameters where passed in wrongly leading to wrong value returned<br>
Open the reportThe reports name a repository or files. Open a report to see its scope.
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.
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.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="argoapp"></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