This is TonUP’s first incident report. During the deployment of the Jetton contract, due to human negligence, we mistakenly configured the decimal for our Jetton contract, resulting in the need for upgrades to all related contracts. We previously mentioned this update in our Telegram Channel here. We are now making this incident report public for everyone’s reference.
Incident date: 21/12/2023
Severity: Critical
Disclosure date: 09/01/2024
At TonUP, we’ve always been committed to maintaining a high level of transparency, especially when it comes to security incidents. We believe that sharing this information is essential in building a safer crypto environment. This incident report represents our latest disclosure and we hope it helps other blockchain projects to avoid similar situations.
Incident Summary
On December 21, 2023, a security incident was identified in TonUP’s IDO contracts, following the successful IDO of UP tokens. A community developer reported to us that the decimal configuration of the UP token was set at 8 decimals, instead of the usual 9 decimals in the Ton ecosystem, resulting in the wrong UP amount distributed by the IDO contracts. At the time of the report the UP tokens have been minted and sent to the IDO contracts, and the IDO was successful. UP claiming wasn’t active yet.
The discrepancy occurred because the UP token was deployed by the Operations team without notifying the R&D team. Consequently, the R&D team assumed 9 decimals for the UP token in their deployment and configuration script, leading to a bug in configuration of the deployed IDO contracts.
Scope & Impact Assessment
Setting a different decimal for a Jetton token is allowed as per the Jetton standard specification. However various dapps/tools may assume 9 decimals. For example toNano('1')
in Typescript and ton("1")
in Tact are used to represent 1 token assuming 9 decimals, which translates to 1_000_000_000, or 10 tokens under 8 decimals. In our case the discrepancy in decimal configuration led to an unintended increase in the UP/TON price setting, giving 10x more UP per TON than we intended. This would have meant that early claimers would receive 10x UP tokens, while those who claim late would receive nothing, effectively leading to a direct loss of user funds. The issue was classified as Critical and was quickly escalated.
Resolution
There was a 12-hour window between the time of report of the issue and the scheduled claim time. Considering the potential future impact of different decimal configurations, the current critical issue, and the fact that no UP has been publicly distributed yet, we decided to redeploy the UP token with nine decimals. We quickly developed a set of new contracts to handle the claiming process. The new contracts were fully tested and passed our internal security audit prior to mainnet deployment. Participant information and amounts were migrated to the new contracts, the new UP tokens deployed and sent to the new claiming contracts, and we replaced the contract addresses on our web front-end.
When claiming opened at the scheduled time, all users were able to claim UP tokens without issues. No user funds were lost.
For all future IDO projects, setting a decimal other than 9 is supported (although still discouraged by us), and we have improved our onboarding process to support that.
Bug Bounty
The incident was classified as Critical with high impact. According to the rules of our bug bounty program, we awarded the reporting party a bug bounty of US$10,000.
Key Takeaways
This incident underscores the need for rigorous testing of not just the smart contract but also the verification of configuration parameters, and the rigorous verification of the state of all smart contracts already deployed with which the smart contract interacts.
Thoughts on the Jetton standard
In the Jetton standard, there’s no on-chain method to get the balance of a Jetton wallet. The reason given was that TON being an async blockchain, by the time the calling smart contract receives the balance, the balance may have already changed. This is not a valid reason in our opinion, because for example in our case not having this method prevents us from verifying the UP token balance on-chain. Had it been possible to get its own UP token balance, the IDO contracts would have known they don’t have enough Jetton tokens and would refuse to start the IDO. In similar situations where the smart contract knows that no outbound transfer can happen between the get balance call and the response, the smart contract can know the minimum amount of Jetton tokens it holds. Unfortunately this is not possible with the current Jetton standard.
We therefore recommend an extension to the Jetton standard (TEP-137), allowing an on-chain method to check the balance of a wallet. This change would prevent similar incidents in the future and allow for more secure and reliable smart contract operations.
Conclusion
We are very thankful for our great community who discovered and reported this issue to us. We are also extremely grateful that this incident was resolved with any impact for our users. We are committed to maintaining the highest standards of security and transparency in all our operations. We will continue to learn from our community and implement the necessary measures to prevent such incidents in the future. We would also like to share our experience with the community so that other projects won’t make the same mistakes. We appreciate your understanding and continued support.
Ken, CTO