The Builder Lab
From an empty machine to a verified contract on the development network
No account, no money, nothing to buy: the faucet gives 25 test IGN an address a day, worth nothing anywhere; "verified" here means the bytecode the chain holds equals a recompilation of the published source, checked in your own hands over the public RPC and again by the explorer. The network is devnet9, the public development network: development-network tokens, no monetary value, the chain may be reset with notice. The one command and the served files are at https://build.igneum.network/lab/ (the script, SOURCE and SHA256SUMS); this page is their guide.
Source: BUILDER-LAB.md on main (the content is the builder's; this page is its presentation); the runs under build/records/
Builder seat, 12 October 2026. One public path, every command executable as written, nothing that needs a person from the team. In plain words: no account, no money, nothing to buy; the faucet gives 25 test IGN an address a day and they are worth nothing anywhere; "verified" on this page means the bytecode the chain holds equals a recompilation of the published source, checked in your own hands over the public RPC and again by the explorer. The network is devnet9, the public development network: development-network tokens, no monetary value, the chain may be reset. This page is the source of the lab served at https://build.igneum.network/lab/ and of the one command. The script is dev/lab/igneum-lab in the repository and https://build.igneum.network/lab/igneum-lab as served; the two are the same file. The repository is not public yet: "main" and a "sha" on this page name a commit in Igneum Labs' repository, which the team can show on request and which becomes a link the day the repository is published; until then the served files, SOURCE and SHA256SUMS at https://build.igneum.network/lab/ are the public record, and a sha is a name, not something to click.
Status line: the path RAN END TO END on the public network on 12 October 2026 (section 9: lab-run-1 from a team box and lab-run-pod-1 from a stock Ubuntu 24.04 machine outside the mesh, both from the published script, every hash stored); the independent reproduction (the execution team, lab-run-2) passed; every row so far is the team's (on team boxes or on clean machines, labelled), and an agent's run on a clean machine is not an outsider's: the outsider column stays empty until developers unrelated to Igneum Labs run it.
0. What you need
An empty machine with a shell. Ubuntu 24.04 on x86_64 is the tested form (a cloud VM or a container). The three tools the lab cannot install for you, because they need the package manager; as root (a container's shell, where sudo is often not installed) or with sudo in front of each command as an ordinary user:
apt-get update && apt-get install -y curl git ca-certificates
Nothing else. No account, no key from anyone, no address on a private network. The lab installs Foundry under ~/.foundry and Node 22 under ~/.igneum-lab if the machine has neither; the script itself uses no sudo and writes only under your home directory and the directory you run it in.
1. The one command
The lab's address is https://build.igneum.network/lab/ with the slash (the earliest independent run found that /lab without it answered 404; a redirect is reliability's, and every line here names the full path). The two-step form, the one to prefer: download the script, check its sha256 against the published sum, read it, then run it. Piping a download into a shell is the habit every outsider is told to distrust, and this page does not teach it; the quick form below is the same file for a machine you will throw away.
curl -fsSLO https://build.igneum.network/lab/igneum-lab curl -fsSL https://build.igneum.network/lab/SHA256SUMS | grep ' igneum-lab$' | sha256sum -c - bash igneum-lab
Every file at https://build.igneum.network/lab/ is main's at the sha named in https://build.igneum.network/lab/SOURCE (which also carries each served file's sha256, so a SOURCE that moves while a sum stays says the file did not change), copied from the repository's mirror by dev/lab/publish.sh and never from a working tree (the rule since the earliest independent run found a served script that matched no sha on main). The sum this page was written against: ba9e4dcae6f43e7cb0d991ffb4f1d5ef943e2ea946d66650609deaac33937231 igneum-lab (also dev/lab/SHA256SUMS in the repository at this page's sha; a page and a sum that differ mean one of them moved and the page's sha says which). The quick form, for a throwaway machine:
curl -fsSL https://build.igneum.network/lab/igneum-lab | bash
Either form runs sections 2 to 7 below in order and prints each step's transaction hash as it goes. It writes everything it did to ./igneum-lab-run/: record.json (every address, hash, block and the compiler settings), steps.tsv (each step with its UTC time), key (the throwaway key, mode 600), foundry/ and hardhat/ (the two projects it made), standard-input.json (the compiler's standard JSON input of the verified contract) and deployed-bytecode.hex. Run it again and it reuses the key and the installed tools; a step that fails names its cause and exits with a code (2 the machine, 3 the faucet, 4 the chain, 5 verification, 6 a transaction sent and not included within ten minutes: the hash is in the record and steps.tsv; run bash igneum-lab run again once the chain moves and it reuses the key, takes no second drip, and carries on from a fresh creation).
The same script runs one step at a time: bash igneum-lab check (the machine and the chain, no transaction), faucet, foundry, verify, hardhat, read. It is about 360 lines of bash; read it before running it.
2. The chain
| name | Igneum devnet9 (the development network) |
| chain id | 7319 (network id 7391) |
| currency | IGN, 18 decimals, test coins, no monetary value |
| public RPC | https://rpc.igneum.network (eth reads and eth_sendRawTransaction on the same host, net, web3, the igneum reads; 20 calls a second per source) |
| faucet | https://build.igneum.network/devnet9/faucet (POST {"address": "0x..."}; GET /state) |
| explorer | https://explorer.igneum.network (Blockscout: /tx/[hash], /address/[address], /block/[number]; JSON at /api/v2/; source verification at /api/v2/smart-[address]/verification/... and the Etherscan-form /api) |
| genesis hash | 0xeae5e6083195fe58658b1f13771362b19a38a0bffb5c8808d458993ee9f54ea1 (read from block 0 over the public RPC, 12 October 2026 01:5x UK; the chain's record is chain/devnet9/; read it yourself: cast block 0 --rpc-url https://rpc.igneum.network --field hash) |
| EVM | In one sentence: a Solidity contract compiled with evm_version = cancun runs here unchanged unless it uses blob transactions or the blob opcodes (there are none on Igneum) or reads PREVRANDAO (which returns the block's difficulty here). The exact list: Igneum EVM-1 is Shanghai plus EIP-1153, 5656 and 6780, no blob opcodes, no EIP-4399 (dev/solidity-target.md) |
| blocks | a 20-second target (the chain's record, ACTIVATION-1.md); measured 21.2 s a block over blocks 148 to 248 on 12 October 2026; EIP-1559 fees |
Pin the chain by its genesis hash, never by the chain id alone: every Igneum development chain before devnet7 carried the same id, and a re-sealed devnet9 under id 7319 is a different chain with a different hash. The lab reads eth_getBlockByNumber("0x0") earliest and refuses any hash but the one above; a reset devnet9 means a re-pinned script and a new sum on this page.
Add it to a wallet with these fields, or let the Igneum Wallet read it out of the box:
chainId 0x1c97 chainName "Igneum devnet9 (development network)" rpcUrls ["https://rpc.igneum.network"] nativeCurrency {name: "Igneum", symbol: "IGN", decimals: 18} blockExplorerUrls ["https://explorer.igneum.network"]
What the public RPC does not serve: debug_*, admin_*, personal_*, eth_sendTransaction, eth_sign. A node sees the addresses you ask about and can lie about balances and history; it cannot spend. Run your own node when it matters (dev/rpc.md).
3. The toolchain (what the one command does on an empty machine)
curl -fsSL https://foundry.paradigm.xyz | bash && ~/.foundry/bin/foundryup # Foundry: forge, cast, anvil export PATH="$HOME/.foundry/bin:$PATH" forge --version
Node 20 or later for Hardhat. The lab fetches the current Node 22 linux-x64 tarball from nodejs.org into ~/.igneum-lab when the machine has no Node 20 or later; any other install of Node 20 or later serves as well.
4. Test IGN from the faucet
A throwaway key. The lab makes one from /dev/urandom and keeps it in igneum-lab-run/key; by hand:
cast wallet new # prints an address and a private key; test coins only, never anything of value
The drip: 25 IGN to any address, once an address a day, one an hour from one place, under the faucet's own daily cap. The faucet is a contract (DevFaucet.sol: drip(to) callable by anyone, no owner, no pause) and a relay that pays the gas for an address that has nothing (faucet-relay.py).
curl -s -X POST -H 'Content-Type: application/json' --data '{"address":"0xYOURADDRESS"}' \ https://build.igneum.network/devnet9/faucet
{"hash": "0x...", "balance_wei": "...", "balance_ign": "...", "label": "development-network tokens; no monetary value", ...}
cast receipt 0xTHEHASH blockNumber --rpc-url https://rpc.igneum.network # waits for the block cast balance 0xYOURADDRESS --rpc-url https://rpc.igneum.network --ether
A 429 is the limit, with next_at for the address's next drip; a 502 is the chain or the relay, with its error. The faucet's state (its balance, what is left today, the contract's address) is GET .../devnet9/faucet/state.
5. Deploy with Foundry
The reference contract is the developer kit's TestToken (TestToken.sol: an ERC-20 on OpenZeppelin 5.7.0 that anyone mints up to 1,000 per call, worthless by design). The lab writes the source and this configuration into igneum-lab-run/foundry/; the same by hand:
mkdir -p lab/src lab/lib && cd lab git clone --depth 1 --branch v5.7.0 https://github.com/OpenZeppelin/openzeppelin-contracts lib/openzeppelin-contracts cat > foundry.toml <<'T' [profile.default] src = "src" out = "out" libs = ["lib"] solc_version = "0.8.28" evm_version = "cancun" optimizer = true optimizer_runs = 200 remappings = ["@openzeppelin/contracts/=lib/openzeppelin-contracts/"] T curl -fsSL https://build.igneum.network/lab/TestToken.sol -o src/TestToken.sol forge build cast send --rpc-url https://rpc.igneum.network --private-key 0xYOURKEY --gas-limit 3000000 --async \ --create "$(forge inspect src/TestToken.sol:TestToken bytecode)" # prints the hash at once cast receipt 0xTHEHASH --rpc-url https://rpc.igneum.network --json # waits for the block; contractAddress is the token cast send 0xTOKEN "mint(address,uint256)" 0xYOURADDRESS 1000ether --rpc-url https://rpc.igneum.network --private-key 0xYOURKEY
forge create ... --broadcast does the same in one command, but waits two minutes for the receipt in silence and then says only "contract was not deployed" with no hash (the earliest runs met it on a stalled head); the two-command form keeps the hash whatever the chain does. The lab sends each transaction up to three times ten seconds apart when the public host's front answers a 5xx (the earliest independent run met one 502), each attempt in steps.tsv.
--gas-limit 3000000: a contract creation goes out with its own gas limit, because the node's eth_estimateGas on a creation can answer "method handler crashed" (found 12 October 2026 02:2x UK by the wallet team; the node seats have it); a call to an existing contract estimates as usual, so the mint needs no limit. TestToken's creation takes about 1.3 million gas. evm_version = "cancun" with no blob builtins is the Igneum target (dev/solidity-target.md); TestToken touches none of them. The compiler settings are part of the verification in section 6: change them and the bytecode changes.
6. Verify the source against the chain, then in the explorer
Verification has two halves. Earliest, over the public RPC and in your own hands: the runtime bytecode the chain holds at the address equals the bytecode a recompilation of the published source produces under the recorded settings (ERC20Permit's seven immutables are set at construction, so the comparison masks the slots the artifact's immutableReferences name and compares everything else, the metadata tail included). Second, in the explorer: https://explorer.igneum.network is a Blockscout with its verifier service, it takes the same source and settings, recompiles them itself and shows the contract as verified with its source on the Contract tab; that is what a stranger reading your address sees.
forge inspect src/TestToken.sol:TestToken deployedBytecode > built.hex cast code 0xTOKEN --rpc-url https://rpc.igneum.network > chain.hex diff <(tr 'A-F' 'a-f' < built.hex) <(tr 'A-F' 'a-f' < chain.hex) && echo "bytecode-verified over the public RPC" forge verify-contract --show-standard-json-input 0xTOKEN src/TestToken.sol:TestToken > standard-input.json sha256sum standard-input.json
standard-input.json is what the explorer's verifier takes; the lab stores it and its sha256 in the record. The explorer half, with Foundry's own command against Blockscout's Etherscan-form API:
forge verify-contract --verifier blockscout --verifier-url https://explorer.igneum.network/api/ --chain 7319 --watch \ 0xTOKEN src/TestToken.sol:TestToken curl -s https://explorer.igneum.network/api/v2/smart-0xTOKEN | grep -o '"is_verified":[a-z]*'
is_verified:true and the source on https://explorer.igneum.network/address/0xTOKEN?tab=contract is the explorer's verdict. One thing to know about it: the explorer matches bytecode twins, so once one TestToken with these settings is verified, every later one reads verified the moment it is indexed, before any submission; the lab stores each submission's answer verbatim in steps.tsv and compares the explorer's verified_at with the submission's time, and says which of the two verified the address (the earliest independent run found the Hardhat address verified by the twin three seconds before the lab's submission reached it).
7. Deploy with Hardhat
The lab writes igneum-lab-run/hardhat/; the same by hand:
mkdir -p hh/contracts hh/scripts && cd hh && npm init -y npm install hardhat@2 @nomicfoundation/hardhat-ethers@3 ethers@6 @openzeppelin/contracts@5.7.0 curl -fsSL https://build.igneum.network/lab/TestToken.sol -o TestToken.sol cat > hardhat.config.js <<'J' require("@nomicfoundation/hardhat-ethers"); module.exports = { solidity: { version: "0.8.28", settings: { evmVersion: "cancun", optimizer: { enabled: true, runs: 200 } } }, networks: { igneum: { url: "https://rpc.igneum.network", chainId: 7319, accounts: [process.env.IGNEUM_KEY] } }, }; J curl -fsSL https://build.igneum.network/lab/deploy.js -o scripts/deploy.js IGNEUM_KEY=0xYOURKEY npx hardhat run scripts/deploy.js --network igneum
{"deployer":"0x...","address":"0xTOKEN2","deploy_tx":"0x...","deploy_block":N,"mint_tx":"0x...","mint_block":N,"bytecode_match":true,"name":"Igneum Test Token","total_supply":"1000000000000000000000"}
deploy.js deploys (with gasLimit: 3000000, for the reason in section 5), mints 1,000 TIGN to the deployer, then reads eth_getCode and compares it with the artifact's deployedBytecode: the earliest half of section 6 against Hardhat's own build. The key goes in the environment of that one command and nowhere else. The explorer half for this deployment posts Hardhat's own standard JSON input (the input of artifacts/build-info/[hash].json, its solcLongVersion as the compiler) to the explorer's API:
curl -s -X POST https://explorer.igneum.network/api/v2/smart-standard-input \ -F compiler_version=v0.8.28+commit.7893614a -F contract_name=TestToken -F license_type=mit \ -F autodetect_constructor_args=true -F 'files[0]=@standard-input.json;type=application/json' curl -s https://explorer.igneum.network/api/v2/smart-0xTOKEN2 | grep -o '"is_verified":[a-z]*'
8. Read it back over the public RPC
cast call 0xTOKEN 'name()(string)' --rpc-url https://rpc.igneum.network cast call 0xTOKEN 'totalSupply()(uint256)' --rpc-url https://rpc.igneum.network cast call 0xTOKEN 'balanceOf(address)(uint256)' 0xYOURADDRESS --rpc-url https://rpc.igneum.network curl -s -X POST -H 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"0xTOKEN","data":"0x06fdde03"},"latest"]}' \ https://rpc.igneum.network
The last one is the chain with nothing in between: 0x06fdde03 is name(), and the hex in result decodes to "Igneum Test Token". The explorer shows the same transactions at https://explorer.igneum.network/tx/[hash] and the contract at /address/[address].
9. The measure and the runs
The measure, BL-1, registered: GUARD-C-CLOSURE.md section 7, BL-1, main 8305deec. In short: one developer unrelated to Igneum Labs (until real outsiders exist, one agent on a clean throwaway machine from a stock image, acting only from the public text and labelled "agent on a clean machine, not an outsider"; never counted toward the outsider condition); the allowed materials are this page as served, the lab's files, the public endpoints and the public package registries; any mesh address, private RPC, hand-issued balance, word from a team member or file not reachable from the public text is a FAIL of the row by itself; the task is sections 0 to 8; the measure is the wall-clock minutes from the earliest command to the last read and the list of steps that failed from the text; a row PASSES when every step completed within 60 minutes with every transaction hash stored under build/records/lab-run-[n]-[who]/ and read by the independent reader (the execution team; the site lead presentation only); the measure PASSES on three rows on three separate clean machines; FAIL rows are retained and the documentation corrected from them with the correcting sha named in the row; the 60 minutes is a ceiling, not a target, and the row stores the actual minutes.
| run | who | machine | started (UK) | minutes | result | record |
|---|---|---|---|---|---|---|
| lab-run-1 | the builder team (the team; not a reproduction; the path's own earliest run) | build-4 (a team box with Foundry and Node already installed) | 02:03 | 36 wall-clock (three attempts: the unfunded relayer 502 at 02:04, the stalled head at 02:15, PASS at 02:39; the passing attempt 2 min 56 s) | PASS on the third attempt, the two failures retained | build/records/lab-run-1/ |
| lab-run-pod-1 | the builder team on a clean machine (agent on a clean machine, not an outsider; not a reproduction) | a stock Ubuntu 24.04 RunPod pod outside the mesh, nothing pre-installed but git, curl and ca-certificates | 02:06 | 33 wall-clock (two attempts: the stalled head at 02:15, PASS at 02:39; the passing attempt 2 min 49 s, Foundry and Node installed by the script) | PASS on the second attempt, the failure retained | build/records/lab-run-pod-1/ |
| lab-run-pod-2 | the builder team on a second clean machine (agent on a clean machine, not an outsider; not a reproduction) | a stock Ubuntu 24.04 RunPod pod outside the mesh (Iceland) | 02:50 | 3 min 6 s, one attempt | PASS | build/records/lab-run-pod-2/ |
| lab-run-pod-3 | the builder team on a third clean machine (agent on a clean machine, not an outsider; not a reproduction) | a stock Ubuntu 24.04 RunPod pod outside the mesh (Iceland) | 02:59 | 2 min 20 s, one attempt | PASS; both explorer verdicts the submissions' own | build/records/lab-run-pod-3/ |
| lab-run-2 | the execution team (the independent reader; the team, labelled) | a throwaway pod | 02:06 | 3 min 46 s on the second attempt; the earliest on the stalled head | PASS with four findings, each a correction (0f1d199d, f1f67d9b, 237db497, 3a1bd414, e7c0bd07's content type); its record main 5ae36365 | build/records/lab-run-2-protocol-b/ |
| lab-run-3 | the economics team (the team on a clean machine, labelled, not an outsider; following only the public page) | a stock Ubuntu 24.04 pod outside the mesh | 03:14 | 2 min 12 s, one attempt | PASS; nine findings on the page's words (F1 the sudo line stops a root shell; F2 to F9 wording, the block target, SOURCE's sums), corrected at this page's sha; its record main 12b8d1c7 | build/records/lab-run-3-economics/ |
| lab-run-4 | the fund-ops team (the team on a clean machine, labelled, not an outsider; following only the public page) | a stock Ubuntu 24.04 pod outside the mesh | 03:14 | 2 min 21 s, one attempt | PASS, no step failed from the text; three lines on grants (the word absent from the page, section 10's stale line, no served route), the earliest two corrected at this page's sha, the route the site lead's; its record main 6391b1ad | build/records/lab-run-4-fund-ops/ |
| lab-run-5 | the Discord seat (the team on a clean machine, labelled) | a stock pod outside the mesh | after the principal's go | |||
| outsiders | three developers unrelated to Igneum Labs | their own | after the public test network is announced |
Refused by name on this page and in every record: Labs staff counted as outside builders; a private RPC in any step; a balance issued by hand; a screenshot in place of a transaction hash; a grant presented as available before the grant process is published and operable.
10. What is not here
- Explorer verification is the explorer's reading of the same source; the bytecode comparison in your own hands (section 6, earliest half) is the verification you do not have to trust anyone for.
- No WorkPay: the reference application of the Consensus Work API (the work-api seat) replaces TestToken as the lab's reference contract when it exists on devnet9; this page then changes with its sha.
- No published contract package: the Pay contracts and the test assets are copied from the repository's src; the lab serves TestToken.sol as a file so the path needs no repository access.
- Grants: the rules are published on main in GRANTS.md (eligibility, the quarterly calendar, the application schema, the scoring rule, the appeal, the payment report; the fund and grants seat's); no call is open until the signer set is published and the launch chain's fund address is the contract; nothing here is an offer. No served route carries the rules yet (the site lead's, ordered 12 October 03:30 UK); until one does, the path is the file's.
- No value: every coin on devnet9 is a development-network token; the chain may be reset, and a reset ends every balance and contract on it. A row in section 9 names the genesis hash of the chain it ran on.
11. The builder pages before the restart (the read for the site lead)
The pages under the pre-restart build/ (build.md, grants.md, earliest-contract.md, faucet.md, rpc.md, compatibility.md, verify-a-block.md, tuning.md) describe the pre-restart chain and come down as a set; the rebuild's /build (rebuild, HELD) is the page that stands. Still true from the old set and kept here: the EVM-compatible environment with Foundry and Hardhat; test coins have no monetary value; grants paid on delivered milestones with published decisions and no promotional obligation. Stale, not served again: the chain ids 4461, 4462, 4463, 4464 and 4465; rpc.devnet.igneum.network; faucet.igneum.network and its 10 IGN drip; every block "built to be proven" and the browser certificate verifier; the "dev fee fund" as the grants' source; the compatibility run of 8 October on igneum-devnet-4; the tuning page's figures; the RPC list probed on igneumd. The rebuild's /build says "nothing on this page is an endpoint": when devnet9 is open, its endpoints table is section 2 of this page, and /build links here and to /faucet; the /faucet page's network card already names devnet9, chain id 7319 and https://rpc.igneum.network and says "explorer coming".