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

# SweeperService

> Registered deposit-address monitoring and automatic sweeping

Package: `monaco.api.sweeper`

Source: `protos/api/sweeper.proto`

Use `SweeperService` to register a deposit address so the watched assets sent there are swept into the rollup automatically once each asset's minimum is reached, and to read the chains and assets it watches. Only the assets `GetChains` lists are indexed — the transfer indexer filters logs to those token addresses — so an unlisted ERC20 sent to the address is not swept.

```protobuf theme={null}
service SweeperService {
  rpc Register(RegisterRequest) returns (RegisterResponse);
  rpc GetChains(GetChainsRequest) returns (GetChainsResponse);
}
```

## Notes

* `Register` requires authentication, and the request carries no identity at all: the application is the one the session was established under (`login` takes its client id) and the credited address is the session's own. A caller can therefore only register their own address under their own application. An unauthenticated call returns `UNAUTHENTICATED`.
* Every `(application, user, deposit target)` triple has a deterministic deposit address; registering the triple makes the backend monitor that address and forward deposits of the watched assets into the rollup, crediting the user under the application. `GetChains` is the authoritative list of what is watched; a token outside it is not indexed and not swept.
* `deposit_target` is optional: `SPOT` (the default when omitted or empty) credits the spot wallet, `MARGIN` asks for the parent margin account's collateral. It is part of the address derivation, so `SPOT` and `MARGIN` yield **two different deposit addresses** for the same application and user, each carrying its own request. A `SPOT` address is never credited to margin.
* `MARGIN` is a routing request rather than a guarantee: a deposit the matching engine cannot route to collateral — an unsupported collateral asset, or a margin account that fails validation — is credited to the spot wallet instead, so funds are never stranded.
* The response returns immediately with the derived `sweeper_address` and a `status` of `registered` (added by this call) or `already_registered` (known before). A one-time initial balance check — which sweeps whatever is already sitting on the address at or above that asset's minimum, the same floor every later sweep applies — runs in the background and is reported by `initial_sweep_check`.
* Idempotent and safe to retry within the current forwarder-based derivation: re-registering an existing triple returns `already_registered` with the same derived address. A completed initial check is never repeated; if `initial_sweep_check` is still `false`, registration schedules it again, while an already-running check is deduplicated.
* **Pre-v1.0.53 registrations need an operator migration.** Calling `Register` for one returns and monitors the new forwarder-derived address in the running resolver, but does not replace that row's stored address; after a resolver restart, warmup reloads only the old value. Pause deposits for that registration and do not display or fund the new address until Monaco confirms its stored row was migrated. Re-registration alone is not restart-durable.
* A `deposit_target` that is neither `SPOT` nor `MARGIN` returns `INVALID_ARGUMENT`. An unrecognized target is rejected rather than defaulting to `SPOT`, which would answer with an address pointed at a ledger the caller did not ask for.
* `PERMISSION_DENIED` means the application the session was established under is no longer active.
* `RESOURCE_EXHAUSTED` means the service-wide admission limit on *new* registrations was hit. They create durable state, so they are bounded across all callers rather than per caller; re-registering an existing address is never limited, and a refused call stores nothing, so it is safe to retry. The status carries a `google.rpc.RetryInfo` detail with the wait — the same interval REST returns as `retryAfter` — so honor that rather than guessing.
* `INTERNAL` is a generic server-side failure. It can happen before the sweeper is contacted at all — a failed application lookup, for instance — so unlike `UNAVAILABLE` it says nothing about the resolver.
* `GetChains` is public: it carries no auth interceptor requirement and takes no request fields, because a depositor needs the supported set before they have a session. It returns one `SweeperChain` per watched chain — the EVM `chain_id`, a short `name`, a `hub` flag marking the settlement chain that deposits on every other listed chain are bridged into, and the `SweeperChainAsset` entries swept there (`symbol`, `token_address`, `decimals`, and `min_sweep_raw`, the minimum sweepable balance in raw base units). A balance strictly below the minimum stays on the deposit address until a later deposit brings the total up to it; a balance exactly at the minimum sweeps. Only an operator changes the set, and how fast that lands depends on the chain's token source: a hub backed by the active-asset registry picks up a listing or delisting on the resolver's own refresh with no restart, while a chain configured with an explicit token list changes only when that configuration is rolled out. The gateway serves the last answer from a short cache either way, so refetch rather than treating a cached set as permanent.
* Every registered deposit address is watched on **every** listed chain and is identical on all of them, so `Register` takes no chain and one registration covers the whole set. `GetChains` is for showing a depositor where they may send, not for choosing where to register.
* `GetChains` is all-or-nothing: rather than publish a partial listing, it returns `UNAVAILABLE` whenever any configured chain cannot be advertised — the sweeper is not configured at all, a configured chain is disabled by its own configuration, a chain's id is not yet verified against its RPC (an unreachable RPC included; until it verifies, its indexer is still seeding and nothing watches that chain), a chain's RPC reports an id outside the JSON-safe range the listing serves, or the resolver could not answer. All of them are safe to retry. `INTERNAL` is a generic server-side failure.
* `UNAVAILABLE` means the sweeper could not complete the registration. Several unrelated conditions share it — missing configuration, an unreachable chain or factory, a store that would not write or read back, a reply that never arrived — so treat it as an **unknown** outcome rather than a negative one: the registration is stored before the reply is composed, and the status alone never tells you whether that happened. Retry either way — registration is idempotent, and the retry answers `registered` or `already_registered` according to whether the first attempt had landed. Both are success and both carry the same address, so accept either.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.