Fee flow
Follow one unit of value from end to end. Purchase money and payout money never mix, they arrive at different addresses, and only one of them can ever reach a holder. Knowing which is which answers most questions about the protocol.
Two pots, kept apart
| Pot | Holds | Can pay holders |
|---|---|---|
| Payments wallet | Node purchases, received as plain transfers | No. It is an ordinary account outside the contract entirely. |
| Factory balance | Payout money, sent by the distributor with creditBatch, plus anything added through fund() or a plain transfer | Yes. This is the only balance a withdrawal draws from. |
The separation is not a policy, it is a consequence of where the money is sent. Buying a node is a transfer to a wallet; the factory has no payable mint and never sees your 0.02 ETH, not for one block. Purchase money cannot become payout liquidity because it is never in the contract to begin with.
Payout money arrives the other way round, from the distributor, and it arrives attached to specific nodes. If the two shared an address every payout would be partly funded by the next buyer and the contract could not tell them apart. Keeping them separate is what gives outstanding and freeBalance() a meaning that can be checked from outside.
The path, hop by hop
- Payment. The buyer sends exactly 0.02 ETH to the payments wallet. Plain transfer, no calldata, no contract involved. That money has left the flow at this point: it funds the operation, and nothing downstream draws on it.
- Watcher. The server reads blocks, finds the transfer, and records it with its transaction hash before anything is minted. See Deploying a node.
mintFor. The relayer creates the node for the paying address and pays that gas itself. The payment transaction hash goes in aspaymentRef, which is what ties the sale to the node in the explorer.creditBatch. The distributor sends the round’s ETH and the per-node amounts in one payable call.msg.valuemust equal the sum of the amounts, so the money and the record land together. See Distribution.- Node balance. The value now sits in the factory, attached to your node and counted in
outstanding. It stays there until you move it. Nobody else can, including the contract owner. withdraw. You call it yourself, from the wallet that owns the node, naming any destination. The whole balance of that node moves.withdrawAlldoes the same across every node you own in one transaction. See Withdrawing.
Where the value comes from today
There is no swap flow yet. A Uniswap v4 pool fixes its hook at initialisation and a hook cannot be attached afterwards, so until Sitowise deploys the hook, runs pools on it, and those pools carry volume, the protocol accrues nothing from trading.
During the launch period rewards are funded by Sitowise. Once pools are attached to the hook, accrual comes from swap flow. Sitowise can reduce or stop launch-period funding at any time.
When that changes, it changes one hop only. A swap through a pool naming the hook would pay a share of the unspecified side into the hook, and sweepNative() would move it into the factory, giving the distributor money to credit that it did not have to fund. The mechanism is described on How accrual works, and it is described as intended, not as running. Everything from creditBatch onward is identical either way: the same call, the same balances, the same withdrawal from your own wallet.
Who pays what
| Cost | Paid by | When |
|---|---|---|
| 0.02 ETH node price | You | Once, as a transfer to the payments wallet |
| Gas for the transfer | You | With the payment |
Gas for mintFor | Sitowise, from the relayer | When your node is created |
Gas for creditBatch, and the ETH it carries | Sitowise, from the distributor | Every round |
| Gas to withdraw | You | Every withdrawal |
| The hook’s share of a swap | The swapper | Later, inside the swap, on the unspecified side |
Checking the flow yourself
A handful of reads describe the state of the payout pot at any moment, and none of them require this website.
cast call $FACTORY "totalDistributed()(uint256)" # every wei ever credited to nodes
cast call $FACTORY "totalWithdrawn()(uint256)" # every wei owners have taken out
cast call $FACTORY "outstanding()(uint256)" # credited, not yet withdrawn
cast call $FACTORY "freeBalance()(uint256)" # balance above what is owed
cast call $FACTORY "isSolvent()(bool)"
cast balance $FACTORY # the pot itselfoutstanding is the sum of every node balance, and freeBalance() is what is left over. Those two are the whole guarantee: rescue is bounded by the second, so no sequence of owner calls reaches the first. If isSolvent() ever returns false the contract is not holding enough to cover the balances it has written, which is a state worth watching for and is listed among the failure modes on Risks.