Intro
On November 27th we announced in our Telegram channel that our launch plans for December 4th were postponed. Postponing was not a decision we took lightly. In this blogpost we’re providing an update on the reason why we delayed the launch and what happens next.
A Smart Contract Bug
The reason we immediately called off the original launch plans was that a logic error in the smart contracts was discovered. The bug was specifically in the redemption logic. The contract code in question was not present in the first phase of our smart contract audit, but was introduced in the remediation between the initial audit results and the second phase of the audit.
The initial audit report did find important items, which is why some code was changed in the remediation, but this new issue introduced in the redemption system slipped through the cracks of the 2nd phase where the remediations were verified to be fully sound.
Multi-Layered Review
In addition to our main contract audit, we’ve worked with contract reviewer Sayoshi Nakamario to verify the security of the ParityUSD contract design. It is with this final review of the changes made during the remediation period that the new bug in the redemption logic was uncovered.
When we verified the reported finding of the contract bug, we immediately called off the launch so we could deeply investigate how this slipped through, and how we would move forward to improve our processes and re-audit the contracts with the needed fix.
It’s because of this second line of defense that the contract bug was found. In the next section we’ll expand on some of the new things we are doing to deepen our review and security analysis of the smart contracts.
AI Smart Contracts Reviews
While experimenting with the newly released Claude Opus 4.5 model for contract review, we noticed promising results. This led us to realize that contract reviews should be AI-powered.
Over the past two weeks we have generated 10+ AI smart contract reviews internally. We ran many reviews because the models are not deterministic and can be prompted in a multitude of different ways. We first gave the models the needed CashScript documentation as background context and went further to create dedicated expert sub-agents for CashScript and ParityUSD which the AI reviewer had access to.
The AI contract reviews we ran were very helpful in highlighting areas which were lacking in code comments or had confusing variables. We improved the code comments and variable naming in a way that would have prevented the bug in the first place. We revisited all contracts in a similar way. Lastly we found one aspect of economic incentives of the stability pool which we still wanted to change.
What Happens Next
Uncovering a contract bug before launch is something we take very seriously and we’re glad this was caught pre-launch. The end result is a more robust, better-documented, and thoroughly reviewed smart contract system.
Our revised smart contract code is now frozen and the third round of audits is underway. Two audit reports will be publicized, one from Sighash Labs and one from Lifestone Labs by Sayoshi.
Once the contract re-audits are completed successfully we’ll be posting both audit reports as well as announce a new launch timeline! This is our final blogpost for 2025, we’ll be back in January with these new updates and want to wish everyone happy holidays and a great new year!
Addendum: Bug Details
The logic error in the smart contracts was a wrong update of newBorrowAmount in the case of a cancelled redemption in the following code from the Redeem.cash contract:
int newAmountBeingRedeemed = int(amountBeingRedeemed) - redemptionAmount;
int newBorrowAmount = int(borrowedAmountBytes) - redemptionAmount;
bytes27 newLoanCommitment = 0x01 + bytes6(newBorrowAmount) + bytes6(newAmountBeingRedeemed) + bytes14(lastPartLoanState); Instead of redemptionAmount, the variable redemptionAmountToFinalize should have been used to correctly account for the case of cancellation. You can see the fix for the redemption bug here in the public contracts repo.
The nature of the bug is such that the variable naming and minimal code comments in that section likely contributed to the mistake being unnoticed. The week following the discovery of the contract bug we have spent a lot of time improving the code comments, variable naming and general documentation to improve on the auditability of the contracts.
The result of this work can be seen in the public contracts repo. That snippet of Redeem.cash now looks like this:
// Construct loan state after redemption (update debt and amountBeingRedeemed)
// the new state for amountBeingRedeemed is reduced by the pendingRedemptionAmount
int newAmountBeingRedeemed = int(amountBeingRedeemed) - pendingRedemptionAmount;
// the state for the new borrowedAmount is reduced by the effectiveRedemptionAmount
// thus if the redemption is cancelled, the borrowedAmount remains unchanged
int newBorrowAmount = int(borrowedAmountBytes) - effectiveRedemptionAmount;
bytes27 newLoanCommitment = 0x01 + bytes6(newBorrowAmount) + bytes6(newAmountBeingRedeemed) + bytes14(lastPartLoanState); Join the Conversation
Stay informed about our progress and dive deeper into the details of the ParityUSD project by following our updates. We invite you to be part of the discussion and join the community.
- Follow ParityUSD on X: x.com/ParityUSD
- Join the ParityUSD group on Telegram: t.me/parityUSD
In our Telegram group, you’ll have the opportunity to engage directly with the team - ask questions, share your thoughts, and be part of the ongoing conversation as we build ParityUSD together!
