Concordance: Automatically Simplifying Smart Contracts
Making complex smart contracts easier to read.
Author:
As the Web3 space has matured, smart contract developers have likewise grown more and more sophisticated, writing heavily optimized code, often in the low-level assembly language available in Solidity. While this approach means faster (and cheaper) smart contract execution, these sophisticated coding techniques make auditing and code reviews more difficult.
That’s why we’re excited to announce the release of a new open-source tool named Concordance that can help bring greater understanding to deeply complex code. Concordance automatically simplifies complex smart contract implementations using LLMs without changing the observable on-chain behavior of the code. We are able to guarantee that the behavior doesn’t change thanks to our equivalence checker Concord.
To explain why we wrote Concordance and how it can be useful, let’s start by looking at an example function taken from the Solady library:
The name suggests that this is performing a “safe” `transferFrom` operation, but how it’s doing so is not immediately clear and the revert condition borders on inscrutable. It’s quite common to find hand-optimized code like this in Web3 applications outside of the Solady library. Auditing or simply reviewing heavily optimized code like this example is extremely difficult and error prone.
What if we could review an equivalent but simpler version?
Concordance will iteratively simplify our Solady example to:
Here the assembly code that builds the call to `token` has been replaced by the use of Solidity’s built ABI encoding APIs. The revert logic has also been significantly clarified. We can now see that the function reverts if:
This implementation behaves the same on all possible parameters to safeTransferFrom and all possible behaviors of the called token (e.g., the token reverts, or returns a malformed buffer or is an EOA, etc.).
Notice that Concordance arrived at this result “iteratively.” Concordance first uses an LLM to propose an equivalent rewrite to the provided code. In our example, this was the Solady safeTransferFrom. However, as we just discussed, the original function is quite complex, and so this initial proposal from the LLM may have some subtle differences in behavior. To ensure the rewrite is equivalent, Concordance always compares the proposal from the LLM to the original code using an “equivalence checker” called Concord.
This equivalence checker will either produce a mathematical proof that the two pieces of code behave the same on all possible inputs, or an example input where the behavior diverges. In the latter case, this example found by Concord is fed back into the LLM with instructions to correct the proposed rewrite. This loop iterates until Concord, the equivalence checker, judges the proposed rewrite equivalent to the original code.
By using Concord to check the proposals from the LLM, the rewrite returned to the user is necessarily equivalent to the original code in every observable way. Concord is thus the foundation on which Concordance rests.
Concord is the name of our general purpose equivalence checker, a tool that can automatically (and exhaustively) check whether two smart contracts are equivalent. We’ve mentioned this tool before when discussing the Vyper compiler bug we found using a prototype of Concord. In that writeup we didn’t get into the details of what it means for two programs to be equivalent, but we’ll address that here.
We’ve said that the resulting code is equivalent in every observable way. This distinction is important: if we insisted that the Ethereum Virtual Machine’s memory and stack always looked exactly the same when running two different programs, that would leave little room for any of the changes necessary for the rewrite above. Instead, we insist that the observable side effects of the code must be the same. These side effects are:
The data associated with these side effects (the reason for a revert, the topics of an emitted log) must also be the same between two programs. More technically, two equivalent implementations A and B will satisfy the following properties:
If both implementations revert then any side effects they performed are rolled back. However, when the implementations return, we insist the effects they had on the state are the same. In particular, if A and B return without exception then they must both make the same sequence of external calls, to the same addresses, with the same ether value and calldatas. They must also emit the same logs, with the same topics and data, and the values in every storage slot must be equal.
Gas consumption is not part of our definition of equivalence. Indeed, unless two EVM executions execute exactly the same opcode with the exact same data, the chances that gas consumption is equivalent is incredibly small. Further, when simplifying code, it is quite likely that the simplified version will use more gas–this is the necessary tradeoff made for undoing optimization.
With this rough definition of equivalence, a natural question arises: how can we ensure the above holds for two implementations A and B on all possible inputs? As we mentioned in our Vyper blog post, Concord is built on the symbolic reasoning used by the Certora Prover, which lets it reason about effectively infinite state spaces, covering all possible inputs.
How Concord achieves this within the framework of the Certora Prover is itself the topic of several blog posts. However, to give a quick overview: Concord instruments the two implementations it is comparing to record all the side effects encountered on any path of execution into two different vectors. After symbolically executing both implementations, we assert that the two vectors are equivalent; if they are not, then there is some difference in behavior between the two implementations. Concord analyzes the vectors to provide a human readable explanation of the different behavior: it is this explanation that is returned back to the LLMs by Concordance. A much more detailed explanation of this technique, and a longer discussion around defining equivalence can be found in our technical report on Concord here.
To reiterate, Concord is a standalone tool that can be used to check the difference between two smart contract implementations. To better understand how Concordance builds upon it, let’s examine a single round of interaction between the LLM and Concord, mediated by Concordance.
The LLM first proposes the following rewrite for the Solady safeTransferFrom:
This is quite close, but Concord finds a counter example:
Interpreting the low-level results here, we can see that Concord is considering a case where the `safeTransferFrom` function being called returns an enormous 4294967295
byte buffer. The original code reverts with 7939f424
(AKA TransferFromFailed()) whereas the proposed rewrite reverts with an empty buffer. The enormous buffer size is not the source of divergent behavior. Instead, notice that the first word in this buffer is 0x0…02. This is not a valid encoding of a boolean, and thus the call to `abi.decode` in the proposed rewrite will revert with an empty buffer. The above counter-example is sent back to Claude for refinement, and it eventually converges to a correct rewrite.
Concordance is a Python script that is included in the open-source Certora repository. You can find the README describing how to install dependencies and run the tool here. As mentioned, Concordance is an open source tool you can try today, although we stress it is very much in pre-alpha development. Of course, we welcome any feedback you may have if you try it out yourself.
As LLMs get more powerful, we can apply them to more and more complex software engineering tasks. However, as has been documented widely, LLMs are not perfect and can make subtle (and sometimes not so subtle) mistakes. By using formal verification tools as guardrails for LLMs, we can benefit from the automation and ease of AI while still getting the bulletproof assurance of formal verification. We’re also exploring other possibilities in this same vein which we’ll hopefully be able to share soon. Stay tuned!