Unpacking Clarity: Stacks' Recipe for Less Hackable, More Transparent, and Ultra-Secure Smart Contracts
This post will show you how Clarity is transforming the Blockchain landscape.
Transparent Source Code
In Solidity, developers write source code that then needs to be compiled to EVM bytecode in order to deploy on Ethereum. This compilation step is where bugs could be introduced or logic obscured.
Only the bytecode actually runs on Ethereum. To understand what the contract does, developers need to share the original Solidity source. (Image: The EVM bytecode can be very difficult to follow)
In Clarity, source code is interpreted directly on the blockchain. There is no compilation step that could hide the logic or introduce bugs.
Guaranteed Halting
Clarity is a decidable language, meaning programs are guaranteed to halt. This avoids potential infinite loops So What is decidable language? Can you give an example? -> Sure
A decidable language in computability theory is one where there exists an algorithm that can take any program in the language and decide whether it halts or runs forever. For Clarity, this means:
There is an algorithm that can analyze any Clarity program and determine if it will halt or not.
This algorithm is used before running the code to ensure it will halt.
If the algorithm determines a Clarity program could run forever, it will reject it.
So a decidable language removes an entire class of bugs and vulnerabilities caused by unbounded loops or recursion.
In contrast, Solidity is not decidable - there is no general way to determine if arbitrary Solidity code halts. Infinite loops are possible
Built-in Reentrancy Prevention
Clarity prevents reentrancy, where calling Contract A → B → A again confuses A's state. Solidity enables this vulnerability So what is a reentrancy attack? Can you give me an example? Sure
Let's imagine a scenario using a real-world example. Let's say there's a cookie jar in your kitchen. This cookie jar has a rule: you can only take one cookie at a time, and you must close the lid before you can take another one.
Imagine you're a clever child and you find a loophole. You realize that if you take a cookie but don't close the lid right away, Mom, who enforces the rules, might get distracted. While she's distracted, you could quickly take another cookie before you close the lid
Here's how it works:
1. You open the lid and take a cookie (just like you're supposed to).
2. But before you close the lid, you ask Mom a tricky question ("Why is the sky blue?") to distract her.
3. While she's thinking about the answer, you quickly take another cookie.
4. Then you close the lid, following the original rule. But you've managed to take two cookies in one turn! In this example, the cookie jar is like a smart contract, and the rule about taking one cookie at a time and closing the lid is like the code in the contract.
The tricky question is like a contract calling another contract, and taking the second cookie before closing the lid is like a reentrancy attack.
Just like Mom didn't expect you to take another cookie while she was distracted, the programmers who write smart contracts might not expect their code to be interrupted and exploited in this way.
That's why it's important to design contracts (or cookie jar rules) carefully to avoid these kinds of loopholes. In Clarity, it would be as if once you open the lid of the jar, time freezes until you close it again.
You wouldn't be able to distract Mom because everything has to wait until the lid is closed. This makes it impossible to take a second cookie before closing the lid, which is how Clarity prevents reentrancy.
Clarity prevents reentrancy very strongly at the language level. It is impossible to write Clarity code that could enable a reentrancy vulnerability. Here are the example codes in Clarity
Contract A has a withdraw function that:
Updates the balance
Calls contract B to transfer tokens
Contract B attempts to call back into contract A's withdraw. This mutual recursion from B->A->B is invalid in Clarity. The compiler would reject contract B.
So there is no way to write a Clarity contract that enables reentrancy like this. The language design fundamentally prevents it. This means developers don't have to worry about securing contracts against reentrancy in Clarity - it's handled at the language level.
Automatic Overflow/Underflow Protection
Clarity handles overflows/underflows automatically at the language level. Solidity requires manual wrapping of each variable. Example below
An overflow happens when a calculation results in a number that is too large to be stored correctly. (code below) (Similarly, an underflow is when a calculation results in a number too small to be stored)
While Solidity has some overflow protection via libraries like SafeMath, it is opt-in per variable. Clarity is always on
Even with SafeMath, Solidity math can still overflow. In Clarity it is impossible no matter what operations you try to do.
Mandatory Error Handling
Clarity requires checking the success or failure of external calls, preventing ignored errors.
In Solidity, contract calls can be made using low-level functions like call or send which don't bubble up failures by default. Developers have to manually check for call success like:
This allows calls to fail silently if the developer doesn't check properly. In Clarity, contract calls must be made using functions like contract-call? which return a response type:
This response must be unwrapped or propagated using control flow functions (image) Failing to handle the response will make the Clarity program invalid.
So while Solidity enables low-level calls that can fail silently, Clarity forces developers to properly handle call failures. This prevents entire classes of errors caused by ignoring failed external calls in Clarity smart contracts.
Conclusion
In summary, Clarity improves smart contract security through:
Transparent source code
Guaranteed halting
Built-in reentrancy prevention
Automatic overflow/underflow protection
Mandatory error handling (try!, unwrap keywords)
These language-level capabilities eliminate entire classes of bugs, errors, and vulnerabilities. Clarity optimizes for correctness. While languages like Solidity enable complex behavior, Clarity chooses to restrict some flexibility in exchange for better security.
Through its design, Clarity prevents problems at the root rather than requiring developers to implement protections manually. Clarity aims to make smart contract programming simpler and safer. Give it a try and let me know what you think!
It's important to understand that while the language and its design choices can mitigate certain risks, it doesn't automatically make every contract written in the language secure. Security also depends on how the contract is coded.
For example, a poorly designed contract might still have other vulnerabilities, like allowing unauthorized users to perform certain actions, or not properly validating inputs.
Therefore, even with Clarity's properties, it's crucial to follow best practices for smart contract development, which include thorough testing, code audits, and careful design to minimize the potential for bugs and vulnerabilities.
That's a wrap on our deep dive into Clarity, Stacks' secret weapon for building transparent, secure, and predictable smart contracts. But the journey doesn't stop here! Follow me for more insights into the innovative world of the Stacks ecosystem