Reproduce it
On any Linux machine with curl and python3. Takes about a minute, downloads only the official Solidity compiler binary.
mkdir -p rbtx && cd rbtx
curl -sL -o solc https://github.com/ethereum/solidity/releases/download/v0.8.20/solc-static-linux
chmod +x solc
curl -sL -o BitcoinRXC.sol https://bitcoinrxc.com/BitcoinRXC.sol
./solc --evm-version london --optimize --optimize-runs 200 --bin-runtime BitcoinRXC.sol \
| grep -A1 "Binary of the runtime part" | tail -1 > local.hex
curl -s -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_getCode","params":["0xFcf33b5bd3Ec789cCc05Ba04388B309224eD5065","latest"],"id":1}' \
https://rpc.richxchain.com \
| python3 -c "import sys,json;print(json.load(sys.stdin)['result'][2:])" > chain.hex
python3 - <<'EOF'
l=open('local.hex').read().strip().lower()
c=open('chain.hex').read().strip().lower()
n=300
print('MATCH' if l[:n]==c[:n] else 'MISMATCH')
print('local ',l[:80])
print('chain ',c[:80])
EOF
The script downloads solc 0.8.20, compiles the source saved beside it, reads the live bytecode straight from the public RPC, and prints whether they match. It does not contact this website for the comparison.
What the code does not contain
Read it below and confirm for yourself. There is no owner variable, no onlyOwner modifier, no administrative mint, no pause, no blacklist, no fee switch, no proxy, no delegatecall and no selfdestruct. The only function that creates units is mint(uint256 nonce, bytes32 challengeDigest), which is callable by anyone and only succeeds when a valid proof of work is supplied.
Because there is no upgrade path, this is permanent. That cuts both ways and the site says so plainly: bugs cannot be patched either.
Source — BitcoinRXC.sol
// SPDX-License-Identifier: MIT
//
// ██████╗ ██╗████████╗ ██████╗ ██████╗ ██╗███╗ ██╗ ██████╗ ██╗ ██╗ ██████╗
// ██╔══██╗██║╚══██╔══╝██╔════╝██╔═══██╗██║████╗ ██║ ██╔══██╗╚██╗██╔╝██╔════╝
// ██████╔╝██║ ██║ ██║ ██║ ██║██║██╔██╗ ██║ ██████╔╝ ╚███╔╝ ██║
// ██╔══██╗██║ ██║ ██║ ██║ ██║██║██║╚██╗██║ ██╔══██╗ ██╔██╗ ██║
// ██████╔╝██║ ██║ ╚██████╗╚██████╔╝██║██║ ╚████║ ██║ ██║██╔╝ ██╗╚██████╗
// ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝
//
// Bitcoin RXC ($RBTX) — RichX Chain, chain ID 20260903
//
// ============================================================
// WHAT THIS IS
// ============================================================
// Bitcoin is open source. This contract implements the part of
// Satoshi Nakamoto's design that almost no token bothers to copy:
// issuance by proof of work.
//
// There is no premine. There is no allocation. Nobody was given
// any RBTX, including the people who deployed this contract. The
// only way a coin comes into existence is that somebody found a
// hash below the target and paid the gas to submit it.
//
// - 21,000,000 hard cap, compiled in as a constant
// - 5 RBTX per claim, halving every 2,100,000 claims
// - difficulty adjusts itself toward one claim per minute
// - 8 decimals, so the smallest unit is a satoshi
//
// ============================================================
// WHAT THIS CONTRACT CANNOT DO
// ============================================================
// There is no owner. Not a restricted owner — no owner variable
// exists anywhere in this file. Which means there is:
//
// NO mint by decree NO pause NO freeze
// NO blacklist NO seize NO fee
// NO upgrade path NO selfdestruct NO admin of any kind
//
// Once deployed, nobody controls this contract. Not an exchange,
// not a government, not the deployer. Nobody can stop a transfer,
// reverse one, or take a balance.
//
// That is not a feature that was added. It is code that was not
// written.
//
// The cost of that is real and is stated plainly: nothing here can
// ever be undone. Coins sent to a wrong address are gone. If a flaw
// is found there is no emergency stop. These are the same terms
// Bitcoin has always had.
//
// ============================================================
// A LIMITATION THAT MUST BE STATED
// ============================================================
// RichX Chain is proof of authority. Its validator set produces
// blocks, and block producers have some influence over the values
// this contract mixes into each challenge.
//
// This contract reduces that influence as far as a contract can:
// the challenge is derived from the PREVIOUS successful claim —
// its solver, its nonce and the value they submitted — rather than
// from a block hash a producer could grind. A validator therefore
// cannot cheaply choose the next challenge.
//
// It cannot eliminate the influence entirely. Anyone deciding
// whether to mine should understand that this is proof of work
// running on a permissioned chain, and weigh it accordingly. It is
// written here rather than left for someone to discover.
//
pragma solidity ^0.8.20;
contract BitcoinRXC {
// ---- identity ----
string public constant name = "Bitcoin RXC";
string public constant symbol = "RBTX";
uint8 public constant decimals = 8; // satoshis, as Bitcoin does
// ---- supply ----
// 21,000,000 x 10^8. A constant: compiled into the bytecode, with no
// storage slot behind it and therefore no function that could raise it.
uint256 public constant MAX_SUPPLY = 21_000_000 * 1e8;
uint256 public totalSupply; // rises only via mint()
// ---- issuance schedule ----
uint256 public constant INITIAL_REWARD = 5 * 1e8; // 5 RBTX
uint256 public constant CLAIMS_PER_ERA = 2_100_000; // halving interval
// 2,100,000 x 5 x 2 = 21,000,000. The geometric series lands exactly
// on the cap, the same way Bitcoin's does.
// ---- difficulty ----
uint256 public constant TARGET_SECONDS_PER_CLAIM = 60; // one claim a minute
uint256 public constant RETARGET_INTERVAL = 512; // claims between adjustments
uint256 public constant MAX_ADJUST_NUMERATOR = 4; // never move more than 4x
uint256 public constant MIN_ADJUST_DENOMINATOR = 4; // ...or less than a quarter
// The easiest the network may ever become. Prevents difficulty collapsing
// to the point where a claim is free.
uint256 public constant MAX_TARGET =
0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
// ---- mining state ----
uint256 public miningTarget; // a solution must hash below this
bytes32 public challengeNumber; // what miners are currently working on
uint256 public claimsMined; // total successful claims, ever
uint256 public claimsThisEra; // claims since the last halving
uint256 public retargetStartTime; // when the current retarget window began
uint256 public retargetStartClaims; // claims at the start of that window
uint256 public lastClaimTime;
// Every solved challenge is recorded so a solution can never be replayed.
mapping(bytes32 => bytes32) public solutionForChallenge;
// ---- ledgers ----
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
// ---- events ----
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event Mint(address indexed miner, uint256 reward, uint256 claimNumber, bytes32 newChallenge);
event Retarget(uint256 previousTarget, uint256 newTarget, uint256 secondsPerClaim);
event Halving(uint256 era, uint256 newReward);
// ---- errors ----
error InsufficientBalance();
error NotApproved();
error ZeroAddress();
error SolutionBelowTarget(); // the hash was not small enough
error ChallengeAlreadySolved(); // someone got there first
error SupplyExhausted(); // all 21,000,000 have been mined
constructor() {
// No allocation of any kind. totalSupply starts at zero and the
// deployer receives nothing. Verify by reading balanceOf on the
// deploying address immediately after deployment: it is zero.
miningTarget = MAX_TARGET;
retargetStartTime = block.timestamp;
lastClaimTime = block.timestamp;
retargetStartClaims = 0;
// The first challenge is fixed by the deployment itself and cannot
// be known before the contract exists.
challengeNumber = keccak256(abi.encodePacked(
"Bitcoin RXC genesis", block.timestamp, block.number, address(this)
));
}
// ============================================================
// MINING
//
// A miner searches off chain for a nonce such that
//
// keccak256(challengeNumber, msg.sender, nonce) < miningTarget
//
// The miner's own address is part of the hash, so a solution found by
// one person cannot be stolen and submitted by another — it is only
// valid from the address it was mined for.
// ============================================================
function mint(uint256 nonce, bytes32 challengeDigest) external returns (bool) {
if (totalSupply >= MAX_SUPPLY) revert SupplyExhausted();
bytes32 challenge = challengeNumber;
bytes32 digest = keccak256(abi.encodePacked(challenge, msg.sender, nonce));
// The submitted digest must match what we compute. This is a
// convenience check that catches a miner using stale state.
if (digest != challengeDigest) revert SolutionBelowTarget();
if (uint256(digest) > miningTarget) revert SolutionBelowTarget();
if (solutionForChallenge[challenge] != bytes32(0)) revert ChallengeAlreadySolved();
solutionForChallenge[challenge] = digest;
uint256 reward = currentReward();
// The final claim pays only what remains, so the cap is exact.
if (totalSupply + reward > MAX_SUPPLY) {
reward = MAX_SUPPLY - totalSupply;
}
unchecked {
claimsMined += 1;
claimsThisEra += 1;
totalSupply += reward;
balanceOf[msg.sender] += reward;
}
lastClaimTime = block.timestamp;
if (claimsThisEra >= CLAIMS_PER_ERA) {
claimsThisEra = 0;
emit Halving(currentEra(), currentReward());
}
_maybeRetarget();
// The next challenge comes from this claim, not from a block hash.
// A block producer cannot cheaply steer it.
challengeNumber = keccak256(abi.encodePacked(
digest, msg.sender, nonce, claimsMined
));
emit Mint(msg.sender, reward, claimsMined, challengeNumber);
emit Transfer(address(0), msg.sender, reward);
return true;
}
// ============================================================
// DIFFICULTY
//
// Every RETARGET_INTERVAL claims, compare how long that window actually
// took against how long it should have taken, and move the target
// proportionally. Movement is clamped to 4x in either direction so a
// burst of hashrate — or a lull — cannot swing the network wildly.
// ============================================================
function _maybeRetarget() private {
uint256 sinceRetarget = claimsMined - retargetStartClaims;
if (sinceRetarget < RETARGET_INTERVAL) return;
uint256 elapsed = block.timestamp - retargetStartTime;
if (elapsed == 0) elapsed = 1;
uint256 expected = RETARGET_INTERVAL * TARGET_SECONDS_PER_CLAIM;
uint256 previous = miningTarget;
uint256 next;
if (elapsed < expected) {
// Claims arriving too fast: make it harder by lowering the target.
uint256 ratio = expected / elapsed;
if (ratio > MAX_ADJUST_NUMERATOR) ratio = MAX_ADJUST_NUMERATOR;
next = previous / ratio;
if (next == 0) next = 1;
} else {
// Too slow: make it easier by raising the target.
uint256 ratio = elapsed / expected;
if (ratio > MIN_ADJUST_DENOMINATOR) ratio = MIN_ADJUST_DENOMINATOR;
next = previous * ratio;
if (next > MAX_TARGET) next = MAX_TARGET;
}
miningTarget = next;
retargetStartTime = block.timestamp;
retargetStartClaims = claimsMined;
emit Retarget(previous, next, elapsed / RETARGET_INTERVAL);
}
// ============================================================
// READ
// ============================================================
/// Which halving era we are in. Era 0 pays 5 RBTX, era 1 pays 2.5, and so on.
function currentEra() public view returns (uint256) {
return claimsMined / CLAIMS_PER_ERA;
}
/// The reward a successful claim pays right now.
function currentReward() public view returns (uint256) {
uint256 era = currentEra();
if (era >= 40) return 0; // below one satoshi
return INITIAL_REWARD >> era; // halve once per era
}
/// How many claims remain before the next halving.
function claimsUntilHalving() external view returns (uint256) {
return CLAIMS_PER_ERA - claimsThisEra;
}
/// How much RBTX has never been mined and is still available.
function remainingSupply() external view returns (uint256) {
return MAX_SUPPLY - totalSupply;
}
/// Difficulty expressed the way miners expect it: how many hashes,
/// on average, one solution is expected to cost.
function miningDifficulty() external view returns (uint256) {
return MAX_TARGET / miningTarget;
}
/// Everything a miner needs, in one call.
function miningParameters() external view returns (
bytes32 challenge,
uint256 target,
uint256 reward,
uint256 era,
uint256 claims,
uint256 remaining
) {
return (
challengeNumber,
miningTarget,
currentReward(),
currentEra(),
claimsMined,
MAX_SUPPLY - totalSupply
);
}
/// Check a candidate solution without spending gas on a transaction.
function checkSolution(address miner, uint256 nonce) external view returns (bool ok, bytes32 digest) {
digest = keccak256(abi.encodePacked(challengeNumber, miner, nonce));
ok = uint256(digest) <= miningTarget;
}
// ============================================================
// RXC-20 (fully compatible with the ERC-20 standard)
//
// Plain transfers. No fee, no limit, no pause, no blacklist.
// Nobody can stop, reverse or intercept one.
// ============================================================
function transfer(address to, uint256 amount) external returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed < amount) revert NotApproved();
if (allowed != type(uint256).max) {
unchecked { allowance[from][msg.sender] = allowed - amount; }
}
_transfer(from, to, amount);
return true;
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function _transfer(address from, address to, uint256 amount) private {
if (to == address(0)) revert ZeroAddress();
uint256 bal = balanceOf[from];
if (bal < amount) revert InsufficientBalance();
unchecked {
balanceOf[from] = bal - amount;
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
}
// ============================================================
// BURN
//
// Any holder may destroy their own coins. Nobody can burn anyone
// else's. Burned supply is not re-mineable: MAX_SUPPLY caps what
// mint() has issued, not what currently circulates, so destroyed
// coins are gone permanently.
// ============================================================
function burn(uint256 amount) external {
uint256 bal = balanceOf[msg.sender];
if (bal < amount) revert InsufficientBalance();
unchecked {
balanceOf[msg.sender] = bal - amount;
}
// totalSupply is deliberately NOT reduced. It records how much has
// ever been mined, which is what the cap is measured against.
emit Transfer(msg.sender, address(0), amount);
}
}