# Blockchain Relay Contract

## Incoming from Alchemy (to orchestrator)

The orchestrator accepts Alchemy Address Activity webhook payloads with these useful fields:

- Signed header required: `x-alchemy-signature`
- Signature verification: required via `HMAC-SHA256(rawBody, ALCHEMY_WEBHOOK_SIGNING_KEY)` before the event enters the queue

- `id`
- `createdAt`
- `type=ADDRESS_ACTIVITY`
- `event.network`
- `event.activity[].hash`
- `event.activity[].fromAddress`
- `event.activity[].toAddress`
- `event.activity[].rawContract.rawValue`
- `event.activity[].asset`
- `event.activity[].category`
- `event.activity[].rawContract.address`
- `event.activity[].log.logIndex`
- `event.activity[].log.removed`

## Relay to Laravel

`POST /api/v1/deposits`

Headers:
- `content-type: application/json`
- `x-relay-secret: <LARAVEL_RELAY_SECRET>`
- `x-relay-timestamp: <unix-seconds>`
- `x-relay-nonce: <random-hex>`
- `x-relay-signature: <hex-hmac-sha256>`

Payload is normalized by the orchestrator before relay:

```json
{
  "user_id": 123,
  "wallet_address": "0x...",
  "amount": "1000000",
  "asset": "USDT",
  "tx_hash": "0x...",
  "block_number": 123,
  "chain_type": "ethereum"
}
```

Signature format:

- signing string: `<x-relay-timestamp>.<x-relay-nonce>.<raw-json-body>`
- algorithm: `HMAC-SHA256`
- key: `LARAVEL_RELAY_HMAC_SECRET` (set same value as Laravel `BLOCKCHAIN_RELAY_HMAC_SECRET`)

Replay protection:

- Laravel stores `x-relay-nonce` in `blockchain_relay_nonces` and rejects reused nonces.
- Nonce freshness is bounded by `BLOCKCHAIN_RELAY_NONCE_TTL_SECONDS`.
- Laravel schedules `app:prune-blockchain-relay-nonces --hours=24` hourly to prune old nonce rows.

Delivery resilience:

- Verified events are queued locally before relay. The webhook route returns `200` once the event is verified and stored.
- If Laravel is unavailable, queued events remain in `EVENT_QUEUE_PATH`/`RELAY_BUFFER_PATH` and are retried periodically (`RELAY_FLUSH_INTERVAL_MS`) and via manual `POST /v1/orchestrator/relay/flush`.

## Wallet registration

`POST /v1/orchestrator/wallets/register`

```json
{
  "user_id": 123,
  "wallets": [
    {
      "chain_type": "ethereum",
      "wallet_id": "wallet_internal_123",
      "address": "0x..."
    }
  ]
}
```

## Mapping resolve

Tracked ownership is resolved by recipient address via the mapping store index.

Returns:
- `wallets[]`
- `source_tron_address`

## Idempotency

The orchestrator sends one deposit per transaction hash.
Laravel stores incoming deposit events keyed by `idempotency_key` in `blockchain_ingress_events`.
