SkillAgentSearch skills...

Starlight

:zap: solidity --> zApp transpiler :zap:

Install / Use

/learn @EYBlockchain/Starlight
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

This code is not owned by EY and EY provides no warranty and disclaims any and all liability for use of this code. Users must conduct their own diligence with respect to use for their purposes and any and all usage is on an as-is basis and at your own risk.

starlight :stars:

Generate a zApp from a Solidity contract.


Introduction

zApps are zero-knowledge applications. They're like dApps (decentralised applications), but with privacy. zApps are tricky to write, but Solidity contracts are lovely to write. So why not try to write a zApp with Solidity?

starlight helps developers do just this...

  • Write a Solidity contract
  • Add a few new privacy decorators to the contract (to get a 'Zolidity' contract)
  • Run zappify
  • Get a fully working zApp in return

Solidity contract --> Zolidity contract --> zappify --> zApp

The main objective of this transpiler is to enable developers to quickly draft frameworks for zApps.

See here for the gitbook, and here for an enormously detailed explanation of how the transpiler works.


Warnings

This code is not owned by EY and EY provides no warranty and disclaims any and all liability for use of this code. Users must conduct their own diligence with respect to use for their purposes and any and all usage is on an as-is basis and at your own risk.

Note that this is an experimental prototype which is still under development. Not all Solidity syntax is currently supported. Here is guide to current functionality.

This code has not yet completed a security review and therefore we strongly recommend that you do not use it in production. We take no responsibility for any loss you may incur through the use of this code.

Due to its experimental nature, we strongly recommend that you do not use any zApps which are generated by this transpiler to transact items of material value.

Output zApps use the proof system Groth16 which is known to have vulnerabilities. Check if you can protect against them, or are protected by the zApps logic.

In the same way that Solidity does not protect against contract vulnerabilities, Starlight cannot protect against zApp vulnerabilities once they are written into the code. This compiler assumes knowledge of smart contract security.

Note on integer limits: Starlight compiles your Solidity contract into a zApp written in JavaScript. Unlike Solidity's uint256 (which supports integers up to 2^256-1), JavaScript's standard Number type can only safely represent integers up to 9,007,199,254,740,991 (2^53 – 1), known as Number.MAX_SAFE_INTEGER. This means that values above 2^53–1 are not fully supported in the JavaScript output, and large values will lose precision or behave unexpectedly. When designing your contracts, ensure that all values remain within the safe integer range for JavaScript.


<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> <!-- END doctoc generated TOC please keep comment here to allow auto update -->

Requirements

To run the zappify command:

  • Node.js v15 or higher (Known issues with v13 and v18).

To run the resulting zApp:

  • Node.js v15 or higher.
  • Docker (with 16GB RAM recommended)
  • Mac and Linux machines with at least 16GB of memory and 10GB of disk space are supported.

Quick User Guide

1. Creating a Zolidity (.zol) contract from a standard Solidity (.sol) contract

When writing zolidity contracts note that not all Solidity syntax is currently supported. Here is a guide to current functionality.

Zolidity decorators: secret, known, unknown, encrypt, sharedSecret and re-initialisable

In Starlight, each secret variable is assigned an owner. The owner holds a secret key that allows them to access the private value and update that variable. Secret variables are stored on-chain via commitments, and the original data (preimage) is kept locally by the owner.

Zolidity extends Solidity with the following decorators:

  • secret: Marks a variable or parameter as private, so its value is hidden from other users and only accessible to its owner.
  • known: Indicates that only the variable owner can modify the variable. By default all variables are known.
  • unknown: Allows any user to increment the variable, not just the owner.
  • encrypt: Use when creating a commitment for another user (e.g., with unknown). Ensures, via zk-proofs, that a correct encryption of the commitment pre-image is broadcast and can be decrypted by the owner so that the commitment can be used in the future.
  • sharedSecret: Marks a secret variable that is shared between two owners, for applications such as Swaps.
  • re-initialisable: Allows a secret variable to be re-initialised, if later in the contract it is burned.

Start with a standard Solidity contract, for example:

// SPDX-License-Identifier: CC0

pragma solidity ^0.8.0;

contract Assign {
  uint256 private a;

  function assign(uint256 value) public {
    a = value;
  }
}

To convert your Solidity contract to a Zolidity contract, insert the secret keyword in front of the declaration of any state variable, local variable, or function parameter you want to keep private:

// SPDX-License-Identifier: CC0

pragma solidity ^0.8.0;

contract Assign {
  secret uint256 private a; // <--- secret

  function assign(secret uint256 value) public { // <--- secret
    a = value;
  }
}

Example: Charity contract using unknown and known

Here's a simple Zolidity contract for a charity, where anyone can donate but only the admin can withdraw. The variable balance is marked with unknown before the incrementation in donate(), to mark that anyone can call donate and increment balance. Only the owner can decrement balance in the withdraw function.

// SPDX-License-Identifier: CC0

pragma solidity ^0.8.0;

contract CharityPot {
  secret uint256 private balance;
  address public admin;

  constructor(address _admin) {
    admin = _admin;
  }

  // Anyone can donate to the charity
  function donate(secret uint256 amount) public {
    unknown balance += amount; // <--- anyone can increment
  }

  // Only the admin can withdraw funds
  function withdraw(secret uint256 amount) public {
    require(msg.sender == admin, "Only admin can withdraw");
    balance -= amount; // <--- only admin can decrement
  }
}

Example: Using the encrypt decorator

Sometimes, a commitment is created for a secret variable that is owned by another user. For example, when using the unknown tag (as explained above), or with address variables — where ownership is tied to the address and can change when the address changes. To use a commitment, the owner needs the commitment pre-image, but they may not have access to it if they did not create the commitment themselves. The encrypt tag solves this by encrypting and broadcasting the commitment pre-image, guaranteeing via the zk proofs that the ciphertext is correctly formed so the new owner can access and use the commitment. For example:

// SPDX-License-Identifier: CC0

pragma solidity ^0.8.0;

contract Assign {
  secret uint256 private a; // <--- secret

  function assign(secret uint256 value) public { // <--- secret
    encrypt unknown a += value; // <--- guarantees the new owner can use the new commitment related to this state update
  }
}

Example: Using the sharedSecret decorator

In the contract below, swapProposals is a secret variable that is created by the user who starts the Swap and modified by a different user who completes the Swap. It therefore needs two different owners and so is marked with the sharedSecret decorator. For more details on how to test Swaps see the Private Swap Contract section.

contract Swap {

    secret mapping(address => uint256) public balances;
    secret mapping(uint256 => address) public tokenOwners;
    struct swapStruct{
        uint256 swapAmountSent;
        uint256 swapTokenRecieved;
        address swapInitiator;
        uint256 pendingStatus;
    }
    sharedSecret mapping(address => swapStruct) swapProposals;  

    function startSwap(
      secret address sharedAddress, secret uint256 amountSent, secret uint256 tokenIdRecieved
    ) public {
        require(swapProposals[sharedAddress].pendingStatus == 0);
        swapProposals[sharedAddress].swapAmountSent += amountSent;
        balances[msg.sender] -= amountSent; 
        swapProposals[sharedAddress].swapTokenRecieved = 

Related Skills

View on GitHub
GitHub Stars199
CategoryDevelopment
Updated11d ago
Forks43

Languages

JavaScript

Security Score

80/100

Audited on Mar 17, 2026

No findings