# The Graph (https://docs.helico.site/docs/thegraph)

The question Aqua cannot answer about itself, and why an indexer is the only way to ask it.

Before an agent can act for a wallet, it has to know what it is allowed to do. On Aqua that turns
out to be the hard part.

```solidity
mapping(address maker =>
    mapping(address app =>
        mapping(bytes32 strategyHash =>
            mapping(address token => Balance)))) private _balances;

event Shipped(address maker, address app, bytes32 strategyHash, bytes strategy);
```

The mapping is `private` and four levels deep, so nothing enumerates it. Reading one balance needs
a strategy hash you already hold. And &#x2A;*no event parameter is `indexed`**.

## Measured, not asserted
That last claim is the one everything rests on, so it is worth asking the chain rather than
reading the source. `bun scripts/check-subgraph.ts` does exactly that before it asks the index:

```
what the chain can answer, asked directly:
  Shipped logs in the last 200,000 blocks: 1
  topics per log: 1–1
  → only topic0, the signature. No parameter is indexed, so logs cannot be
    filtered by maker, by app or by token. Only by "a Shipped happened".
```

One topic — the event signature. You cannot ask for a maker's logs, because maker is not indexed.

**If any parameter were indexed, a log would carry two topics or more and the script would say
so** instead of the conclusion. A check that cannot fail is not a check.

So &#x2A;"which mandates does this wallet have, and what is left in each?"* has **no on-chain answer at
any speed**. The Graph is not making this faster. It is the only way to ask.

## What the subgraph indexes
Aqua's four events on Arbitrum One, plus our own account factory. It is deployed to Subgraph
Studio and serving live data — a mocked dataset would not qualify and would not be useful.

Against the busiest maker on the chain, which is not ours:

```
maker     0xef9f7f4006fe95afede04f6916e72556a957ebbc
mandates  49, of which 11 still active, across 5 tokens
```

Forty-nine strategies under one address, and no way on chain to learn that any of them exist. A
**docked** mandate comes back marked docked rather than merely empty — Aqua zeroes the ledger and
emits no per-token event, so from outside a revocation and a spent balance look identical, and the
schema keeps Aqua's own three-state sentinel to tell them apart.

## Where it is load-bearing
**Inside the enclave.** The confidential workflow asks the index what an account's live mandates
could still be asked to pay, and raises its liquid buffer to that number. The floor may only be
**raised**, so an index that is unreachable, empty or lagging can never make the decision wrong —
only less good. The run says which buffer it used.

**In the dapp.** "What this wallet may spend" is answered from the subgraph, because the chain
cannot answer it.

**In the enclave's fleet.** Which accounts exist at all comes from the index, so an account opened
a minute ago is managed without anybody editing a file.

## The cache, and why it is not in the path
A backend endpoint sits in front of Studio and remembers an answer for a minute, so a hundred
visitors asking the same question cost one query. It knows no GraphQL beyond the operation name —
the queries live in `@helico/plugin-thegraph` — and **the browser falls back to Studio when the
cache is absent**, which three browser checks prove by unplugging it. Reading Studio directly is
what made these panels work when everything of ours was down, and a smaller bill is not worth
losing that quietly.

## What we do not use
Subgraph Studio, and nothing else. No Substreams, no Token API, and no Subgraph MCP server — that
last one was planned and is not built. Said here because a page about an integration should be as
clear about its edges as about its middle.
