Enhancing Uniswap: A Seamless Transition to StarkNet with UniStark
Written on
By Jorik Schellekens
Acknowledgments to Guy McComb, Greg Vardy, Swapnil Raj, Carmen Irene Cabrera Rodriguez, Rodrigo Pino, Anneli Nowak, and Carla Segvic for their valuable insights and suggestions.
We are thrilled to share that we've reached a significant milestone in the Warp project: the successful transpilation and compilation of Uniswap v3! Additionally, we are close to finalizing a Hardhat plugin that will enable users to execute all Solidity hardhat tests using the transpiled Cairo language.
In the accompanying video, we demonstrate the original SqrtPriceMath.spec.ts from the Uniswap v3 hardhat test suite. The Warp plugin for Hardhat automatically runs these tests on the transpiled Cairo version. Although the plugin is still in development, we will soon have the entire Uniswap test suite operational with a Uniswap implementation deployed on StarkNet.
From the beginning, Warp has aimed to facilitate the transpilation of existing Solidity codebases into Cairo for deployment on StarkNet. With recent advancements in StarkNet, such as contract creation from other contracts, Warp is now poised to tackle one of Solidity’s most intricate projects: Uniswap v3.
Don’t just take our word for it; visit our Uniswap fork, update to the latest version of Warp, and give it a try!
warp transpile --compile-cairo contracts/UniswapV3Pool.sol
Warp successfully transpiles and deploys every Solidity file from the Uniswap v3 repository with minimal source changes.
Significant effort has gone into accurately compiling such a large-scale project, and the results are promising. As Warp continues to mature, the barriers for both large and small projects to explore StarkNet capabilities are diminishing.
Warp isn't stopping at Uniswap! We plan to keep enhancing features and replicate this process with additional protocols, bringing new solutions to StarkNet at an impressive pace. If you’re interested in bringing your project to StarkNet, please get in touch!
This transpilation effort has helped us identify and resolve several issues with Warp's functionality and has guided our priorities for new features. We are also motivated to integrate Warp with Hardhat, with further updates coming soon.
As a result of these developments, Warp now supports the following features:
- abi.encode, abi.encodePacked - Be cautious! Addresses are packed in 32 bytes since StarkNet addresses are 251 bits.
- abi.decode - Currently, only value types are supported.
Adjustments to Uniswap's Solidity Contracts
To make Uniswap function correctly, we made minor adjustments to the source code. Some Solidity features remain unsupported due to fundamental differences between Ethereum and StarkNet, necessitating developer intervention. For other features, support is under development.
Before diving deeper, here is a brief overview of the changes made, ranked by significance/difficulty (from most to least challenging):
- Upgrade the Solidity version from 0.7.6 to 0.8.14.
- Replace inline assembly with equivalent Solidity or Inline Cairo.
- Substitute low-level calls with calls to the appropriate interface.
- Eliminate indexed arguments in Events.
We will explore these changes, their necessity, and what Warp can achieve in the future.
Upgrading the Solidity Version
Uniswap's contracts primarily utilize 0.7.6, while Warp fully supports Solidity 0.8.*. To leverage all benefits of Warp, we updated the pragma in all contracts to 0.8.14. However, this is not a trivial task; the version change introduces semantic differences that must be addressed.
For instance, arithmetic operations in 0.7.* are unchecked, meaning they can overflow or underflow without raising errors. Conversely, in 0.8.*, all arithmetic is checked, causing overflows and underflows to trigger errors. To accommodate this, we wrapped arithmetic operations in unchecked blocks.
There are subtle differences in type conversions for operators, leading to minor changes in implicit conversions. You will notice these adjustments throughout the repository.
The contracts UniswapV3Pool.sol and TransferHelper.sol had low-level calls that were replaced with high-level cross-contract calls through interfaces.
We are currently working on supporting 0.7.*, but this is not a priority for Warp at this moment.
Substituting Inline Assembly
Uniswap's libraries: FullMath.sol, TickMath.sol, and UnsafeMath.sol, utilize YUL assembly for gas efficiency. Warp does not support YUL assembly blocks, as many assembly instructions rely on specific memory and calldata layouts in the EVM—assumptions that do not hold in the Cairo transpilation. Thus, we have removed assembly support.
Replacing the YUL code was a straightforward task of rewriting the assembly in Solidity. For instance, in contracts/libraries/FullMath.sol, the following assembly:
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
was transformed into:
unchecked {
prod0 = a * b;
uint256 mm = mulmod(a, b, 2**256 - 1);
uint256 prod1;
prod1 = mm - prod0;
if (mm < prod0) prod1 -= 1;
}
We demonstrate the replacement of each YUL block in FullMath, though a single simple Cairo function could have replaced the entire FullMath library.
While replacing assembly with Solidity may have reduced the contracts' gas efficiency, StarkNet's promise of lower gas costs should mitigate this concern for contracts operating on its platform.
For features in YUL that cannot be expressed as simple Solidity arithmetic, Warp supports inline Cairo blocks:
contract WARP {
// / warp-cairo
// from starkware.starknet.common.syscalls import library_call
// func __default_{
// syscall_ptr : felt*,
// pedersen_ptr : HashBuiltin*,
// range_check_ptr,
// }(selector : felt, calldata_size : felt, calldata : felt*) -> (
// retdata_size : felt, retdata : felt*
// ){
// alloc_locals;
// let (class_hash_low) = WARP_STORAGE.read(STATEVAR(implementation_hash));
// let (class_hash_high) = WARP_STORAGE.read(STATEVAR(implementation_hash) + 1);
// let class_hash = class_hash_low + 2**128 * class_hash_high;
// let (retdata_size : felt, retdata : felt*) = library_call(
// class_hash=class_hash,
// function_selector=selector,
// calldata_size=calldata_size,
// calldata=calldata,
// );
// return (retdata_size=retdata_size, retdata=retdata);
// }
fallback() external {
}
}
Following this effort, we have prioritized supporting a subset of YUL for arithmetic operations.
Replacing Low-Level Calls
Warp does not maintain the same calldata semantics as Solidity, rendering low-level calls ineffective for transpilation. The solution involves reintroducing that semantic information and substituting the call with an appropriately bound interface call.
Indexed Events
This change is simple. Warp currently does not support indexed events, but that feature is on the way! In the meantime, apply s/indexed//g.
Ternaries
We are in the process of testing ternaries in Warp, with an expected release next week. In the meantime, we are replacing the ternaries in Uniswap v3 with equivalent if statements.
Summary of Changes
Overall, the modifications made to the contracts were straightforward, requiring only a solid understanding of Solidity. You can review all the changes we implemented here.
Uniswap Tests
Compiling and deploying the project is just the beginning; we need to ensure our modifications are accurate. Warp already includes an extensive set of semantic tests for solc's repository, but we also want to incorporate Uniswap's tests. To achieve this, we are meticulously adjusting the interface between ethers.js, Waffle, and Hardhat to enable the entire Uniswap test suite to run seamlessly. Our objective is to execute Uniswap's tests with minimal changes, ensuring that inputs, outputs, matchers, and fixtures remain consistent. We facilitate the automatic translation of inputs and outputs between the Solidity ABI and Cairo ABI.
We have initiated testing using this strategy and will share results in the coming days, along with step counts (StarkNet’s equivalent measure of computation) and benchmarking data for analysis.
What’s Next?
Once the test suite is complete, we will publish a blog post detailing the test results, benchmarks, and a UniStark testnet deployment address. We will also provide implementation insights for the Hardhat integration along with instructions for replication in your own projects. More technical articles will follow, explaining how Warp addresses complex transpilation challenges such as modifiers, inheritance, and memory/storage copy semantics. Stay connected with us on Twitter to ensure you don’t miss out!
At Nethermind, we have numerous exciting projects, and we are always on the lookout for passionate individuals to join our team. Our work spans the entire Web3 ecosystem, from the Nethermind node to foundational cryptography research and application-layer protocol development. If you have an interest in compilers, the Warp team would love to hear from you. Check out our job board: https://nethermind.io/company/.
Please note, this is a Nethermind initiative conducted independently of Uniswap. All development and testing described in this article comply with the Uniswap Business Source License 1.1 and other relevant licenses. Nethermind does not utilize Uniswap v3 Core for revenue-generating activities.