Events
The factory emits enough to reconstruct the protocol’s entire history from logs alone: every node and the payment that bought it, every credit, every payout and every configuration change. This page lists all of them.
Factory events
event NodeMinted(uint256 indexed id, address indexed owner, bytes32 paymentRef, uint64 createdAt);
event Credited(uint256 indexed id, uint256 amount, uint256 newBalance);
event Withdrawn(uint256 indexed id, address indexed to, uint256 amount);
event RelayerChanged(address relayer);
event DistributorChanged(address distributor);
event PausedChanged(bool paused);
event MaxPerWalletChanged(uint256 max);
event OwnerChanged(address owner);
event OwnershipOfferStarted(address indexed pendingOwner);
event Funded(address indexed from, uint256 amount);
event Rescued(address indexed to, uint256 amount);| Event | Emitted when |
|---|---|
NodeMinted | The relayer creates a node. paymentRef is the hash of the transfer that paid for it, which is what makes the sale checkable |
Credited | A node’s balance goes up, once per node per batch. newBalance is the balance after the credit, so a single log is enough to know where a node stands |
Withdrawn | A node owner takes their balance. withdrawAll emits one of these per node it sweeps, inside one transaction |
RelayerChanged, DistributorChanged | A role is rotated, and also once each from the constructor with the deploy-time values |
OwnershipOfferStarted, OwnerChanged | The two halves of the ownership handover. The first is only an offer; the second is the transfer actually taking effect, and it also fires once at deployment |
PausedChanged, MaxPerWalletChanged | The owner changes a setting. Neither can affect an existing balance |
Funded | ETH arrives without being attached to a node, through fund() or a plain transfer to the contract. This is what a mistaken transfer to the factory produces |
Rescued | The owner takes unattached funds out. It can never exceed freeBalance(), so it can never be a holder’s money |
OwnerChanged, RelayerChanged and DistributorChanged so that the initial roles are in the log history and not only in storage.Reconstructing the protocol from logs
These four are enough to rebuild every number this site displays, without trusting the site:
NodeMintedgives you every node, its owner at mint, and the payment behind it.Creditedsummed per node givestotalReceived; summed overall it givestotalDistributed.Withdrawnsummed the same way givestotalWithdrawn.- Credits minus withdrawals equals
outstanding, which the contract also reports directly. If the two disagree, one of them is wrong and it is worth knowing which.
Nodes are not tokens and cannot be transferred, so ownership never changes after NodeMinted. There is no transfer log to follow and none is missing.
Hook events
event SwapAccrued(
PoolId indexed poolId,
address indexed currency,
uint256 amount,
uint256 cumulative,
uint16 shareBps
);
event Swept(address indexed currency, address indexed to, uint256 amount);
event FactoryChanged(address indexed oldFactory, address indexed newFactory);
event ShareBpsChanged(uint16 oldShareBps, uint16 newShareBps);
event SweepRecipientChanged(address indexed oldRecipient, address indexed newRecipient);SwapAccrued would be the one that matters for anyone checking whether the protocol is swap funded. It is designed to fire once per swap that produced a non-zero share, so the day it exists, an empty log is a complete answer: if it has never fired, no swap has ever paid this hook. Today there is no hook address to query at all, which is the same answer arrived at sooner.
Topic hashes
Topic zero is the keccak hash of the event signature with canonical types. Recompute any of these rather than trusting the table: cast keccak "Credited(uint256,uint256,uint256)".
| Event | Topic 0 |
|---|---|
NodeMinted(uint256,address,bytes32,uint64) | 0x01f6c872d9a3a5d1c63c69a4edd6004260f7ac9b086be1468e99f0569307bd1c |
Credited(uint256,uint256,uint256) | 0x3806c51c015e3f5bafa6a64bacc9ec78aa02f9a208d9d22e52635e923ba00f6e |
Withdrawn(uint256,address,uint256) | 0xcf7d23a3cbe4e8b36ff82fd1b05b1b17373dc7804b4ebbd6e2356716ef202372 |
Funded(address,uint256) | 0x5af8184bef8e4b45eb9f6ed7734d04da38ced226495548f46e0c8ff8d7d9a524 |
Rescued(address,uint256) | 0x8aec0ce3dadffacf4b7a963e0fed1ff2e6151b4c95d4a65acafa9d1299630402 |
OwnerChanged(address) | 0xa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf36 |
RelayerChanged(address) | 0x88cb58f8479aba47ccd2dcbc41bf94bc01e3f58a877cbe5e7f3bd978d89773ba |
DistributorChanged(address) | 0xe37acc13f5ed9d0cc83c2842e093fe5a494d5b8fb5b1db06356b327081832f52 |
PausedChanged(bool) | 0xd83d5281277e107f080e362699d46082adb74e7dc6a9bccbc87d8ae9533add44 |
MaxPerWalletChanged(uint256) | 0x00c836a17ed3a6a59dce35376ae3c2777797dc03f39f463153c0fea5b65f0683 |
OwnershipOfferStarted(address) | 0x23a54ba7a990a65d3d8c17e693e5b066e88154ce314d568de26f02e98ac33dd1 |
Reading logs
# every node minted, from deployment to now
cast logs --from-block 0 \
--address $FACTORY \
"NodeMinted(uint256,address,bytes32,uint64)" \
--rpc-url https://rpc.mainnet.chain.robinhood.com
# every credit to one node, using the indexed id as the second topic
cast logs --from-block 0 \
--address $FACTORY \
"Credited(uint256,uint256,uint256)" \
--rpc-url https://rpc.mainnet.chain.robinhood.com
# payouts, filterable by destination through the second indexed topic
cast logs --from-block 0 \
--address $FACTORY \
"Withdrawn(uint256,address,uint256)" \
--rpc-url https://rpc.mainnet.chain.robinhood.comWith viem, the same thing is a typed getLogs against the generated ABI. The application ships that ABI, including the custom errors, so a revert is decoded into a real reason instead of "execution reverted". The error list is on Factory interface.
Both NodeMinted and Withdrawn index the node id first and an address second, so a wallet’s whole history can be filtered on topics without downloading every log the contract ever wrote.
What is not in the logs
The payment that buys a node is not a factory event, because it does not touch the factory. It is an ordinary transfer to the payments wallet, visible on the explorer as such, and it is tied to the node by its hash appearing as paymentRef in NodeMinted. That is the join between the two halves of the sale. See Settlement.
The decision behind a credit is not on chain either. Which nodes are credited and by how much is computed off chain by the distribution worker; what the chain records is the result, with the ETH attached. The per-node history is also available over HTTP from GET /api/node/:id, which is a convenience, not the source of truth.
Sign-ins are not on chain. Signing in produces a message signature, not a transaction, so it leaves no trace on the network.