# `Onchain.Sleuth`
[🔗](https://github.com/ZenHive/onchain/blob/v0.5.4/lib/onchain/sleuth.ex#L1)

Compound-style "deploy-as-call" primitive for arbitrary read-only logic
against live chain state.

Sends an `eth_call` with no `to` and creation bytecode as `data`. The node
executes the constructor in-memory against live state; the response is
the bytes the constructor would have deployed. The caller ABI-decodes
those bytes as the query result.

Complements `Onchain.Multicall` (batches existing view functions) and
`onchain_evm` / revm (local simulation). Use Sleuth when you need
derived or conditional logic that isn't exposed as a view function and
you want live-state execution in one RPC round-trip.

Inspired by Compound's Sleuth: https://github.com/compound-finance/sleuth

Bytecode must be supplied by the caller. Solidity source → bytecode
compilation is out of scope — use `OnchainJs.Solc.compile/2`
(onchain_js Task 2) or an external build step (foundry, hardhat).

## Error Format

Pass-through from underlying modules:

| Source | Shape |
|--------|-------|
| `Onchain.Hex.decode/1` on bytecode | `{:error, {:invalid_hex, _}}` |
| `Onchain.ABI.encode_call/2` on ctor args | `{:error, {:encode_error, _}}` |
| `Onchain.RPC.call/3` | `{:error, {:rpc_error, _}}` |
| `Onchain.ABI.decode_response/2` | `{:error, {:decode_error, _}}` |

## Functions

| Function | Purpose |
|----------|---------|
| `query/5` | Execute deploy-as-call → decoded values list |
| `query!/5` | Same, raises on error |

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `query!` | 5 | Execute a Sleuth deploy-as-call. Raises on error. | `bytecode: value`, `constructor_types: value`, `constructor_args: value`, `return_type: value`, `opts: value` |
| `query` | 5 | Execute a Sleuth deploy-as-call: encode ctor args, append to bytecode, eth_call, decode. | `bytecode: value`, `constructor_types: value`, `constructor_args: value`, `return_type: value`, `opts: value` |

# `query`

```elixir
@spec query(String.t(), String.t(), tuple(), String.t(), keyword()) ::
  {:ok, list()} | {:error, term()}
```

Execute a Sleuth deploy-as-call: encode ctor args, append to bytecode, eth_call, decode.

## Parameters

  * `bytecode` - Creation bytecode as 0x-prefixed hex string (output of `solc --bin` or `OnchainJs.Solc.compile/2`) (value)
  * `constructor_types` - Tuple type signature for constructor args, e.g. "(uint256,address)" or "()" for none (value)
  * `constructor_args` - Tuple of constructor argument values matching constructor_types, e.g. \{42, addr_bin\} or \{\} (value)
  * `return_type` - Tuple type signature for decoding the returned bytes, e.g. "(uint256)" or "(uint256[])" (value)
  * `opts` - Options: :rpc_url, :timeout, :block (integer, "latest", "finalized", ...) (default: `[]`, value)

## Returns

List of decoded return values from the constructor's returned bytes (`{:ok, [decoded]} | {:error, term()}`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block (integer, \"latest\", \"finalized\", ...)",
      kind: :value
    },
    return_type: %{
      description: "Tuple type signature for decoding the returned bytes, e.g. \"(uint256)\" or \"(uint256[])\"",
      kind: :value
    },
    bytecode: %{
      description: "Creation bytecode as 0x-prefixed hex string (output of `solc --bin` or `OnchainJs.Solc.compile/2`)",
      kind: :value
    },
    constructor_types: %{
      description: "Tuple type signature for constructor args, e.g. \"(uint256,address)\" or \"()\" for none",
      kind: :value
    },
    constructor_args: %{
      description: "Tuple of constructor argument values matching constructor_types, e.g. {42, addr_bin} or {}",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, [decoded]} | {:error, term()}",
    description: "List of decoded return values from the constructor's returned bytes"
  }
}
```

# `query!`

```elixir
@spec query!(String.t(), String.t(), tuple(), String.t(), keyword()) :: list()
```

Execute a Sleuth deploy-as-call. Raises on error.

## Parameters

  * `bytecode` - Creation bytecode as 0x-prefixed hex (value)
  * `constructor_types` - Tuple type signature, e.g. "(uint256,address)" or "()" (value)
  * `constructor_args` - Tuple of ctor values, e.g. \{42, addr_bin\} or \{\} (value)
  * `return_type` - Return tuple type, e.g. "(uint256[])" (value)
  * `opts` - Options: :rpc_url, :timeout, :block (default: `[]`, value)

## Returns

List of decoded return values (`[decoded]`)

```elixir
# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :block",
      kind: :value
    },
    return_type: %{
      description: "Return tuple type, e.g. \"(uint256[])\"",
      kind: :value
    },
    bytecode: %{
      description: "Creation bytecode as 0x-prefixed hex",
      kind: :value
    },
    constructor_types: %{
      description: "Tuple type signature, e.g. \"(uint256,address)\" or \"()\"",
      kind: :value
    },
    constructor_args: %{
      description: "Tuple of ctor values, e.g. {42, addr_bin} or {}",
      kind: :value
    }
  },
  returns: %{type: "[decoded]", description: "List of decoded return values"}
}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
