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

# Configure On-Chain Providers (Alchemy, QuickNode, Tron)

> Configure Alchemy, QuickNode, and Tronscan so a self-hosted Idem instance can detect on-chain stablecoin transfers and match them to your settlements.

<Note>
  Idem is currently **self-hosted only** — every deployment needs this configuration. Managed cloud is on the roadmap; this page won't apply once it ships.
</Note>

Idem detects on-chain stablecoin transfers through three provider integrations, one per chain family: **Alchemy** for EVM chains, **QuickNode** for Solana, and **Tronscan** for Tron. Each needs its own environment variables set before it will do anything — an unconfigured provider doesn't error, it just silently never detects transfers on that chain.

## How detection works

EVM and Solana are event-driven — the provider pushes a webhook to Idem the moment a matching transfer happens. Tron has no webhook mechanism, so Idem polls the Tronscan REST API on a fixed interval instead. All three also run a one-time startup recovery sweep that replays anything missed since the last recorded checkpoint (for example, during a deploy) — this is not a second polling loop, just a catch-up pass.

```mermaid theme={null}
flowchart LR
  A[Alchemy] -- "POST /internal/webhooks/alchemy\nX-Alchemy-Signature" --> D[Idem]
  B[QuickNode] -- "POST /internal/webhooks/quicknode\nX-QN-Signature" --> D
  C[Tronscan REST] -- "polled every N ms" --> D
  D --> E{"watched_addresses\nmatch?"}
  E -- yes --> F["Post OnChainEntry\n+ update chain_checkpoint"]
  E -- no --> G[Ignore]
```

Whichever path a transfer arrives through, it only becomes a ledger entry if it matches a row in `watched_addresses` — see [Registering which addresses to watch](#registering-which-addresses-to-watch) below. Configuring a provider is necessary but not sufficient on its own.

## Alchemy (EVM — Ethereum, Base, Polygon)

| Variable                      | Required                   | Purpose                                                                                                                                  |
| ----------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `ALCHEMY_API_KEY`             | For EVM detection          | Your Alchemy API key. Shared across all three EVM networks — it's interpolated into the RPC URL for Ethereum mainnet, Base, and Polygon. |
| `ALCHEMY_WEBHOOK_SIGNING_KEY` | Yes, outside `dev` profile | Validates the `X-Alchemy-Signature` header on inbound webhook deliveries.                                                                |

These map to `idem.chain.evm.rpc-url`, `idem.chain.evm-base.rpc-url`, `idem.chain.evm-polygon.rpc-url`, and `idem.chain.alchemy-webhook-signing-key` in `application.yaml`, if you're setting properties directly instead of via environment variables.

<Steps>
  <Step title="Set ALCHEMY_API_KEY">
    Leaving this blank silently disables all three EVM readers — no error at startup, they simply never activate. This is a deliberate opt-in design, not a bug, but it means a half-configured EVM setup looks identical to a fully-disabled one from the logs.
  </Step>

  <Step title="Create a Notify webhook in the Alchemy dashboard">
    Configure an "Address Activity" webhook pointed at `https://<your-host>/internal/webhooks/alchemy`, signed with the same value you set as `ALCHEMY_WEBHOOK_SIGNING_KEY`.
  </Step>
</Steps>

## QuickNode (Solana)

| Variable                   | Required                   | Purpose                                                             |
| -------------------------- | -------------------------- | ------------------------------------------------------------------- |
| `QUICKNODE_SOLANA_URL`     | For Solana detection       | Full Solana JSON-RPC endpoint URL.                                  |
| `QUICKNODE_WEBHOOK_SECRET` | Yes, outside `dev` profile | Validates the `X-QN-Signature` header on inbound Stream deliveries. |

These map to `idem.chain.solana.rpc-url`, `idem.chain.solana.batch-size` (default `100`), and `idem.chain.quicknode-webhook-secret`.

<Steps>
  <Step title="Set QUICKNODE_SOLANA_URL">
    Same silent-disable behavior as Alchemy applies — a blank URL means no Solana reader is ever constructed.
  </Step>

  <Step title="Create a QuickNode Stream">
    Point it at `https://<your-host>/internal/webhooks/quicknode`, signed with `QUICKNODE_WEBHOOK_SECRET`.
  </Step>
</Steps>

<Note>
  Signature validation is `HMAC-SHA256` over `nonce + timestamp + rawBody` — not the body alone. Idem expects all three headers together on every delivery: `X-QN-Signature`, `X-QN-Nonce`, and `X-QN-Timestamp`.
</Note>

## Tron (Tronscan REST polling)

| Variable           | Required | Purpose                                                                                       |
| ------------------ | -------- | --------------------------------------------------------------------------------------------- |
| `TRONSCAN_API_KEY` | Optional | Sent as `TRON-PRO-API-KEY`. Raises your Tronscan rate limit; Tron detection works without it. |

Maps to `idem.chain.tron.api-url` (defaults to `https://apilist.tronscan.org`, so Tron detection is active out of the box) and `idem.chain.tron.polling-interval-ms` (default `5000`).

Unlike Alchemy and QuickNode, there's no dashboard or webhook to register — Tron polling starts working as soon as the app is running.

## Fail-fast on missing webhook secrets

<Warning>
  Outside the `dev` Spring profile, a blank `ALCHEMY_WEBHOOK_SIGNING_KEY` or `QUICKNODE_WEBHOOK_SECRET` **crashes the app at startup** — this is intentional, so a production deployment can't silently accept unsigned webhook traffic. In the `dev` profile only, both are optional: a blank value logs a warning and skips signature validation.
</Warning>

## Registering which addresses to watch

Configuring providers makes Idem *capable* of seeing a transfer — it doesn't tell Idem *which* transfers matter to you. That mapping lives in the `watched_addresses` table: a row ties a specific wallet address and token contract, per tenant, to the debit/credit account pair that should receive the resulting journal entry.

<Note>
  There is currently no tenant-facing API to register a watched address — this is an operator/database-level step today. If you're self-hosting, your deployment administrator needs to insert the row directly; it isn't something you can do through the REST API or a dashboard yet.
</Note>

## Multi-replica deployments

If you're running more than one replica (for example, on GKE), set `IDEM_SCHEDULING_DISTRIBUTED_LOCK_ENABLED=true` (maps to `idem.scheduling.distributed-lock.enabled`, default `false`). This ensures only one replica runs Tron polling and the startup recovery sweep at a time, backed by a ShedLock table. Single-instance and local setups can leave this unset.

## Sample configuration

```bash theme={null}
ALCHEMY_API_KEY=
ALCHEMY_WEBHOOK_SIGNING_KEY=
QUICKNODE_SOLANA_URL=
QUICKNODE_WEBHOOK_SECRET=
TRONSCAN_API_KEY=
IDEM_SCHEDULING_DISTRIBUTED_LOCK_ENABLED=false
```
