> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cryptocheckout.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Address derivation

> How pool and forwarder addresses are computed, why they must stay byte-identical across implementations, and what breaks if they drift.

Every address in the system is computed before it exists. Getting this wrong strands funds permanently, so the rules are strict.

## The inputs

```
EVM:  keccak256(0xff ++ factory ++ salt ++ keccak256(init_code))[12:]
TRON: keccak256(0x41 ++ factory ++ salt ++ keccak256(init_code))[12:]
```

| Artefact               | Salt                | Init code commits                                |
| ---------------------- | ------------------- | ------------------------------------------------ |
| Pool                   | merchant identity   | recipient, treasury, partner, basis-point splits |
| Forwarder              | `paymentId`         | the destination pool                             |
| Per-customer forwarder | customer identifier | the destination pool                             |

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

<Warning>
  The CREATE2 salt is `keccak256("cryptocheckout.v1")`, **pinned forever**. A new salt is a new protocol.
</Warning>

Deployment goes through the canonical CREATE2 factory at `0x4e59b44847b379578588920cA78FbF26c0B4956C`, 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.

TRON derives separately because of the `0x41` prefix.

## Three predictors that must agree

The same derivation exists three times and all three must produce identical bytes:

| Where                 | Files                                                                                          |
| --------------------- | ---------------------------------------------------------------------------------------------- |
| Solidity              | the contracts and their fixture vectors                                                        |
| Edge functions (Deno) | `_shared/forwarderAddress.ts`, `_shared/poolAddressTron.ts`, `_shared/forwarderAddressTron.ts` |
| Browser (TypeScript)  | `src/lib/pool/poolDerive.ts` and siblings                                                      |

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](/trust/db-display-only), 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

<Danger>
  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.
</Danger>

The plan for the upcoming change is a single cutover bundling everything that touches init code:

* Forwarder clone (EIP-1167)
* Strip `refund()` from the forwarder
* Strip the reward code from the pool
* Add the gas-reimbursement parameter to the pool

Three separate migrations would be three chances to strand a customer payment. Each needs either a quote-freeze window or a versioned predictor.

## The TRON gate

<Danger>
  **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.
</Danger>

It was proven on 2026-06-19 for the current init code: predicted equalled deployed for `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.parseJsonAddress` reverts on `null`, and cheatcode reverts are not catchable. Use `vm.keyExistsJson` plus a length check.
* Foundry 1.5+ treats `address(this)` in a `Script` contract as fatal.
* `Deploy.t.sol` writes fixtures into `contracts/deployments.json`. Always `git checkout -- contracts/deployments.json` after running tests — a polluted copy breaks the shared contracts module and the `/verify` page at runtime. CI fails if it is dirty.
