Zycord
Audit date ·
This is a point-in-time review of code quality and tokenomics at the commit above, not a safety certification. Projects can change after this date. Check for newer releases or a newer audit before relying on these scores.
RandomX Go L1; no premine, height-based issuance, and a keyless, time-limited treasury.
🪙 Zycord (ZCD) — Code & Tokenomics Audit
Repo: gitlab.com/zycord-group/zycord-node at commit a580df8ccc081a678fd6bee2cf55cbdba48830c6
Date: 2026-10-17 Scope: recon, code review (money and consensus path read line by line), tokenomics, write-up. All phases ran. Hashrate and profitability were not measured — they are out of scope for this scale.
🎯 Assessment: FAVORABLE (reviewed code and tokenomics only; not a safety certification)
Zycord is a hand-written Go layer-1 with a RandomX v2 CPU subsidy and an unusually strict codebase. Issuance is a pure function of block height: every node recomputes the subsidy from the height alone, and no header carries an amount. There is no premine, no mint path to the native coin, and the founder share is 3% and time-boxed.
This scores code and tokenomics only. It says nothing about whether Zycord survives, whether anyone will trade it, or whether it outlives its author. The chain is about ten days old at audit and the author is anonymous. Those are survival questions, not trust problems, and they do not move the number.
🔑 At a glance
| Field | Value |
|---|---|
| Project | Zycord (ZCD) |
| Chain / consensus | Own L1, PoW RandomX v2, 30 s target; PoS gate at height 1,576,800 |
| Code score | 9.5 / 10 |
| Tokenomics score | 10.0 / 10 |
| Risk score | 9.5 / 10 (the lower of the two) |
| Assessment | FAVORABLE (code and tokenomics only; not a safety certification) |
🔗 Links & sources
| What | Link | Status |
|---|---|---|
| 🏠 Homepage | not found | — |
| 💻 Source | https://gitlab.com/zycord-group/zycord-node at a580df8 | ✅ fetched, HTTP 200 |
| 🔎 Explorer | https://explorer.zycord.com/api/block/1 | 🔍 cited in this note, not fetched |
| 📄 Whitepaper | not found | — |
| 📣 Announcement | not found | — |
| 💬 Socials | not found | — |
🧮 Risk score inputs
| # | Finding | file:line | Reachable | Deduction | Applies to |
|---|---|---|---|---|---|
| 1 | LOW — the header carries its own PoWHash and CheckWork compares the recomputed digest to it, so the cheap pre-execution gate passes an easy-looking header and costs one full RandomX hash per unverified header to reject. Bounded per-peer cost, not an issuance flaw | core/pow/pow.go:57-73, core/pow/pow.go:95-105, node/verify/work.go:42 | live | −0.5 | code |
| 2 | No hard cap — tail emission continues forever — Deliberate design, stated in the params file itself. Not a missing-cap finding | spec/params.json:35-38, core/params/params.go:269-276 | live | −0 | tokenomics |
| 3 | Issuance is a pure function of height and every node recomputes it — so no MAX_SUPPLY constant is needed and nothing is unbounded | core/params/params.go:256-296, core/fold/fold.go:402 | live | −0 | tokenomics |
| 4 | Founder tax 3% of subsidy until height 3,153,600 — below the scale's 5% floor | spec/params.json:41, core/params/founder.go:58-70 | live | −0 | tokenomics |
| 5 | Treasury 3% (6% after height 3,153,600) has no spend path — so no live-spending-key deduction | core/validity/validity.go:618-624, core/fold/fold.go:1591-1597 | live | −0 | tokenomics |
| 6 | Docs-vs-code block-1 payout mismatch — resolved, stale note, not a contradiction (commit b4303e1 changed emission_ramp_blocks) | spec/params.json:64 | live | −0 | tokenomics |
code deductions, one at a time:
0.5
tokenomics deductions, one at a time:
0.0
code_score = 10 - 0.5 = 9.5
tokenomics_score = 10 - 0 = 10.0
risk_score = min(code, tokenomics) = 9.5
Instant-AVOID override fired: none. All five checked —
- No key, role or admin path mints native supply or edits the schedule outside consensus.
deriveMintrefuses a MINT unless the asset carries version byte0x03, so the all-zero native asset cannot be minted (core/validity/derive.go:216-224,core/types/slots.go:10-11). - No code sends a seed, key or wallet contents anywhere; the phrase path says so outright (
wallet/phrase.go:23-25). - No backdoor, killswitch or remote-code path found in
node/,core/,wallet/. - Not a fork presented as original work — RandomX is an external algorithm behind a build tag and is declared in the consensus root (
spec/params.json:56). - No supply or privacy claim that the code contradicts — see the tokenomics table.
🔒 Code
Commit audited: a580df8
- ✅ The subsidy is never read from the block.
Emission(height)is a pure function of height (core/params/params.go:256-266) and the split is recomputed per block atcore/params/founder.go:58-70, called fromcore/fold/fold.go:402. A miner cannot declare a reward. The header carries only a payout address (core/types/block.go:60), validated as a user address atcore/fold/blockrules.go:662-670(B11). - ✅ Coinbase rolls through a 240-slot maturity ring and burns the reward if the payee is spent (
core/fold/fold.go:1395-1428, F12). Producer, founder and treasury credits are applied inside one journal, with undo on overflow (core/fold/fold.go:419-441). - ✅ Height continuity is enforced where ancestry is held:
node/chain/forkchoice.go:192,201refuse a branch block that is not parent height + 1. This is the check that makes "issuance is a function of height" safe — a header cannot claim height 5,000,000 on top of height 3 and collect a mature subsidy. - ✅ Proof of work is checked on every path:
pow.CheckWorkrecomputes the RandomX digest (core/pow/pow.go:57-73), the cheap half rejects a target abovemax_target(core/pow/pow.go:95-105, B38), and the declared seed epoch is pinned to the height (core/fold/blockrules.go:651-657, B0b). - ✅ Protocol cells are unreachable to user traffic twice over: V7 refuses any certificate write under address version
0x00(core/validity/validity.go:618-624) and F13 refuses it again at apply (core/fold/fold.go:1591-1597). Debits elsewhere require a signature-bearing user address (core/crypto/crypto.go:164-166, used atcore/validity/intent.go:282). - ⚠️ LOW —
PoWHashis carried in the header and compared rather than treated as secret.CheckCommitmentruns where no parent is known and evaluates no work function (core/pow/pow.go:95-105, B38), so a header with an easy-looking target passes the free gate and then costs one RandomX hash to reject atcore/pow/pow.go:69-72. Reachable on the gossip and sync paths (node/sync/sync.go:394,node/verify/work.go:42). Consensus is not broken — the digest is still verified against the header input. Fix: rate-limit work evaluations per peer. - ✅ Wallet keys are encrypted at rest: Argon2id + AES-256-GCM with a validated parameter ceiling (
wallet/keyfile.go:23-62,271-272). The BIP39 PBKDF2 step is deliberately skipped and the reason is written down (wallet/phrase.go:23-25). - ✅ Node RPC binds loopback only,
127.0.0.1:9420, and rejects a non-loopbackHost(node/rpc/rpc.go:54,270-281). No telemetry or analytics code found innode/,wallet/,cmd/. - ✅ Dependency surface is three
golang.org/xmodules, confined towallet/andcmd/bymake check-imports;core/is standard library only (go.mod:1-14).go.sumis present. The storage engine is written in-tree rather than imported. No unpinned-dependency deduction. - ✅
unsafe.Pointerappears only in Windows syscall shims and a PTY test (internal/osfile/osfile_windows.go,node/storage/lock_windows.go,cmd/zycordd/underwrite_pty_linux_test.go). Nothing on a consensus path. No deduction. - ✅ Tests cover the money path heavily:
core/validity/emission_bound_test.gocross-checksCumulativeEmissionagainst a height-by-height sum ofEmission(lines 113, 132-155);core/fold/theorem_test.go:298,340pin that the supply cap, not a per-cell ceiling, stops a mint;core/cevm/protocolcellguard_test.go:57sweeps opcodes for guards on protocol cells including the treasury cell. No deduction. - ✅ CI exists and builds the release artifact:
.gitlab-ci.ymlis a tag-triggeredreleasestage that requires the signing key and deploy credentials to be mounted and refuses a tag that is not on the commit. No deduction.
📊 Tokenomics
| Item | Value | Evidence | Flag |
|---|---|---|---|
| Premine | None. Emission(0) returns zero: "The genesis block pays no coinbase: there is no miner to pay, and a reproducible genesis must not credit an address". B10 also refuses a genesis block that names a payout address | core/params/params.go:256-260, core/fold/blockrules.go:643-645 | ✅ |
| Mint authority over the native coin | None. deriveMint refuses unless args.Asset[0] == AddrVersionAsset (0x03); the native asset is the all-zero address, version 0x00. Asset mints are capped against AssetCapSlot | core/validity/derive.go:216-243, core/types/slots.go:10-11 | ✅ none |
| Issuance enforced in consensus | Yes. Every node recomputes the split per block from the height (core/fold/fold.go:402 via p.SubsidySplit). No header field carries an amount. The schedule also bounds user-side claims: V5 refuses a deposit larger than CumulativeEmission(TTL) | core/fold/fold.go:402, core/params/founder.go:58-70, core/validity/validity.go:755 | ✅ enforced in consensus |
| Hard cap | No hard cap — tail emission continues forever. The decaying schedule runs epochs 0-4375, then a permanent tail of 33000000 drops (0.33 ZCD per block) from epoch 4376, height 12,602,880. Stated as a design choice in the params file | spec/params.json:35-38, core/params/params.go:269-276 | ✅ deliberate tail emission |
| Emission curve | No halving step. Per-epoch geometric decay, divisor 1054 per 2,880-block epoch → yearly factor 0.70702, so the rate halves every two years. Slow start over 86,400 blocks (30 days): block 1 pays 0.00023623 ZCD, block 43,200 pays 10.21 ZCD, block 86,400 is the first full rate at about 20.41 ZCD | spec/params.json:35-38, core/params/params.go:269-290 | ✅ |
| Supply totals (not a cap) | Pre-tail sum 62,744,838.47 ZCD; 62,744,817.47 reachable because genesis pays nothing; about 61,836,900.03 actually issued because the slow start withholds 907,917.44 forever. The file warns that the easy product 63,745,920 overshoots by 1.6% and is only an upper bound | spec/params.json:63 note | ✅ |
| Dev / founder share | 3% of the subsidy (300 bps) to 0x02f29dc4...abe9b11, until height 3,153,600 (3 years at 30 s). Ends there. About 1,208,790 ZCD in total, 3% of what is issued by then. On the subsidy only, never on fees | spec/params.json:41, core/params/founder.go:58-70 | ✅ 3% tax — below the 5% floor |
| Producer share | 94% while the founder share is active, exact remainder via saturating math, so the shares always sum to the subsidy | core/params/founder.go:58-70 | ✅ |
| Treasury | No spend path. Credited into TreasurySlot(), the native balance cell of ProtocolAddress = version byte 0x00 + 31 zero bytes. V7 rejects any certificate write to a 0x00 cell; F13 repeats it at apply; debits elsewhere require IsUserAddress, which is false for the protocol address. No key signs for it, no timelock, no multisig — inert. From height 3,153,600 it also takes the founder's 3%, so 6% accrues | core/types/slots.go:122-123, core/crypto/crypto.go:131-132,161-166, core/validity/validity.go:618-624, core/fold/fold.go:433-440,1591-1597 | ✅ no spend path |
| Docs vs code | Old vault claim: block 1 pays 0.00022207. Params and code: block 1 pays 0.00023623. Cause found — commit b4303e1 (2026-10-03) set emission_ramp_blocks to 86,400 and rewrote the ramp notes. Code matches current docs; the 2026-10-15 figure was stale | spec/params.json:64, commit b4303e1 | ✅ no contradiction |
| Genesis reproducible from published params | Yes. Params are embedded and in the consensus root; spec/chain-ids.json is a chain-id ledger, so a released binary refuses a parameter file that reuses a spent chain id under another genesis | spec/chainids.go:16-69, commit b4303e1 | ✅ |
| Params tamper resistance | Validate() holds treasury_share_bps below 10,000 and founder bps below 10000 - treasury_bps, with an explicit anti-integer-wrap comment ("a founder bps near 2^64 would then pass and mint without bound"); the founder address must be a persistent 0x02 address and end_height is required. Changing any of it changes the consensus root, so it needs a hard fork the network accepts | core/params/params.go:659-661, core/params/founder.go:75-95 | ✅ |
🕵️ Privacy
- ✅ RPC binds loopback only,
127.0.0.1:9420, and refuses a non-loopbackHostheader as CSRF protection (node/rpc/rpc.go:54,270-281). Remote access is documented as an SSH tunnel or an authenticating reverse proxy (node/rpc/rpc.go:399). - ✅ No telemetry or analytics code in
node/,wallet/,cmd/,internal/. No forced outbound beacon found on the node or wallet paths. - ✅ The update system requires a detached signature and treats its absence as fatal (
update/carryforward.go:11-14); the release key is committed publicly and the CI job compares rather than copies it (.gitlab-ci.yml). - ✅ Key custody is local. Key files are Argon2id + AES-256-GCM, with the passphrase as the encryption (
wallet/keyfile.go:23-62,271-272). Nothing leaves the machine. - ⚠️ The BIP39 PBKDF2 seed step is deliberately not performed anywhere (
wallet/phrase.go:23-25). The paper phrase is not a second factor — the key-file passphrase is the only encryption. Pick a long passphrase. - ⚠️ Stratum mining links your IP to the payout address like any pool (
node/stratum/conn.go:424). Solo mining avoids it. - 🔍 Exit privacy is unconfirmed for this audit — it depends on where the coin is traded, which is out of scope.
🚩 Red flags
- ⚠️ LOW — the header declares its own
PoWHash, so the free pre-execution work gate passes an easy-looking header and a full hash is needed to reject it (core/pow/pow.go:57-73). A per-peer resource shape, not a consensus break. - ⚠️ RandomX key schedule departs from upstream: the key comes from height plus chain id, not a key block hash (
spec/params.json:66). Every future key is known from genesis. The author states the cost himself: long-horizon offline specialisation. The PoS gate at height 1,576,800 bounds the exposure. - ⚠️ Single anonymous author (Simstoshi) and a chain about ten days old at audit —
genesis_time1791331200 is 2026-10-07 00:00 UTC. Ordinary abandonment risk, not a trust problem. - ⚠️ The launch was relaunched: commit
b4303e1(2026-10-03) pins mainnet and testnet chain ids live after "every relaunch parameter frozen", and the params file records an earlierh1_bondof 40,320 that was superseded before launch. The cancelled instance's chain id stays spent inspec/chain-ids.json.
✅ Green flags
- ✅ Issuance is a pure function of height and every node recomputes it:
core/params/params.go:256-266read atcore/fold/fold.go:402, and re-applied to user claims atcore/validity/validity.go:755. - ✅ No premine, and the refusal is written into both the emission function and the genesis shape rule (
core/params/params.go:258-260,core/fold/blockrules.go:643). - ✅ The treasury is a keyless protocol cell — 3%, then 6%, of the subsidy accrues there and no transaction can move it (
core/validity/validity.go:618-624,core/crypto/crypto.go:165-167). - ✅ Anti-wrap thinking in the parameter validator,
core/params/founder.go:75-95. - ✅ Consensus test volume far above the norm for a small PoW chain:
core/foldis about 27,000 lines includingproperty_test.go,griefing_era1_test.go,cert_budget_test.go,theorem_test.go; golden vectors inspec/plus a 3,253-line vector generator. - ✅ Supply-chain discipline: three third-party modules total, storage engine in-tree, allowlist enforced by
make check-imports(go.mod:1-14). The reproducible-build pin lives in the Makefile and a test fails ifgo.mod, the Makefile and the Dockerfile disagree. - ✅ Documentation states its own costs, including the 907,917.44 ZCD the slow start withholds and never repays (
spec/params.json:64). Reproduced independently below. - ✅ A parameter file cannot be loaded unvalidated:
params.ParsesetsDisallowUnknownFields()then callsValidate()before returning (core/params/params.go:735-742), and the comment there states whyValidatealone is not the gate. A typo'd founder field fails to load rather than defaulting. - ✅ Checkpoints cannot mint. They are client history pins, not consensus:
spec/checkpoints.jsoncarries a_not_consensuskey saying nothing in it entersparams.ConsensusRoot(), mainnet'scheckpointsarray is empty andmin_chain_workis0, and the set exposes only refusal-style checks —Contradicts,MeetsFloor,ValidateAgainstTip(node/checkpoints/checkpoints.go:94,101,122) — and no balance-write path. - ✅ Emission reproduced independently from
spec/params.jsonalone (script/tmp/audit/zycord/emcheck.py, replayingbuildEmissionTableatcore/params/params.go:369-390): block 1 pays0.00023623ZCD and the slow start withholds907,917.44ZCD — both exact matches to the doc comment atspec/params.json:63-64. Rate at block 86,400 reproduces as 20.41043 ZCD against the doc's "20.41 ZCD".
📋 Context (does not affect the score)
| Signal | Value |
|---|---|
| Repo / commit audited | gitlab.com/zycord-group/zycord-node at a580df8ccc081a678fd6bee2cf55cbdba48830c6; shallow clone holds 377 commits |
| Contributors / commits | 🔍 not resolvable from a shallow clone; GitLab authorship needs auth |
| AI-assisted commits | yes — 279 commits carry Co-Authored-By: Claude, 2 more carry a Claude Opus co-author trailer |
| Relaunch, rename or abandoned predecessor | yes in the launch history — b4303e1 (2026-10-03) references frozen relaunch parameters and pins both chain ids live; an earlier h1_bond of 40,320 was superseded before launch |
| Independent audit | none found in-repo; SECURITY.md exists |
| Public RPC or explorer reachable | no working API. https://explorer.zycord.com/api/block/1 and /api/status both returned 404 page not found (fetched 2026-10-18, --max-time 25). The explorer UI exists but exposes no JSON endpoint, so on-chain supply stays unverified, not clean |
| Market data | not relevant at this stage |
| Hashrate, difficulty, power cost, profitability | out of scope for this scale |
❓ What I could not check
- 🔍 On-chain supply and the real block-1 payout. Still open:
https://explorer.zycord.com/api/block/1returns404 page not found, so no public endpoint answers. Close it by syncing a node and comparingCumulativeEmission(h)with the sum of balance cells at height h. The code proves the schedule; it does not yet prove the live chain followed it. - 🔍 One number I could not reproduce exactly: my independent replay of
buildEmissionTablegives 4,377 table epochs and a tail handover at height 12,605,760, whilespec/params.json:63states epoch 4376 / height 12,602,880 — a two-epoch (one-epoch-index) difference at the tail boundary, which also shifts my pre-tail total to 62,745,788.87 against the documented 62,744,838.47. My script is naive about the final partial epoch, so this is recorded as my gap, not a repo defect; the repo asserts these values in its own tests. Close it by runninggo test ./core/paramsand reading the emission table tests rather than re-deriving them. - 🔍 Founder address history beyond the code path. Close it by reading
NativeBalanceSlot(0x02f29dc4...abe9b11)on a synced node and checking every credit came from a block subsidy. - 🔍 Contributor count and per-commit authorship. Close it with a full clone plus GitLab API access.
- 🔍 Absolute hashrate and 51% cost. Out of scope for the score, but it stays the practical risk for a miner until measured from a synced node.
- 🔍 That the treasury cell has only ever accrued on the live chain. The code path is closed by V7 + F13 +
IsUserAddress, so this is a belt-and-braces check.
🧭 Bottom line
This is one of the better-engineered small PoW chains reviewed here. The subsidy is a pure function of height, every node recomputes it, height continuity is enforced before the subsidy is read, and the treasury share has no spend path at all — it accrues into a protocol cell that no certificate can debit.
Tokenomics scores 10.0 because there is nothing to deduct: no premine in code, no native mint path, a 3% founder share that ends at height 3,153,600, and a deliberate perpetual tail of 0.33 ZCD per block from height 12,602,880. There is no hard cap and the tail continues forever — the project states that as design, and issuance being a pure function of height means an absent MAX_SUPPLY constant is not a defect.
Code scores 9.5: one LOW finding about the cheap work gate accepting a header-declared PoWHash. That is resource exhaustion per peer, not a consensus break. To reach 10.0 the node needs per-peer rate limits on work evaluations.
What would move it down a band: a parameter change that raises founder_share.bps or moves end_height without the chain-id ledger refusing it, or evidence of specialised RandomX hardware exploiting the fixed key schedule. Watch the PoS gate at height 1,576,800 and the founder end at 3,153,600.
Citations
core/pow/pow.go:57-73
core/pow/pow.go:95-105
node/verify/work.go:42
spec/params.json:35-38
core/params/params.go:269-276
core/params/params.go:256-296
core/params/founder.go:58-70
core/validity/validity.go:618-624
core/validity/derive.go:216-224
core/types/slots.go:10-11
Every citation on this page points at a file that was opened during the audit. Nothing here is a guess.