Gluon is a cryptocurrency stabilization protocol. This repository contains its
smart contract on Ergo, the tests that execute it, and the scripts that deploy and operate
its reactors: Gluon Gold, pegged to one gram of gold, and Gluon Dollar, pegged to one US dollar. The
Gluon Ergo SDK (published to npm as @stability-nexus/gluon-ergo-sdk), located in sdk/, and the
Gluon Ergo UI, in its own repository, build users' transactions
for the reactors.
- ErgoScript, compiled to ErgoTree version 0
- Ergo, with prices from oracle pools
- TypeScript, run directly by Node.js 24 (there is no build step)
- Fleet SDK: compiler, mock chain, transaction builder and wallet
- sigmastate-js, the JavaScript build of the interpreter that Ergo nodes run
- Vitest
- Node.js 24 (the version in
.nvmrc) - npm
git clone https://github.com/StabilityNexus/Gluon-Ergo-Contracts.git
cd Gluon-Ergo-Backend-Contractsnpm cinpm testThe tests compile contracts/Gluon.es and execute every transaction type on a simulated chain,
so they need neither a node nor network access. npm run typecheck checks the TypeScript code, and npm run verify
checks the deployment configs and records (see Deployments).
contracts/Gluon.es the Gluon reactor contract
config/ public deployment parameters: the node, Gold and Dollar
lib/ deployment tooling: compilation, configuration, records, signing and simulation
sdk/ the SDK, published to npm as @stability-nexus/gluon-ergo-sdk
test/ contract tests, on a simulated chain
scripts/ deployment and operation scripts
deployments/ records of every deployment, per network
Deployments.md the same records, for humans (generated)
audits/ audit and security review reports
skills/ security review skills for coding agents
public/ logos
Each deployment is one box, the reactor, guarded by contracts/Gluon.es. It holds the reserve
of ERG and the neutrons and protons that are not in circulation. Neutrons are the stablecoin, pegged to an asset such
as one gram of gold. Protons are worth the rest of the reserve, so they absorb the swings of the price of ERG.
The fusion ratio q = neutrons in circulation × price of one neutron / reserve is the share of the reserve that backs
the neutrons. Fission, fusion and beta decays are allowed only while q is in the healthy range [0.5, 0.98], and beta
decays must also leave it there.
| Contents | |
|---|---|
| Value | the reserve plus _MinFee, in nanoERG |
| Token 0 | the reactor NFT |
| Token 1 | the neutrons not in circulation |
| Token 2 | the protons not in circulation |
| R4 | (Long, Long): total supply of neutrons and of protons |
| R5 | SigmaProp: the treasury, which receives the dev fees. The contract calls it TREASURY_MULTISIG, but deployments use a single-signature key. |
| R6 | (Long, Long): dev fees paid so far, and the maximum, in nanoERG |
| R7 | Coll[Long]: value of the beta decays + in each of the last 14 buckets of 720 blocks (about a day each), in nanoERG |
| R8 | Coll[Long]: the same for beta decays − |
| R9 | (Long, Long): first block of the current bucket, and the peg factor scaled by 10⁹ |
The compile-time constants are _MinFee, _OraclePoolNFT, _OracleBuybackNFT and _OracleFeePk (the ErgoTree of the
oracle's buyback contract). Each deployment sets them in its config.
Every transaction spends the reactor as INPUTS(0) and recreates it as OUTPUTS(0), under the same contract. It
also has the oracle pool box as data input 0. Except in Update Treasury, that box must hold _OraclePoolNFT as its
first token and must have been created less than 35 blocks before the block that includes the transaction. Update
Treasury does not use the oracle, but the contract reads data input 0 and INPUTS(1) in every transaction, so every
transaction needs both.
| Transaction | Reactor | Other inputs | Outputs after the reactor | Context variables |
|---|---|---|---|---|
| Fission | ERG in; neutrons and protons out, in proportion to their supplies | boxes paying the ERG | OUTPUTS(1): the user's box; then the fee outputs |
Optional UI-fee key, as variable 0 of the reactor input |
| Fusion | neutrons and protons in, in proportion to their supplies; ERG out | boxes holding the neutrons and protons | as for fission | as for fission |
| Beta Decay + | protons in, neutrons out; R7, R8 and R9's bucket block updated | boxes holding the protons; the oracle buyback box last, if an oracle fee is due | as for fission | as for fission, plus what the buyback contract requires (see the oracle fee below) |
| Beta Decay − | neutrons in, protons out; R7, R8 and R9's bucket block updated | boxes holding the neutrons; the oracle buyback box last, if an oracle fee is due | as for fission | as for beta decay + |
| Adjust Peg | peg factor × 0.99 if q > 0.98, or × 1.01 if q < 0.5; nothing else changes |
a box paying the miner fee | none required | none |
| Update Treasury | new key in R5; nothing else changes | INPUTS(1): a box of the current treasury's address; the treasury signs |
none required | none |
Anyone may submit any of these transactions, except Update Treasury, which only the current treasury can sign.
Fees. Each transaction of the first four types moves a value, its principal: the ERG paid or withdrawn, or the
value in ERG of the protons or neutrons that decay. Fission keeps 0.1 % of it in the reserve, fusion 0.5 %, and a beta
decay 0.5 % plus the net value of the beta decays in the same direction over the last 14 buckets, relative to the
reserve. Up to three fees are paid out, each in its own output, which receives exactly the fee plus _MinFee. These
outputs follow OUTPUTS(1) in this order, and a fee of zero has no output:
- Oracle fee, 0.1 % of a beta decay. It goes to the oracle buyback box, which the transaction spends as its last
input and recreates with the same contract (
_OracleFeePk) and first token (_OracleBuybackNFT), and with the fee plus_MinFeeadded to its value. Oracle-core v2 buyback contracts, such as the USD oracle's, also require variable 0 =1on the buyback input and the id of the spent buyback box in R4 of its successor. - Dev fee, 0.5 %, decreasing linearly to zero as the dev fees paid (R6) approach their maximum. It goes to the treasury (R5) and is added to R6.
- UI fee, 0.4 %, if the reactor input sets variable 0 to a key. It goes to that key.
lib/transactions.ts builds all six transactions, and lib/math.ts mirrors the
contract's integer arithmetic. Off-chain code such as the SDK must follow the same layouts and arithmetic.
The contract computes the price of one neutron as oracle price × peg factor / 10⁹. The initial peg factor therefore
sets the unit:
| Gluon Gold | Gluon Dollar | |
|---|---|---|
| Oracle pool | GoldPool, in nanoERG per kg of gold | USD Pool, in nanoERG per US dollar |
| Initial peg factor | 0.001 (stored as 1000000) | 1 (stored as 1000000000) |
| One neutron at launch | one gram of gold | one US dollar |
For Gold, a peg factor of 0.001 means no adjustment. Adjust Peg changes the factor by 1 % at a time, so compare it
with the initial factor, which deployments/ records.
The live Gold reactor runs v2 (tag v2).
In v3, contracts/Gluon.es:
- stores
(LastBucketBlock, PegFactor)in R9, instead ofLastBucketBlockalone; - multiplies the oracle price by the peg factor, instead of dividing it by 1000;
- adds Adjust Peg;
- allows fission, fusion and beta decays only in the healthy range, and checks that beta decays stay in it;
- normalizes the fusion ratio as
min(q / (q + 1 − q*), q)with q* = 0.99, instead ofmin(q, 0.66); - sets the slope of the beta decay fee, φ₁, to 1 instead of 0.5;
- checks the oracle in all four user transactions, not only in beta decays;
- requires Update Treasury to recreate the reactor under the same contract, which v2 did not check.
Public parameters live in config/: networks.json names the mainnet node and explorer,
and each deployment has a config, such as config/gold.json and
config/dollar.json. Values that are not decided yet start with TODO, and the scripts refuse
to use them. The only secret is the mnemonic of the wallet that signs, in .env:
cp .env.example .env # then set MNEMONIC| Script | Purpose |
|---|---|
node scripts/compile.ts <config> |
Print the contract a config compiles to: ErgoTree, address and template hash |
node scripts/bootstrap.ts <config> [--submit] |
Deploy a reactor |
node scripts/adjust-peg.ts <config> [--submit [--repeat]] |
Adjust the peg of an unhealthy reactor, once or until it is healthy |
node scripts/update-treasury.ts <config> <address> [--submit] |
Move the treasury to another single-signature address |
node scripts/render-deployments.ts |
Regenerate Deployments.md from deployments/ |
Scripts that send transactions do a dry run by default: they build and sign the transactions and have the node check
them, without broadcasting. Only --submit broadcasts.
The bootstrap mints the reactor NFT, the neutrons and the protons, then creates the reactor and burns the neutrons and
protons in circulation at genesis. The seed (at most 10 ERG) therefore stays in the reserve for good. The token amounts
follow from the config's initialFusionRatio and initialProtonPriceErg: in the shipped configs, the reactor starts
at a fusion ratio of 0.6, with one proton worth 1 ERG.
- Set every
TODOvalue in the deployment's config: the treasury, the seed and the token metadata. - Commit the contract source: the deployment record points to the commit it was compiled from.
- Run
node scripts/bootstrap.ts <config>. Besides the node's check, the dry run simulates a fission, both beta decays, a fusion and an Adjust Peg against the new reactor box. - Run it again with
--submit. It records the deployment indeployments/<network>.jsonandDeployments.md. Commit both, and tag the deployed commit.
Deployments.md lists every reactor deployed so far, with its contract address, its tokens and the
parameters it was deployed with. deployments/ holds the same records as JSON, for programs. On
mainnet, the v2 Gluon Gold reactor is live; the v3 Gold and Dollar reactors are planned. The v3 Gold deployment
needs a new oracle buyback box first, because the GoldPool's buyback NFT was burned.
npm run verify, which CI runs, recompiles each recorded contract from the commit it was compiled from and checks
that it yields the recorded ErgoTree, address and template hash. It also checks that every config is valid and that
Deployments.md is up to date.
sdk/ holds the TypeScript SDK, published to npm as @stability-nexus/gluon-ergo-sdk. It reads reactor boxes, computes quotes and analytics, and builds EIP-12 transactions for Gluon v3 reactors on Ergo mainnet.
- Build:
npm run build:sdkcompiles the SDK TypeScript source tosdk/dist/. - Packaging check:
npm run check:sdkvalidates package structure, browser safety, and type declarations. - Releases: Automated via
.github/workflows/release.ymlon pushing a tag matchingsdk-v<version>(where the tag matchessdk/package.json), using npm trusted publishing (OIDC with provenance).
The SDK in sdk/ is licensed under the GNU General Public License v3.0 (GPL-3.0-only). As decided in the repository cleanup (decision CD4), this license applies to the SDK package only; the repository as a whole has no global license file.
audits/ holds the reports. They name the contract by its former path, GluonWBoxGuardScript.es, at the
commits they reviewed:
AuditReport-KB.pdf: audit by Krasavice Blasen (November 2024);GluonWBoxGuardScript-refactoring-audit-2026-10.md: audit of the October 2026 refactoring of the contract;GluonWBoxGuardScript-remediation-review-2026-10.md: review of the remediations of that audit's findings;GluonWBoxGuardScript-refactoring-audit-bbc77a8.md: audit of a later refactoring commit,bbc77a8;sec_review_2026-10-11T02-51-42Z_369fb81.mdand its remediations: security review of v3.
skills/ holds the security-review and security-remediation skills for coding agents.
Found a vulnerability? Please do not open a public issue. Contact the Stability Nexus team privately via Discord or Telegram.
We welcome contributions of all kinds! To contribute:
- Fork the repository and create your feature branch (
git checkout -b feature/AmazingFeature). - Commit your changes (
git commit -m 'Add some AmazingFeature'). - Run the development workflow commands to ensure code quality:
npm run typechecknpm testnpm run verify
- Push your branch (
git push origin feature/AmazingFeature). - Open a Pull Request for review.
If you encounter bugs, need help, or have feature requests:
- Please open an issue in this repository providing detailed information.
- Describe the problem clearly and include any relevant logs or screenshots.
We appreciate your feedback and contributions!
© 2025 The Stable Order.