Rizemind on Arc Mainnet#

Rizemind is deployed on Arc, Circle’s USDC-gas L1. This page documents the current deployment and walks through examples/arc, which runs a federation against it end to end.

Caution

On Arc Mainnet, gas is real USDC. Every transaction in this walkthrough spends money. Rehearse on Arc Testnet first — the same example runs there with one word changed.

Deployment record#

Item

Value

Network

Arc Mainnet, chain ID 5042

SwarmV1Factory

0x721a4ebA8747eF5db299E8Eec67A2FbAe7866353

Deployed components. Deployed once per chain, by forge/script/deployments/:

  • SelectorFactory — registry and ERC-1967 proxy factory for trainer and evaluator selectors.

  • AlwaysSampled — selector implementation, always-sampled-v1.0.0.

  • RandomSampling — selector implementation, random-sampling-v1.0.0.

  • SwarmV1 — swarm logic contract; proxies delegate to it.

  • SwarmV1Factory — what the Python library calls. createSwarm() deploys a swarm proxy plus its two selector proxies in one transaction.

Each federation then deploys its own SwarmV1 ERC-1967 proxy through the factory. Only the factory address is needed to use the deployment; it is registered in the library, so no configuration is required to reach it.

Supported release. The chain registrations landed on main and are not yet in a tagged release. Use a checkout that contains ARC_MAINNET_CHAINID in rizemind/web3/chains.py.

Arc Testnet. The same components are deployed on chain ID 5042002 and registered alongside mainnet, so the walkthrough below runs unchanged against either network.

Two Arc properties worth knowing#

USDC has two views. The native view — eth_getBalance, gas prices, msg.value — uses 18 decimals, exactly like ether, so web3.py’s and Foundry’s wei arithmetic needs no adjustment. The ERC-20 view (precompile 0x3600…0000) uses 6 decimals. They are the same balance a factor of 1012 apart; mixing them is the usual explanation for a balance that looks absurd.

Deployment is permissionless. Unlike Rizenet, Arc has no deployer allowlist, so there is no address to whitelist before you begin.

Walkthrough: examples/arc#

A minimal federation: three trainers, one round, decentralized Shapley value contribution scoring, CPU-only simulation. It is deliberately small — the decentralized Shapley strategy evaluates every non-empty coalition, so three trainers means seven evaluations per round and ten would mean 1023.

Step 1 — Install#

uv sync
cd examples/arc

Torch and the dataset dependencies are pulled in on first use; no extra sync flags are needed.

Step 2 — Choose the network#

One setting picks the chain, in [tool.flwr.app.config]:

arc-network = "mainnet"   # or "testnet", or "local" for Anvil

It selects the chain ID and the default RPC endpoint together, so the chain guard and the endpoint cannot drift apart. Override it for a single run instead of editing the file:

uv run -- flwr run . --run-config arc-network=testnet

$ARC_RPC_URL overrides only the endpoint, for a private or rate-limited node. The chain guard still holds it to the network’s chain ID.

Step 3 — Create the aggregator account#

Only the aggregator sends transactions. Trainers sign EIP-712 messages off-chain and never touch the chain, so their accounts need no USDC.

uv run -- rzmnd account generate arc-aggregator --words 24
export RZMND_PASSPHRASE='the passphrase you just chose'

This writes an encrypted keystore to ~/.rzmnd/keystore/. Account 0 of that mnemonic is the aggregator; accounts 1..N are the trainers.

Caution

Do not use rzmnd account load to read the addresses back — it prints the mnemonic and every derived private key to your terminal, where they remain in scrollback and shell logs.

Step 4 — Fund the aggregator#

Print the addresses. This touches no network, so it works before anything else is configured:

uv run -- python addresses.py

Send USDC to the aggregator address. Budget one createSwarm (roughly 1M gas) plus two transactions per round. On testnet, https://faucet.circle.com issues test USDC.

Note

The aggregator is the same address on every network, because the same mnemonic derives the same accounts everywhere. Funding it on testnet does nothing on mainnet, and the address itself is no evidence of which chain you are on — the chain guard is.

Step 5 — Run the pre-flight checks#

uv run -- python preflight.py

It reads the same pyproject.toml the run does and verifies, without sending a transaction, that:

  • the RPC is reachable and reports the chain ID arc-network selected;

  • web3’s Proof-of-Authority middleware is configured as this chain needs;

  • the SwarmV1Factory has code at the registered address;

  • the aggregator can pay, priced at the current gas price;

  • createSwarm would succeed — a dry eth_call that resolves both selectors through the SelectorFactory and reverts if either version is unregistered on this chain.

Fix every FAIL before continuing. Use --network testnet to check the other network without editing anything. Neither the passphrase nor any key is printed.

Step 6 — Run the federation#

uv run -- flwr run .

What happens, in order:

  1. Arc mainnet (chain 5042) via https://rpc.mainnet.arc.io — the resolved network. The server aborts here if the RPC disagrees.

  2. Web3 swarm contract address: 0x721a…6353 — the factory.

  3. One createSwarm transaction; the new swarm proxy address follows.

  4. Per round: trainers sign, coalitions are evaluated, then distribute followed by nextRound — two aggregator transactions.

  5. Metrics are written under logs/.

First run also downloads CIFAR-10 (roughly 170MB), the trainers’ data.

Step 7 — Verify on-chain#

export ARC_RPC=https://rpc.mainnet.arc.io
export SWARM=0x...   # the proxy address from the run

cast call $SWARM "currentRound()(uint256)" --rpc-url $ARC_RPC

Then look the swarm up in the Arc explorer and confirm the TrainerContributed events, one per trainer per round. preflight.py prints the explorer link for the network it checked.

Reusing a swarm#

While factory_v1 is set, every run deploys a new swarm — on mainnet, that means paying for one each time. To keep training against an existing swarm, replace the factory block with its address:

[tool.web3.swarm]
address = "0xYourSwarmProxyAddress"

SwarmConfig accepts exactly one of address or factory_v1, so remove the [tool.web3.swarm.factory_v1] block when you add address.

Further reading#

  • examples/arc/README.md — the example’s own documentation, including rehearsing against a local Anvil chain.

  • forge/DEPLOYING.md — bringing the contracts up on a chain that does not have them yet.