Build on Dznye
Plain HTTP and JSON. Every change is a transaction you sign yourself; there is no privileged endpoint, and no account needed with us.
Node address: https://api.dznye.com (also https://chain.dznye.com/api). The wallet is at wallet.dznye.com, the explorer at explorer.dznye.com, network status at status.dznye.com.
Fees paid for you: the welcome fund pays the fee for everyday actions (sending, names, devices, trading, channels, small contract calls), within limits, so newcomers never need DZY first. Name it as payer (its address is at GET /sponsor) and send the signed transaction to POST /sponsor {"tx": …}.
dznye-1
| Name | dznye-1 (inside every signature) |
|---|---|
| Genesis hash | 1085eabf51d40005d6e8ad6d70dcdd66d7cb6b4a6c4eb8eae858ff2e6a33ad77 |
| Genesis file | dznye-1.genesis.json |
| Fee | 0.001 DZY per transaction (1,000 parts; 1 DZY = 1,000,000 parts) |
| Coin | DZY, 6 decimals, at most 12,000,000, never destroyed |
Check that a node runs exactly this chain: node chain/cli.mjs genesis check dznye-1.genesis.json --node https://chain.dznye.com/api
The API
| Request | Gives |
|---|---|
GET /api/ | chain, block height, validators, the fee, the latest snapshot, this node's role, any alert |
GET /api/account/<address> | balances, the nonce to use next, device keys, agent keys, guardian, stake |
GET /api/validators | the validators, each one's say, every stake |
GET /api/assets, /api/offers | every token and every open offer |
GET /api/supply, /api/supply/<token> | limit, issued, existing, circulating; for DZY the treasury, stake and each pool |
GET /api/genesis, /api/blocks?from=1&limit=100, /api/block/<n> | everything needed to replay and check the chain |
GET /api/snapshot | the latest kept snapshot: a block and the whole state after it |
GET /api/peers, POST /api/peers {url} | places known to hold this chain; announce yours (checked before it is listed) |
GET /api/contracts, /api/contract/<addr>, /api/contract/<addr>/view?fn=&args= | contracts: all of them, one in full, or a free question to one |
GET /api/channel/<id> | a payment channel |
POST /api/tx { "tx": … } | send a signed transaction (60 a minute per address) |
Signing a transaction
A transaction is { chain, from, nonce, type, data, sig }. The signature is Ed25519 over "dzy-tx:" + canon({ chain, from, nonce, type, data, signer, payer }), where canon writes JSON with keys sorted and whole numbers only. Amounts are whole numbers of the smallest part.
import { Client } from './chain/src/client.ts';
const c = new Client('https://chain.dznye.com/api');
await c.send(myKey, 'transfer', { asset: 'DZY', to: address, amount: 1_500_000 }); // 1.5 DZY
// an app paying its user's fee: the user signs naming the app, the app adds its signature
const tx = await c.prepare(userKey, 'transfer', { asset: tokenId, to, amount: 5 }, { payer: appKey.address });
await c.submit(await Client.sponsor(appKey, tx));
| Type | Does |
|---|---|
transfer | send any asset, with an optional public memo |
asset.create, mint, burn | make a token (1 DZY to the treasury), issue it (its maker only), destroy your own (DZY goes to the treasury instead) |
offer.create, offer.take, offer.cancel | swap any asset for any other, both sides at once |
key.add, key.remove | devices on one account |
agent.allow, agent.revoke | a limited key for software: one asset, a total, a fee budget, an end time |
guardian.set, recover.start / .cancel / .finish | recovery through a guardian, with a 3-day wait |
stake.bond, stake.unbond, stake.withdraw, validator.unjail | validators locking DZY |
evidence | proof that a validator signed two different votes |
treasury.spend | the treasury paying out (the issuing key only, with a public reason) |
name.set, name.release | choose a name (3 to 20 small letters, digits or dashes; 0.1 DZY to the treasury), so people can send to @name. Look one up: GET /api/name/<name> |
account.multisig | several keys must agree: every transaction from the account then needs need of keys to sign (extra signatures go in cosigs; Client.cosign) |
contract.deploy, contract.call | put a program on the chain; call one of its functions, optionally sending it coins |
channel.open, .fund, .close, .start, .update, .settle | payment channels (the second layer) |
No installs: Node 24 is enough
node chain/cli.mjs key new me.json
node chain/cli.mjs info --node https://chain.dznye.com/api
node chain/cli.mjs balance <address> --node https://chain.dznye.com/api
node chain/cli.mjs send --key me.json --to <address> --amount 1.5 --node https://chain.dznye.com/api
node chain/cli.mjs token create --key me.json --symbol GOLD --name "Game gold" --node https://chain.dznye.com/api
node chain/cli.mjs validators --node https://chain.dznye.com/api
Programs on the chain
A contract is a set of functions written as JSON: no compiler, and every node reads it the same way. It has its own address, can hold DZY and tokens, and keeps its own storage. Everything is whole numbers, nothing random, nothing outside the chain, so every node gets the same result. Every step costs gas; a call that fails (a require, too little gas) changes nothing at all, including coins sent with it.
{ "functions": {
"lock": { "args": ["until"], "body": ["seq",
["require", [">", ["paid", "DZY"], 0], "send the DZY to lock with this call"],
["set", "owner", ["caller"]], ["set", "until", ["arg", "until"]]] },
"withdraw": { "body": ["seq",
["require", ["=", ["caller"], ["get", "owner"]], "only the owner"],
["require", [">=", ["now"], ["get", "until"]], "still locked"],
["transfer", "DZY", ["get", "owner"], ["balance", "DZY"]]] },
"unlocks": { "view": true, "body": ["get", "until"] } } }
| Values | whole numbers, text, true, false, null |
|---|---|
| Operations | + - * / % min max, = != < <= > >= and or not, if seq for let var arg, str len, require fail, get has set del, caller self now height paid balance isaddress, transfer |
| Limits | program 12,000 characters; 200,000 gas a call; loops of at most 1,000 rounds; 10,000 stored values |
| Costs | deploying: 1 DZY to the treasury. Gas: the chain's fee, once, plus once per 1,000 gas; unused gas is paid back |
| Not in version 1 | contracts calling contracts (so re-entrancy attacks cannot exist), making tokens, deleting contracts |
node chain/cli.mjs contract deploy --key me.json --file chain/contracts/vault.json --node https://chain.dznye.com/api
node chain/cli.mjs contract call --key me.json --contract <address> --fn lock --args '[1800000000000]' --pay 5 --node …
node chain/cli.mjs contract view --contract <address> --fn unlocks --node …
Examples to copy: escrow, crowdfund and vault, in chain/contracts/. Ask a contract something for free: GET /api/contract/<address>/view?fn=unlocks&args=[].
Payment channels: instant and free
Two people lock coins in a channel once (channel.open, channel.fund). From then on they pay each other off the chain, instantly and for free, by both signing each new state: { nonce, a, b }, the amounts each side would get. They settle on the chain once:
Together
channel.close with the newest state both signed: paid out at once.
Alone
channel.start with the newest state (or the deposits). The other side has a day to show a newer one with channel.update; then anyone sends channel.settle.
Cheating fails
Closing on an old state only works if the other side never shows up within the day. Keep the newest state, and check in.
node chain/cli.mjs channel open --key alice.json --to <bob> --amount 10 --node …
node chain/cli.mjs channel sign --key alice.json --id <id> --nonce 7 --a 6 --b 4 # both sign; swap signatures
node chain/cli.mjs channel close --key bob.json --id <id> --nonce 7 --a 6 --b 4 --sig-a … --sig-b … --node …
Keep a copy of the chain
No single place should hold the only copy. Every copy can be checked against the chain's own fingerprints, so a copy from anywhere is safe to use.
The archive: the whole chain as plain files, at /archive/index.json. Make your own and keep it anywhere (a folder, a USB stick, a git repository, a web server, IPFS):
node chain/archive.mjs update --node https://chain.dznye.com/api --out ./dznye-archive
The swarm: /api/peers lists the nodes known to hold this chain. A follower can copy from several sources at once (nodes and archives), finds more through them, and raises an alert if any of them shows a different history:
node chain/node/server.mjs --follow https://chain.dznye.com/archive/index.json,https://chain.dznye.com/api --data ./dznye-data
# with a public address, others learn of your node too:
PUBLIC_URL=https://my-node.example.org node chain/node/server.mjs --follow https://chain.dznye.com/api --host 0.0.0.0
Check it yourself, or help run it
A follower holds no key: it copies every block, checks every rule and signature, and raises an alert if anything is wrong.
node chain/node/server.mjs --follow https://chain.dznye.com/api --data ./dznye-data
A validator stakes at least 1,000 DZY and runs a node with its key and the other validators as peers. The whole process, from keys to upgrades, is in the runbook (chain/launch/RUNBOOK.md).
Watching any set of nodes: node chain/launch/check.mjs https://chain.dznye.com/api --genesis dznye-1.genesis.json