Why Not Just Use Solidity?
The honest answer is that Solidity was designed for the EVM, and the EVM was designed in 2014 with assumptions that no longer hold. Reentrancy vulnerabilities, unchecked arithmetic, implicit integer coercions, and the absence of ownership semantics in the type system have collectively cost the industry billions of dollars across hundreds of exploits. Solidity has improved - SafeMath is now default, Solidity 0.8 added overflow checks - but each fix is a patch on an architecture that was not built to prevent the problem in the first place.
Photon takes a different starting point. It draws on ideas from Rust (ownership and move semantics), Haskell (algebraic types, explicit effects), and the academic linear type literature to create a language where whole classes of bugs are prevented at compile time rather than caught by auditors at review time. It also integrates with the Qlorix runtime's CRYSTALS-Dilithium3 signature verification and Kyber-768 key derivation primitives as first-class standard library functions, rather than requiring you to import an unaudited third-party cryptography library.
Photon compiles to Qlorix bytecode, not EVM bytecode. If you are looking for EVM compatibility, the Qlorix network also supports a compatibility layer for Solidity contracts - but for anything you are writing fresh, Photon is the right choice.
Setting Up the Development Environment
The Photon toolchain ships as a single binary called photon that bundles the compiler, a local test node, a deployment CLI, and an interactive REPL. Install it with the Qlorix installer script:
The toolchain requires no additional dependencies on macOS or modern Linux distributions. On Windows, use WSL2 - the native Windows build is in progress but not yet stable for testnet work.
Create a new project with photon init:
The photon.toml manifest specifies the contract name, the target network, and dependencies. Open it to confirm the testnet endpoint is set correctly:
Photon Concepts You Need Before Writing Code
Linear Types and Move Semantics
In Solidity, token balances are just integers in a mapping. You can copy them, accidentally double-count them, or forget to subtract from one side of a transfer. The ERC-20 standard works around this through convention and auditing, but the language itself provides no guarantee that value is conserved.
Photon's type system enforces conservation directly. A value of a linear type can be used exactly once - it must be either consumed (transferred, burned) or explicitly stored, never silently dropped. The compiler rejects code that loses track of a linear value. For token balances, this means an entire category of double-spend and accounting bugs become compile-time errors rather than runtime vulnerabilities.
EffectManifest
Every Photon contract function carries an EffectManifest annotation that declares which side effects the function is permitted to perform. The compiler enforces that the function's body matches its manifest. A function annotated @pure cannot read storage. A function annotated @reads(balances) can read the balances storage slot but cannot write to it or emit events. A function annotated @mutates(balances) @emits(Transfer) can do both of those things but nothing else.
This is the Photon equivalent of Solidity's view and pure modifiers, but far more granular and enforced at the type level rather than as an optimizer hint. It means that when you read a Photon contract, every function's signature tells you exactly what it can touch. You do not need to read the entire body to understand the blast radius of a given call.
Quantum-Safe Primitives
The qlorix-std library exposes the network's CRYSTALS-Dilithium3 signature verification as a built-in function: Crypto.verifyDilithium(pubkey, message, signature). This is useful when your contract needs to verify off-chain signatures - for example, a permit-style approval flow where the user signs a message off-chain and submits the signature in a transaction. Because Qlorix uses Dilithium3 natively, you do not need to implement or import signature verification logic yourself.
Writing the Token Contract
Open src/main.photon and replace its contents with the following. Read through the code first; the explanation follows.
A few things to notice compared with a Solidity ERC-20. The storage declaration is separate from the contract body, making the full state surface explicit at a glance. Every function begins with its EffectManifest annotations - @reads, @mutates, @emits - so any reader knows immediately what the function can touch without scanning its body. The transfer function returns a typed Result rather than reverting with a string; callers can pattern-match on the error variant instead of catching a raw revert. And there is no unchecked block in sight - arithmetic overflow on U256 panics by default in Photon, so you never accidentally wrap around.
Compiling and Running Tests
Compile the contract with:
The compiler runs linearity and effect-manifest checks before producing bytecode. If you violate a manifest - say, you add an emit call inside a function not annotated with @emits - the error message points to the specific line and tells you which annotation to add. Write a quick test in tests/token_test.photon to verify the transfer logic before going to testnet:
Deploying to Qlorix Testnet
First, generate a testnet keypair and fund it from the faucet:
Note that the wallet address is derived from a Dilithium3 public key - this is a native Qlorix address, not an ECDSA address. The key material is stored in ~/.photon/wallets/deployer.key and is encrypted with your system keychain by default.
Deploy the contract by passing the constructor arguments directly to photon deploy:
Calling the Contract
Use photon call to read state and photon send to submit mutating transactions:
The ABI generated at compile time (target/my-token.abi.json) is the same format consumed by the Qlorix TypeScript and Rust SDKs, so wiring up a frontend or a backend service that calls your contract follows the exact same pattern. The SDK handles Dilithium3 transaction signing automatically using the wallet you configure - from a frontend developer's perspective, calling a Photon contract feels nearly identical to calling a Solidity contract via ethers.js.
What to Explore Next
This tutorial has covered the fundamentals - project setup, language concepts, a complete contract, testing, and deployment. The Photon standard library has considerably more to offer:
- Crypto.verifyDilithium for verifying off-chain user signatures inside contracts (permit flows, meta-transactions)
- ZkProof primitives for integrating with the Qlorix Groth16 prover to build privacy-preserving contracts
- CrossContract for type-safe inter-contract calls with effect propagation across the call boundary
- photon fmt and photon audit for formatting and automated vulnerability scanning before mainnet deployment
- The @upgradeable contract modifier for proxy patterns that preserve state across bytecode upgrades
The full language reference and standard library documentation live at qlorix.com/docs. The Qlorix Grant Program is actively funding teams building DeFi protocols, tooling, and infrastructure in Photon - if you are building something interesting, the application is at qlorix.com/grants.