
Leo vs Noir vs Solidity: A ZK developer's guide
This article compares three key programming languages in the decentralized and privacy-focused technology space: Leo, for private applications on the Aleo blockchain; Noir, a versatile language for zero-knowledge (ZK) circuit development (notably for Aztec but also backend-agnostic); and Solidity, the leading language for smart contracts on Ethereum and EVM-compatible chains. We will examine their design, syntax, ecosystems, and security considerations.
Overview
Leo
Leo is an open-source, statically typed, functional programming language developed by Provable for building private decentralized applications on the Aleo blockchain. It features a Rust-inspired syntax with JavaScript-like ergonomics, providing a clean and approachable developer experience while abstracting away the complexities of zero-knowledge proof construction. Leo compiles to Aleo Instructions, which runs on snarkVM, a zero-knowledge virtual machine purpose-built for privacy-preserving computation.
The language includes built-in support for defining both public and private functions, working with constraint systems, and enabling iterative computations — all within a framework designed to make zero-knowledge development more intuitive and efficient. With a strong focus on formal verification, Leo emphasizes correctness and security at the language level, giving developers confidence in their applications. By combining high-level expressiveness with native zero-knowledge integration, Leo offers a powerful foundation for creating secure, private, and scalable blockchain applications.
Noir
Noir is an open-source, Rust-like domain-specific language (DSL) developed by Aztec Labs for writing zero-knowledge (ZK) circuits. It aims to become backend agnostic and developer-centric. Noir compiles code into an Abstract Circuit Intermediate Representation (ACIR), which is then executed by the ACIR Virtual Machine (ACVM). This architecture decouples Noir from any specific ZK proving system, enabling compatibility with various backends.
It supports unconstrained functions and iterative proofs, allowing computations to be performed outside the circuit when necessary. Noir can be used either within the Aztec Network or for general-purpose ZK development. When integrated with Aztec, Noir leverages Aztec.nr framework to add abstractions for managing private and public state and facilitates smart contract interactions in a privacy-preserving manner.
Solidity
Solidity is a high-level, statically typed programming language specifically designed for writing smart contracts on the Ethereum blockchain. Influenced by JavaScript, Python, and C++, Solidity is the primary language used to define contract logic, manage state, and facilitate interactions on the Ethereum Virtual Machine (EVM). It enables developers to write decentralized applications (dApps) that can handle complex logic, manage assets, and enforce rules without relying on centralized systems. Solidity contracts can define custom data structures, manage permissions, and emit events for off-chain tracking.
However, privacy is not a native feature of Solidity or the EVM; all contract data and interactions are publicly visible on the blockchain. This transparency, while beneficial for auditability, poses significant limitations for applications requiring confidentiality — such as private financial transactions or sensitive user data. Developers must rely on external solutions to introduce privacy features into Solidity-based applications. As such, while Solidity is foundational for open, trustless systems, it is not inherently designed for privacy-preserving use cases.
Comparative Analysis: Design, Syntax, and Paradigms
Language Design and Philosophy
Leo & Noir (ZK-Native) is designed to simplify ZK proof development by abstracting cryptographic complexities. Privacy is a core consideration. While Noir's backend-agnosticism offers wider ZK applicability. Leo is designed to abstract the complexity of writing ZK circuits, making it easier to develop zero-knowledge applications without deep cryptographic expertise.
Solidity (Public by Default) is designed for transparent, auditable smart contracts on public blockchains. Privacy is an add-on via L2s or external ZK integrations.
The primary paradigm difference is that Leo and Noir developers work within the constraints of ZK circuits (requiring compile-time known loop bounds, careful state handling), while Solidity developers manage public state within the EVM's gas-metered environment.
Syntax and Common Constructs
All three are statically-typed. Leo and Noir have a Rust-like feel, while Solidity is closer to JavaScript/C++.

Type Systems and Data Management
All three languages employ static typing, a deliberate choice for environments where code correctness and predictability are paramount, especially when managing valuable assets or sensitive data. This allows for errors to be caught at compile-time, reducing the likelihood of runtime failures that could have significant consequences on a blockchain.
Primitive Data Types
Leo offers a rich set of primitives including bool, signed/unsigned integers (i8 through i128, u8 through u128), and types crucial for ZK cryptography: field (elements of the base field of the elliptic curve), group (elliptic curve points), and scalar (elements of the scalar field). It also includes address for Aleo blockchain addresses and signature for cryptographic signatures.
Noir's core numeric type is Field, representing the native field element of the chosen proving backend. It provides abstractions for unsigned integers (e.g., u8, u64, u128) over these field elements, along with bool and fixed-length strings declared as str<N>.
Solidity features bool, signed/unsigned integers of various sizes (e.g., int256, uint256), address (with a payable variant), fixed-size byte arrays (bytes1 to bytes32), dynamically-sized bytes and string, and fixed-point numbers (fixed/ufixed).
Complex Data Types
Structs, arrays, and tuples are common across all three.
Leo: structs as user-defined custom data types. records are a special kind of struct fundamental to Aleo's privacy model, representing private data that must include an owner: address field. mappings provide key-value storage on-chain for public data. Arrays are fixed-size.
Noir: Supports structs, fixed-size arrays, and tuples. Its standard library extends these with more dynamic collections like BoundedVec (dynamically sized vector with a compile-time maximum capacity) and HashMap.
Solidity: structs allow for custom data types. mappings are the primary way to implement associative arrays or dictionaries for on-chain storage. Arrays can be fixed-size or dynamic. enums allow for user-defined enumerated types.
State Management
The approach to state management is a critical differentiator, deeply tied to each language's target blockchain architecture and privacy model.
Leo implements a hybrid state model. Public, on-chain state is managed using mapping data structures. Private state is encapsulated within records, which are handled off-chain in a manner analogous to the UTXO (Unspent Transaction Output) model. transition functions consume existing records and produce new records off-chain, with ZK proofs attesting to the validity of these state changes. In Leo, the asynchronous programming model is designed to facilitate the management of public on-chain state through async functions and async transition functions. Async functions encapsulate logic that modifies public mappings and return a Future object, representing deferred execution until the associated zero-knowledge proof is verified. These functions cannot be invoked directly; instead, they must be called from async transition functions, which initiate the off-chain proof generation process and return the corresponding Future. This structure ensures atomicity and consistency in state updates. Additionally, Leo supports composing multiple asynchronous operations using the await keyword within async functions, allowing for complex workflows involving external program calls. It's important to note that while public state is managed within async functions, private state operations involving records are handled within transition or async transition functions, maintaining the confidentiality of sensitive data.
Noir is a stateless language, as it is used mainly to define ZK circuits. Circuits specify both computations and (linear) constraints, but do not explicitly handle persistent state. State-management is generally the responsibility of the environment that uses the Noir program. For example, if employed in the Aztec Network, then Aztec.nr framework helps managing private and public state inside of Aztec contracts. If Noir is applied to produce a verifier for an EVM chain, the Solidity contract will handle any necessary on-chain state. This design gives Noir flexibility but places the onus of state architecture on the developer or the integrating platform.
Solidity employs an account-based state model. State variables declared within a contract are persistently stored on the Ethereum blockchain within that contract's dedicated storage space. All state is public by default and all state transitions are transparently recorded on the blockchain. This model is well-understood and widely adopted but offers no native privacy for state variables.
Development Ecosystem and Tooling Landscape
The maturity and comprehensiveness of the development ecosystem—including compilers, IDE support, testing frameworks, and libraries—are critical factors for developer productivity and the overall success of a programming language.

Performance and Efficiency
Leo & Noir (ZK Languages): Performance involves off-chain proof generation (CPU/memory intensive), proof size, and on-chain proof verification cost. Optimizations focus on minimizing circuit constraints (e.g., careful loop design, unconstrained functions in Noir, efficient arithmetic).
Solidity: Performance is about EVM gas consumption. Optimizations involve minimizing state writes, use efficient data types, and optimize loops. solc --optimize helps.
The ZK paradigm requires managing off-chain proving resources and on-chain verification costs, distinct from Solidity's gas-only model.
Developer Experience and Learning Curve
When comparing Leo, Noir, and Solidity from a developer experience and learning curve perspective, each language offers unique advantages and challenges depending on the developer’s background.
Leo presents a syntax that blends Rust-like structure with JavaScript influences, resulting in a TypeScript/Rust hybrid feel. It abstracts many of Rust’s complexities, such as ownership and mutability, making it more approachable. Developer feedback generally indicates that Leo is easier to learn and use than Noir, particularly for zero-knowledge (ZK) application development. However, the primary learning challenge lies in understanding Aleo’s privacy model—centered around records and transitions—and grasping the foundational concepts of zero-knowledge proofs.
Noir, while also Rust-like in syntax, introduces a steeper learning curve. It demands a more in-depth understanding of ZK circuit design, constraint systems, and the underlying ACIR/ACVM abstraction models. Although Noir provides helpful abstractions, its complexity can be a hurdle for those new to the zero-knowledge paradigm.
Solidity, in contrast, offers a syntax reminiscent of JavaScript and C++, making it more immediately accessible to a wide range of developers. The learning curve here is less about syntax and more about mastering the Ethereum Virtual Machine (EVM), understanding gas optimization, and navigating common security pitfalls. Solidity benefits from a vast ecosystem of tools, documentation, and a large developer community, which helps ease the onboarding process.
For Rust developers, the transition to Leo or Noir tends to be smoother due to the familiarity of the syntax. Leo often serves as an easier entry point into ZK development, while Noir requires deeper conceptual understanding. Transitioning to Solidity, however, involves adjusting to its object-oriented style and the specifics of the EVM model.
JavaScript or Python developers typically find Solidity more familiar. Leo can also be relatively accessible, acting as a gentler introduction to ZK concepts compared to Noir. The main challenges for these developers are adapting to the Rust-like syntax of Leo and Noir, as well as understanding the fundamentals of the zero-knowledge paradigm.
In summary, the choice of language significantly affects the onboarding experience for ZK and smart contract development. Leo lowers the barrier to entry into ZK, Noir offers powerful but complex abstractions, and Solidity provides a well-supported environment with more familiar syntax for many developers.
Conclusion
The ZK DSL landscape is dynamic. Success hinges on community building, comprehensive libraries, and excellent developer experience. Languages like Leo and Noir, by abstracting complexity and offering familiar syntax, are crucial for broader ZK adoption. Their different approaches, Leo's integrated platform focus versus Noir's backend agnosticism address varied needs within the growing ZK ecosystem, while Solidity remains the primary interface for public blockchain interaction.
With thanks to contributors Oğuz Utku YILDIZ @kyatzu.
About Aleo
Our blog features the stories of developer and privacy advocates building a better internet with zero knowledge.
For further information contact us at hello@aleo.org