# GET - /api/audit-contract

### Request

* **URL**: `/api/audit-contract`
* **Method**: `GET`
* **Content-Type**: `application/json`

### Query Parameters

| Parameter | Type             | Required | Description                                                                                                                                            |
| --------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `chain`   | `SupportedChain` | Yes      | Target blockchain (case-insensitive)                                                                                                                   |
| `address` | `string`         | Yes      | Contract address to audit                                                                                                                              |
| `fast`    | `boolean`        | No       | If `true`, runs a lightweight safe/unsafe classification instead of a full audit (default: `false`)                                                    |
| `refresh` | `boolean`        | No       | If `true`, forces a fresh LLM re-audit when the current audit is older than **1h**. Within the cooldown, returns the cached audit with `fresh: false`. |

### Response

**Full Audit Response (`200 OK`)**

```typescript
{
  audit: {
    // identity
    address: string;
    chain: SupportedChain;
    name: string | null;
    symbol: string | null;
    contractType: ContractType;
    decimals: number | null;
    logoUrl: string | null;

    // verdict
    isSafe: boolean;                   // may differ from the LLM's original call — see "Dynamic mitigation" below
    description: string;               // overridden to a canned message if all vulns are mitigated on-chain
    vulnerabilities: Vulnerability[];

    // source / provenance
    openSource: boolean;
    sourceType: 'verified' | 'decompiled' | 'none';
    isDecompiled: boolean;
    isProxy: boolean;
    implementationAddress: string | null;
    origin: 'ETHERSCAN' | 'ROUTESCAN' | 'BLOCKVISION' | 'SOURCIFY' | null;
    creatorAddress: string | null;
    contractHash: string | null;       // creation tx hash

    // launchpad / dedup short-circuits
    isWhitelistedLaunchpad: boolean;
    launchpadName: string | null;
    dedupedFromAddress: string | null; // set when the result was reused from an identical bytecode
    dedupedFromChain: SupportedChain | null;

    // on-chain runtime state (NEW in v2)
    ownerAddress: string | null;       // owner() return value, or null if contract has no owner()
    ownerRenounced: boolean | null;    // true iff owner() == 0x0; null if contract is non-Ownable
    pairCreatedAt: string | null;      // ISO timestamp of DEX pair creation (source: Naos); null if not tracked

    // market data (sourced from Naos / indexer / RPC fallback)
    priceUsd: number | null;
    marketCap: number | null;
    liquidity: number | null;
    topHolders: TokenHolder[];

    // pool / hook (Uniswap V4)
    hookAddress: string | null;
    hookAudit: HookAudit | null;

    // audit meta
    createdAt: string;                 // ISO timestamp
  },
  fresh?: boolean;                     // only present when ?refresh=true — true if a re-run happened, false if within cooldown
}
```

**Fast Audit Response (`200 OK`)** — when `fast=true`

```typescript
{
  isSafe: boolean;
  description: string;
  fresh?: boolean;                     // only present when ?refresh=true
}
```

**Error Response**

```json
{
  "error": "Description of the error"
}
```

### Dynamic mitigation — how `isSafe` is computed

Every call to `/audit-contract` re-evaluates the runtime state of each access-control gate the LLM identified, via a **single batched `Multicall3`** to the contract (\~100-200ms overhead on cache hits, no LLM re-run).

For each vulnerability the LLM returns a structured `gatedBy` pointer describing *what controls the dangerous function*. The server resolves each gate on-chain:

| `gatedBy.type` | Resolver                                                                      | Neutralized when                         |
| -------------- | ----------------------------------------------------------------------------- | ---------------------------------------- |
| `modifier`     | Call `owner()` / `admin()` for known owner-like modifiers                     | Returns `0x0` (renounced)                |
| `role`         | Call `getRoleMemberCount(keccak256(roleName))` (OZ `AccessControlEnumerable`) | Returns `0`                              |
| `publicGetter` | Call `<varName>()` — auto-generated getter                                    | Returns `0x0` or a burn address          |
| `storageVar`   | *Not resolvable* (private/internal)                                           | Never — stays unresolved (safe fallback) |
| `none`         | *Not resolvable* (no gate)                                                    | Never                                    |

Each vulnerability gets two extra fields after resolution:

* `mitigated: boolean` — `true` only when the gate is proven neutralized on-chain.
* `gateReason: string` — human-readable outcome (e.g. `"Owner renounced"`, `"MINTER_ROLE has 2 active holders"`, `"_taxWallet is private storage — can't auto-verify"`, `"On-chain check reverted — gate couldn't be verified"`).

The top-level `isSafe` becomes `true` if either:

1. The LLM originally classified the contract as safe, OR
2. **All** vulnerabilities have `mitigated: true` (i.e. every gate is neutralized on-chain).

When case 2 triggers, `description` is replaced by a canned message:

> "All admin-gated functions are disabled on-chain — the contract is effectively immutable and no longer poses the flagged risks."

**Invariant** — a vulnerability is only marked `mitigated: true` with positive on-chain proof. Unverifiable gates (private storage, unknown modifiers, reverting calls) stay `mitigated: false`. There are zero false-positives on the safety verdict.

### Examples

{% tabs %}
{% tab title="JavaScript — Full Audit" %}

```javascript
const { audit } = await fetch(
  `https://audit.serializedlabs.com/api/audit-contract?chain=${chain}&address=${contract}`
).then(r => r.json());

// Show only actively-exploitable vulns
const active = audit.vulnerabilities.filter(v => !v.mitigated);

// Visualize the gate state
for (const v of audit.vulnerabilities) {
  console.log(`${v.type}: ${v.gateReason}`); // e.g. "HiddenFees: Owner renounced"
}

// Show pair age
if (audit.pairCreatedAt) {
  const ageDays = (Date.now() - new Date(audit.pairCreatedAt).getTime()) / 86_400_000;
  console.log(`Pair age: ${ageDays.toFixed(0)} days`);
}
```

{% endtab %}

{% tab title="JavaScript — Force Refresh" %}

```javascript
// Force a fresh LLM re-audit (only if > 1h old)
const res = await fetch(
  `https://audit.serializedlabs.com/api/audit-contract?chain=${chain}&address=${contract}&refresh=true`
).then(r => r.json());

console.log(res.fresh);   // true if a re-run happened, false if within cooldown
console.log(res.audit.createdAt);
```

{% endtab %}

{% tab title="JavaScript — Fast Mode" %}

```javascript
const result = await fetch(
  `https://audit.serializedlabs.com/api/audit-contract?chain=${chain}&address=${contract}&fast=true`
).then(r => r.json());

console.log(result.isSafe);
console.log(result.description);
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = f"https://audit.serializedlabs.com/api/audit-contract?chain={chain}&address={contract}"
response = requests.get(url).json()
audit = response["audit"]

# Active (non-mitigated) vulns
active = [v for v in audit["vulnerabilities"] if not v.get("mitigated")]
```

{% endtab %}
{% endtabs %}

### Types

```typescript
enum SupportedChain {
  ETH = "ETH",
  OP = "OP",
  ARB = "ARB",
  BASE = "BASE",
  BSC = "BSC",
  AVAX = "AVAX",
  APE = "APE",
  BLAST = "BLAST",
  LINEA = "LINEA",
  MANTLE = "MANTLE",
  POLYGON = "POLYGON",
  ZKEVM = "ZKEVM",
  SCROLL = "SCROLL",
  SONIC = "SONIC",
  ZKSYNC = "ZKSYNC",
  ABSTRACT = "ABSTRACT",
  MONAD = "MONAD",
  PLASMA = "PLASMA",
  MEGAETH = "MEGAETH",
}

type ContractType = 'ERC-20' | 'ERC-721' | 'UNKNOWN';

interface Vulnerability {
  description: string;
  severity: number;                 // 25..100
  code: string;
  type: VulnerabilityType;
  gatedBy?: GatedBy;                // structured pointer to the access-control gate
  mitigated?: boolean;              // true iff gate is neutralized on-chain
  gateReason?: string | null;       // human-readable outcome of gate resolution
}

type VulnerabilityType =
  | "UnlimitedMinting"
  | "UnauthorizedTransfer"
  | "HiddenFees"
  | "LiquidityDrain"
  | "MaliciousUpgrade"
  | "UnsafeExternalCall"
  | "BlacklistAbuse";

type GatedBy =
  | { type: 'modifier';     value: string }  // e.g. onlyOwner, onlyAdmin
  | { type: 'role';         value: string }  // OZ AccessControl role constant, e.g. MINTER_ROLE
  | { type: 'publicGetter'; value: string }  // var with auto getter, e.g. "taxWallet" in `address public taxWallet`
  | { type: 'storageVar';   value: string }  // private/internal var — unresolvable on-chain
  | { type: 'none' };                         // unrestricted OR non-access-control risk

interface TokenHolder {
  address: string;
  quantity: string;
  percentage: number | null;
}

interface HookAudit {
  isSafe: boolean | null;
  description: string | null;
  vulnerabilities: Vulnerability[];
  address: string | null;
}
```

### Vulnerability Types Explained

| Type                     | Description                                                                                                                       |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| **UnlimitedMinting**     | Owner can mint unlimited tokens or manipulate the total supply, leading to inflation and value dilution.                          |
| **UnauthorizedTransfer** | Owner can transfer tokens from user wallets without consent, or trick users into granting unlimited allowances to steal funds.    |
| **HiddenFees**           | Hidden or owner-adjustable transfer fees that can be changed at any time, allowing the owner to drain value from transactions.    |
| **LiquidityDrain**       | Owner can withdraw contract funds or liquidity pool assets, commonly known as a "rug pull".                                       |
| **MaliciousUpgrade**     | Unrestricted upgrade patterns or self-destruct functions that allow the owner to change contract behavior or destroy it entirely. |
| **UnsafeExternalCall**   | Unsafe external calls or reentrancy vulnerabilities that can be exploited to drain funds or manipulate contract state.            |
| **BlacklistAbuse**       | Owner can arbitrarily blacklist or freeze addresses, blocking all transfers for targeted users.                                   |

### Backwards compatibility

* All new v2 fields (`gatedBy`, `mitigated`, `gateReason`, `ownerAddress`, `ownerRenounced`, `pairCreatedAt`, `fresh`) are **additive**. Clients ignoring them continue to function.
* Legacy audits created before v2 (rows in our DB without `gatedBy`) return vulnerabilities with `mitigated: null` / `gateReason: null` — no crash, no mitigation applied, LLM's original verdict preserved.
* `isSafe` semantics changed in v2: it now reflects runtime on-chain state in addition to the LLM's classification. If you previously derived safety from `vulnerabilities.length` or from parsing `description`, switch to trusting `isSafe` directly.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.serializedaudit.io/getting-started/quickstart/get-api-audit-less-than-network-greater-than-less-than-contract-greater-than.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
