How to issue and mine your own dMint token¶
Who this page is for: anyone who wants to launch a permissionless,
PoW-mined fungible token on Radiant and mine the first claim from it —
end to end, from the command line, on testnet (no real value at risk).
A dMint token has no central issuer: you deploy a contract that pays a fixed
FT reward to whoever finds a valid proof-of-work nonce, and anyone (including
you) mines claims from it until it’s exhausted. This guide deploys with no
premine — every unit has to be mined. If you want an allocation issued
alongside the mineable supply, --premine <photons> adds one, and
--premine-to <address> chooses who gets it; see
Premine.
This is the issuance counterpart to Mint from a V1 dMint contract (which mines an existing mainnet contract via the library). Here you deploy your own contract and mine it with two CLI commands. For the byte-level theory, see the V1 dMint deploy concept page.
Note
Both commands broadcast real transactions, so they need a wallet and an ElectrumX endpoint. Do this on testnet first (this guide) — deploying your own contract is the one dMint flow that doesn’t require mainnet. Every broadcast is gated; see the broadcast gate.
TL;DR — three commands¶
# 1. scaffold the token metadata
pyrxd glyph init-metadata --type dmint-ft --out token.json
# (edit token.json: set ticker, name, decimals)
# 2. deploy the contract (commit -> reveal); prints the token_ref + contract outpoints
pyrxd --network testnet glyph deploy-dmint token.json --max-height 100 --reward 1000
# 3. mine + claim one reward from a contract it printed
pyrxd --network testnet glyph claim-dmint --contract <REVEAL_TXID>:0
Step 2 genesises a 1-photon singleton contract; step 3 PoW-mines a
nonce and pays you --reward photons of the FT. Repeat step 3 (you or
anyone) up to --max-height times.
Prerequisites¶
pyrxd installed —
pip install pyrxdand a created wallet (pyrxd wallet new). See your first Radiant transaction.A funded testnet wallet + a testnet ElectrumX endpoint. Follow Use the public Radiant testnet to run
radiantd -testnet, point pyrxd at it, and get testnet coins. The examples below pass--network testnet; set--electrumx wss://HOST:PORT(or put it in your config) so pyrxd knows where to broadcast.A few thousand testnet photons in a single UTXO — the deploy funds the contract carriers + fees, and the claim funds the FT reward + fee.
Step 1 — scaffold the metadata¶
pyrxd glyph init-metadata --type dmint-ft --out token.json
This writes a dmint-ft template with "protocol": ["FT", "DMINT"]
already set (a dMint deploy rejects anything else). Edit the file to set
your ticker, name, and decimals.
Step 2 — deploy the contract¶
pyrxd --network testnet glyph deploy-dmint token.json \
--max-height 100 \
--reward 1000 \
--num-contracts 1 \
--difficulty 1
Flag |
Meaning |
|---|---|
|
claims allowed per contract (total supply = |
|
photons of the FT paid per successful claim, up to Radiant’s money supply (2.1×10^18) |
|
parallel contracts to genesis (1–250); each is an independent mining lane |
|
initial PoW difficulty (1 = easiest; start here on testnet), up to 2^63−1 |
These upper bounds are the points past which a contract built from the value could never
be minted — a state number the covenant cannot read (wider than 8 bytes), a reward no
transaction can pay, or a difficulty whose target is 0 — and deploy-dmint refuses anything
outside them before it touches your wallet, naming the flag. They are the same for V1 and V2,
except that V1’s --max-height stops at 2^31 (below).
--target-time (V2) is at most 0xFFFFFFFF seconds in the ASERT, LWMA and EPOCH modes, whose
retarget compares it with the gap between two timestamps that are each below 2^31 in any
mint the covenant accepts, so a larger value is a spacing no mint can meet. FIXED and SCHEDULE never read
it as a number; there it only has to fit pyrxd’s 8-byte encoder (up to 2^63−1).
V1’s --max-height stops at 2^31 because a V1 contract stores its height in 4 bytes: every
mint but the last writes the next height with NUM2BIN(height + 1, 4), which cannot hold
2^31, so a contract with a larger --max-height would stop at height 2^31−1 with mints it can
never make. Mainnet has such V1 deploys (Photonic builds them). claim-dmint mints them like
any other up to height 2^31−1 and refuses the mint from there; deploy-dmint will not create
one.
V1 vs V2 (adaptive difficulty)¶
deploy-dmint deploys V1 by default — the established mainnet format,
fixed difficulty. Pass --v2 for a V2 contract with a difficulty algorithm
(--daa-mode fixed|asert|lwma|epoch|schedule). V2 is consensus-validated on
regtest and Radiant mainnet; it requires an explicit --v2 opt-in as the
newer format. Examples:
# LWMA adaptive difficulty (retargets every block)
pyrxd glyph deploy-dmint token.json --v2 --daa-mode lwma --target-time 60 --max-height 100 --reward 1000
# EPOCH (periodic retarget; difficulty >= 32768 so target <= 2^48)
pyrxd glyph deploy-dmint token.json --v2 --daa-mode epoch --epoch-length 2016 --max-adjustment 4 --difficulty 32768 --max-height 100 --reward 1000
# SCHEDULE (pre-baked difficulty curve: [height, difficulty] pairs)
pyrxd glyph deploy-dmint token.json --v2 --daa-mode schedule --schedule '[[100, 4], [1000, 8]]' --max-height 2000 --reward 1000
EPOCH note. EPOCH was briefly disabled while its canonical Photonic bytecode had an int64-overflow that bricked the contract on-chain. That fix is now merged upstream (
Radiant-Core/Photonic-Wallet#2— divide-first with the target clamped to 2^48 on both sides of the retarget multiply) and pyrxd byte-matches it, so EPOCH deploy is re-enabled. EPOCH requires--difficulty >= 32768(the 2^48 target cap) and a--target-timeat least the--max-adjustmentfactor (e.g.>= 4for the default4).
--last-time(ASERT and LWMA). The deployed state carries alastTimeslot — the baseline the first retarget measures against — and those two modes read it as a script number on the very first mint.deploy-dmintstamps the deploy time by default, which is what Photonic’s own deploy does, so you normally never pass--last-time. If you do pass it, it must be a real Unix timestamp: the state pushes it as a fixed four-byte value, and anything below 2^23 (including 0) is not a minimally encoded script number. MINIMALDATA is in Radiant’s mandatory script-verify flags — consensus, not mempool policy — so such a contract aborts on its first retarget and can never be mined. The deploy is refused rather than built, because nothing can fix it once the reveal confirms. Values above0x7FFFFFFFare refused too.
claim-dmint auto-detects V1 vs V2 from the contract. For an EPOCH or
SCHEDULE V2 contract you must pass the same --epoch-length/--max-adjustment
or --schedule you deployed with (those parameters live in the contract code,
not the on-chain state) — the claim with: line deploy-dmint prints already
includes them. You do not need to pass --half-life for an ASERT contract:
the claim reads the baked half-life out of the contract’s own bytecode. Pass it
only to assert what you expect — a value that disagrees with the baked one fails
fast, naming the baked value, before the PoW grind.
--current-time is the mint’s locktime; leave it unset. It defaults to the
wall-clock time when the claim is built, which is what you want. If you do pass
it, pass a real Unix timestamp at or after the contract’s lastTime. The claim
refuses, before any mining, a mint pyrxd will not build — for example one whose
contract can no longer be minted — and says why.
The command builds a commit transaction (an FT-commit hashlock plus
K ref-seed outputs), waits for it to confirm, then builds the
reveal that genesises the contracts. On success it prints the
token_ref and one outpoint per contract:
dMint contract deployed!
commit txid: ab12…
reveal txid: cd34…
token_ref: ab12…:0
contracts (1):
cd34…:0
total supply: 100000 photons
claim with: glyph claim-dmint --contract cd34…:0
Each contract output is exactly 1 photon — that’s a consensus
requirement of the covenant, not a quirk; pyrxd enforces it. Keep the
token_ref and the contract outpoints; they’re how you (and anyone
else) mine the token.
Step 3 — mine and claim a reward¶
pyrxd --network testnet glyph claim-dmint --contract cd34…:0
You can also pass --token-ref ab12…:0 to auto-discover a live (un-exhausted)
contract for the token. The command:
fetches the contract and its current state,
funds the mint from your wallet (the FT reward + change go to
--reward-address, defaulting to your largest-UTXO address),PoW-mines a SHA256d nonce that satisfies the contract target,
splices the nonce into the spend, signs the funding input, and broadcasts.
On success it prints the mint txid; the contract is recreated at
height + 1 for the next claim. The exception is the contract’s last mint
(height + 1 == max_height): the covenant requires that mint to burn the
contract output (d8 <contractRef> 6a in its place) rather than recreate it,
so nothing can mint that contract afterwards. claim-dmint builds it that way,
says so in the summary it prints before mining, and reports final_mint in
its --json result.
Mining notes¶
Size the job first.
pyrxd glyph dmint-estimate --contract <TXID>:<VOUT>benchmarks this machine’s SHA256d rate and prints the expected attempts and an ETA. It keeps them apart on purpose: the hash rate is MEASURED, the attempt counts are EXACT (2**96 / target), and every ETA is PROJECTED — the aggregate rate assumes linear scaling across workers, which is not measured. Add--jsonfor the same split machine-readably.pyrxd glyph dmint-estimate --difficulty 1 # offline, no wallet needed
Read the ETA as a distribution, not a countdown. Mining is memoryless: hashes already spent do not shorten what remains. That is why the output is a mean plus p50/p90/p99, and why the live progress during a claim does not tick down.
It uses the bundled parallel miner by default, across your CPU cores, with live hash rate + remaining-time quantiles on stderr (
--no-progressto silence,--workers Nto change the pool). Point--miner-cmd "/path/to/glyph-miner …"at a GPU miner for seconds instead of minutes — whether you see live progress from it depends on the miner: the wire protocol carries OPTIONAL progress frames on stderr (seedocs/concepts/parallel-mining.md), so an updated external miner streams the same way; an older one just stays silent until it finishes, which still works.--miner-cmd in-processforces the slow single-threaded miner.V1’s nonce is only 4 bytes, so any single sweep has roughly a 39% chance of containing a valid nonce.
claim-dminthandles this the way real miners do — it rerolls an internal field and re-mines on exhaustion (up to--max-rerolls). Rerolling does not change the estimate: the total attempts you need are the same whether they are spent in one sweep or ten.--timeoutcaps each individual grind.The signed mint hex is echoed to stderr before broadcast, so a dropped connection is recoverable (re-broadcast the hex).
The broadcast gate¶
Every broadcast (the commit, the reveal, and the mint) is shown and
confirmed first. In an interactive shell you get a y/N prompt; in
scripts, pass --json --yes to skip the prompt — --json requires
--yes, so a script can never broadcast unconfirmed. The claim confirms
once, before the multi-minute grind (all the value facts are known
then), so an unattended --json run fails fast rather than blocking on a
prompt after the mine.
Going to mainnet¶
The same two commands run against mainnet — drop --network testnet
(mainnet is the default) and point --electrumx at a mainnet endpoint.
On mainnet the reward photons are a real Glyph FT and the transactions
cost real RXD, so deploy with the parameters you actually want and
double-check each confirmation prompt. To mine an existing mainnet
dMint token (e.g. GLYPH) rather than your own, see
Mint from a V1 dMint contract.