The inputs
Because the pool’s payout configuration is part of its init code, the pool address commits to where the money goes. That is what makes client-side re-derivation meaningful.
Fixed constants
Deployment goes through the canonical CREATE2 factory at0x4e59b44847b379578588920cA78FbF26c0B4956C, inherited from forge-std, not via the deployer EOA. Predict using the factory address, never vm.addr(key). TRON has no canonical Arachnid factory, so it uses Create2FactoryTron.
Same address across seven EVM chains
The init code is byte-identical across EVM chains, so a merchant’s pool has the same address on all seven. This is a product property, not an accident, and it constrains design:- Anything that makes init code chain-dependent breaks it.
- A clone would embed the implementation address, making cross-chain identity contingent on that implementation landing at a byte-identical address everywhere. This is one of the four reasons the pool is not cloned.
0x41 prefix.
Three predictors that must agree
The same derivation exists three times and all three must produce identical bytes:
The browser port is pinned to the Solidity fixture vectors by test. It has to be — it is the load-bearing check in the trust model, and a drifted port would either block legitimate payments or fail to catch a tampered one.
Update all three in lockstep. Never one alone.
Init-code changes are migrations
Every init-code change moves every CREATE2 address. Any in-flight invoice quoted under the old scheme must drain before cutover, or its customer pays to an address the new predictor no longer recognises.
- Forwarder clone (EIP-1167)
- Strip
refund()from the forwarder - Strip the reward code from the pool
- Add the gas-reimbursement parameter to the pool
The TRON gate
Re-prove the
0x41 derivation on Nile for any new init code before an address is shown as a QR. A prefix or bytecode mismatch strands funds with no recovery. This is a hard gate, not a checklist item.MerchantPoolTron and InvoiceForwarderTron, with a full pay, sweep, distribute, and claim cycle netting 99% through Create2FactoryTron. That proof does not carry over to changed bytecode.
Practical Foundry notes
vm.parseJsonAddressreverts onnull, and cheatcode reverts are not catchable. Usevm.keyExistsJsonplus a length check.- Foundry 1.5+ treats
address(this)in aScriptcontract as fatal. Deploy.t.solwrites fixtures intocontracts/deployments.json. Alwaysgit checkout -- contracts/deployments.jsonafter running tests — a polluted copy breaks the shared contracts module and the/verifypage at runtime. CI fails if it is dirty.