Skip to content

fix: check held balance in rescue operation#1490

Merged
jaime-iobermudez merged 3 commits into
developfrom
fix/rescue-amount
Jun 17, 2026
Merged

fix: check held balance in rescue operation#1490
jaime-iobermudez merged 3 commits into
developfrom
fix/rescue-amount

Conversation

@jaime-iobermudez

Copy link
Copy Markdown
Contributor

Summary
Fixes a Medium-severity vulnerability (reported by the security audit) where the rescue operation could drain stablecoin tokens held in escrow on behalf of token holders.

When a hold is created, the holder's tokens are wiped and an equivalent amount is minted into the stablecoin contract (the treasury) as escrow, tracked by totalHeldAmount. The rescue function only checked the contract's raw token balance and did not subtract the held obligations, so an account with the _RESCUE_ROLE could transfer out the escrow backing active holds — leaving hold records without backing tokens and causing executeHold / releaseHold / reclaimHold to fail.

This aligns rescue with the existing burn logic, which already excludes held amounts via getBurnableAmount().

Changes
Contracts

RescuableFacet.sol: added getRescuableAmount() (balanceOf(contract) − totalHeldAmount) and a checkRescueAmount modifier; rescue() now validates against the rescuable (unreserved) amount instead of the raw contract balance. Registered the new selector.
IRescuable.sol: added the getRescuableAmount() signature and the RescuableAmountExceeded(int64 rescuableAmount) error.
SDK (mirrors the existing burn flow)

New GetRescuableAmountQuery + handler and RescuableAmountExceeded domain error (ErrorCode 20017).
RPCQueryAdapter.getRescuableAmount() calling the new contract view.
ValidationService.checkRescuableAmount() and a pre-flight check in RescueCommandHandler (kept the existing treasury-balance check as well).
Registered the query handler in Injectable.
Tests
hold.test.ts: new "Rescue when held amount greater than 0" block (mirrors the existing burn-with-hold test) — rescuing the full balance with an active hold reverts with RescuableAmountExceeded; rescuing only the unreserved amount succeeds and leaves the escrow intact.
rescuable.test.ts: updated the over-balance case to assert the new RescuableAmountExceeded error.
jest-setup-file.ts: mocked getRescuableAmount.

Signed-off-by: jaime-iobermudez <jaime.bermudez@io.builders>
@jaime-iobermudez jaime-iobermudez self-assigned this Jun 17, 2026
@jaime-iobermudez jaime-iobermudez requested review from a team as code owners June 17, 2026 08:05
@swirlds-automation

swirlds-automation commented Jun 17, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Comment thread packages/contracts/contracts/extensions/RescuableFacet.sol Outdated
Comment thread packages/contracts/contracts/extensions/RescuableFacet.sol
Comment thread packages/contracts/contracts/extensions/RescuableFacet.sol
Comment thread packages/contracts/contracts/extensions/RescuableFacet.sol
Comment thread packages/contracts/contracts/extensions/RescuableFacet.sol
Signed-off-by: jaime-iobermudez <jaime.bermudez@io.builders>
Signed-off-by: jaime-iobermudez <jaime.bermudez@io.builders>
@sonarqubecloud

Copy link
Copy Markdown

❌ The last analysis has failed.

See analysis details on SonarQube Cloud

@jaime-iobermudez jaime-iobermudez merged commit 4abaf54 into develop Jun 17, 2026
14 of 15 checks passed
@jaime-iobermudez jaime-iobermudez deleted the fix/rescue-amount branch June 17, 2026 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants