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

# EVM Phishing Detection

> Contract-level phishing analysis that catches allowance drain traps, permit signature abuse, and silent ETH forwarding

## Overview

Most phishing attacks don't look like phishing — they look like normal contracts.
TxShield's phishing endpoint **simulates what a contract actually does** when
interacted with, not just what it claims to do.

Three attack vectors are checked in every scan:

* **Allowance Drain Traps** — contracts that call `transferFrom` to silently
  sweep your approved ERC20 tokens to an attacker wallet
* **Permit Signature Abuse** — contracts that consume a signed `permit()`
  to grant unlimited allowance to a malicious address without a second transaction
* **Native ETH Forwarding** — contracts that silently forward any ETH you send
  to an external address instead of doing what they claim

***

## Endpoint

**`POST`** `/api/phishing/phishing-checks`

***

## Request

### Headers

Content-Type: application/json
Authorization: Bearer txs\_your\_api\_key\_here

### Body

```json theme={null}
{
  "targetContractAddress": "0x3e391e5cb8ea766c93134faf486e6393158032c2",
  "chainId": 1
}
```

### Parameters

| Field                   | Type     | Required | Description                                           |
| ----------------------- | -------- | -------- | ----------------------------------------------------- |
| `targetContractAddress` | `string` | ✅        | The contract address to analyze for phishing behavior |
| `chainId`               | `number` | ✅        | Chain to run the check on. See supported IDs below    |

### Supported Chain IDs

| Chain           | chainId |
| --------------- | ------- |
| Ethereum        | `1`     |
| BNB Smart Chain | `56`    |
| Base            | `8453`  |
| Arbitrum        | `42161` |

***

## Response

```json theme={null}
{
  "contractAddress": "0x3e391e5cb8ea766c93134faf486e6393158032c2",
  "chain": "ethereum",
  "verdict": "SAFE",
  "riskScore": 0,
  "flagCount": 0,
  "isEmpty": false,
  "inconclusive": false,
  "reason": null,
  "checks": [
    {
      "name": "Allowance Drain Trap",
      "triggered": false,
      "severity": "CRITICAL",
      "detail": "Target did not attempt to drain approved ERC20 allowances."
    },
    {
      "name": "Permit Signature Abuse",
      "triggered": false,
      "severity": "CRITICAL",
      "detail": "Target did not attempt to consume permit signatures."
    },
    {
      "name": "Native ETH Forwarder",
      "triggered": false,
      "severity": "MEDIUM",
      "detail": "Target retained ETH — no silent forwarding detected."
    }
  ],
  "raw": {
    "allowanceDrained": false,
    "drainedTo": null,
    "drainedAmount": null,
    "permitAbused": false,
    "permitGrantedTo": null,
    "etherForwarded": false,
    "etherForwardedTo": null
  },
  "detectedAt": "2025-01-01T00:00:00.000Z"
}
```

***

## Response Fields Explained

### Top Level

| Field             | Type             | Description                                                          |
| ----------------- | ---------------- | -------------------------------------------------------------------- |
| `contractAddress` | `string`         | The contract address that was analyzed                               |
| `chain`           | `string`         | The chain the analysis ran on                                        |
| `verdict`         | `string`         | Human-readable risk verdict. See verdict table below                 |
| `riskScore`       | `number`         | Composite risk score from `0` to `100`                               |
| `flagCount`       | `number`         | Number of phishing checks that triggered                             |
| `isEmpty`         | `boolean`        | `true` if the address has no deployed bytecode                       |
| `inconclusive`    | `boolean`        | `true` if analysis could not complete definitively                   |
| `reason`          | `string \| null` | Populated when `isEmpty` or `inconclusive` is `true`, explaining why |
| `checks`          | `array`          | Results of each individual phishing check                            |
| `raw`             | `object`         | Raw extracted values for direct frontend consumption                 |
| `detectedAt`      | `string`         | ISO 8601 timestamp of when the analysis ran                          |

***

### `checks[]` — Each Phishing Check

| Field       | Type      | Description                                                      |
| ----------- | --------- | ---------------------------------------------------------------- |
| `name`      | `string`  | Name of the phishing check                                       |
| `triggered` | `boolean` | Whether this specific attack vector was detected                 |
| `severity`  | `string`  | `"CRITICAL"` or `"MEDIUM"` — the impact level if triggered       |
| `detail`    | `string`  | Human-readable explanation of what was found (or confirmed safe) |

***

### `raw` — Extracted Attack Data

| Field              | Type             | Description                                                          |
| ------------------ | ---------------- | -------------------------------------------------------------------- |
| `allowanceDrained` | `boolean`        | Whether a `transferFrom` drain was detected                          |
| `drainedTo`        | `string \| null` | Attacker address that received the drained tokens                    |
| `drainedAmount`    | `string \| null` | Amount of tokens routed to the attacker                              |
| `permitAbused`     | `boolean`        | Whether a `permit()` signature was consumed maliciously              |
| `permitGrantedTo`  | `string \| null` | Address that received the permit-granted allowance                   |
| `etherForwarded`   | `boolean`        | Whether received ETH was silently forwarded out                      |
| `etherForwardedTo` | `string \| null` | Address ETH was forwarded to. `null` if chain tracing is unavailable |

***

## Verdict Reference

| Verdict          | riskScore Range | Meaning                                                  |
| ---------------- | --------------- | -------------------------------------------------------- |
| `SAFE`           | `0 – 14`        | No phishing behavior detected                            |
| `EMPTY_CONTRACT` | —               | No bytecode at this address. Cannot be analyzed          |
| `INCONCLUSIVE`   | —               | Analysis ran but could not reach a definitive verdict    |
| `LOW_RISK`       | `15 – 39`       | Minor flags detected. Proceed with caution               |
| `HIGH_RISK`      | `40 – 74`       | Active phishing indicators found. Warn the user strongly |
| `CRITICAL`       | `75 – 100`      | Confirmed phishing attack vector. Block the transaction  |

<Note>
  `isEmpty` and `inconclusive` override `riskScore` in the verdict. Always
  check these flags first. An empty contract address is a common front for
  phishing flows where the real attack happens one hop away.
</Note>

***

## How to Read the Results

### The Safe Contract

```json theme={null}
{
  "verdict": "SAFE",
  "riskScore": 0,
  "flagCount": 0,
  "checks": [
    { "name": "Allowance Drain Trap",   "triggered": false },
    { "name": "Permit Signature Abuse", "triggered": false },
    { "name": "Native ETH Forwarder",  "triggered": false }
  ]
}
```

All three checks clean. No flags. Safe to proceed.

***

### The Allowance Drain Trap

```json theme={null}
{
  "verdict": "CRITICAL",
  "riskScore": 95,
  "flagCount": 1,
  "checks": [
    {
      "name": "Allowance Drain Trap",
      "triggered": true,
      "severity": "CRITICAL",
      "detail": "Target called transferFrom and routed 50000000000 tokens to 0xDead...Beef"
    },
    { "name": "Permit Signature Abuse", "triggered": false },
    { "name": "Native ETH Forwarder",  "triggered": false }
  ],
  "raw": {
    "allowanceDrained": true,
    "drainedTo": "0xDeadBeef...",
    "drainedAmount": "50000000000"
  }
}
```

The contract calls `transferFrom` and routes your approved tokens to an
attacker wallet. Classic approval phishing. Block immediately.

***

### The Permit Signature Abuser

```json theme={null}
{
  "verdict": "CRITICAL",
  "riskScore": 90,
  "flagCount": 1,
  "checks": [
    { "name": "Allowance Drain Trap", "triggered": false },
    {
      "name": "Permit Signature Abuse",
      "triggered": true,
      "severity": "CRITICAL",
      "detail": "Target called permit() and granted allowance to 0xDead...Beef"
    },
    { "name": "Native ETH Forwarder", "triggered": false }
  ],
  "raw": {
    "permitAbused": true,
    "permitGrantedTo": "0xDeadBeef..."
  }
}
```

The contract consumes a `permit()` signature to grant itself or a third
party unlimited token allowance — without ever asking for a second
approval transaction. The user thinks they're signing a gasless swap.
They're handing over their wallet.

***

### The Silent ETH Forwarder

```json theme={null}
{
  "verdict": "HIGH_RISK",
  "riskScore": 60,
  "flagCount": 1,
  "checks": [
    { "name": "Allowance Drain Trap",   "triggered": false },
    { "name": "Permit Signature Abuse", "triggered": false },
    {
      "name": "Native ETH Forwarder",
      "triggered": true,
      "severity": "MEDIUM",
      "detail": "Target forwarded received ETH to 0xDead...Beef"
    }
  ],
  "raw": {
    "etherForwarded": true,
    "etherForwardedTo": "0xDeadBeef..."
  }
}
```

Any ETH sent to this contract is silently routed to an external address.
Common in fake mint pages and impersonation contracts where the UI
looks legitimate but funds never stay in the contract.

***

### The Empty Contract

```json theme={null}
{
  "verdict": "EMPTY_CONTRACT",
  "riskScore": 0,
  "isEmpty": true,
  "reason": "No bytecode found at this address on this chain",
  "checks": []
}
```

Nothing is deployed here. This may be a pre-deployment address used
in a phishing link, a wrong chain, or a destroyed contract.
Do not interact with it.

***

## Code Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.txshield.xyz/api/phishing/phishing-checks \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer txs_your_api_key_here" \
    -d '{
      "targetContractAddress": "0x3e391e5cb8ea766c93134faf486e6393158032c2",
      "chainId": 1
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.txshield.xyz/api/phishing/phishing-checks',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer txs_your_api_key_here'
      },
      body: JSON.stringify({
        targetContractAddress: "0x3e391e5cb8ea766c93134faf486e6393158032c2",
        chainId: 1
      })
    }
  );

  const data = await response.json();

  // Recommended block logic
  const shouldBlock =
    data.verdict === "CRITICAL" ||
    data.verdict === "HIGH_RISK" ||
    data.riskScore >= 40 ||
    data.checks.some((c) => c.triggered && c.severity === "CRITICAL");

  const shouldWarn =
    data.verdict === "LOW_RISK" ||
    data.isEmpty ||
    data.inconclusive ||
    data.checks.some((c) => c.triggered);

  if (shouldBlock) {
    console.error(`PHISHING DETECTED — verdict: ${data.verdict}, score: ${data.riskScore}`);
  } else if (shouldWarn) {
    console.warn(`SUSPICIOUS CONTRACT — verdict: ${data.verdict}`);
  }
  ```

  ```python Python theme={null}
  import requests

  res = requests.post(
      "https://api.txshield.xyz/api/phishing/phishing-checks",
      headers={
          "Content-Type": "application/json",
          "Authorization": "Bearer txs_your_api_key_here"
      },
      json={
          "targetContractAddress": "0x3e391e5cb8ea766c93134faf486e6393158032c2",
          "chainId": 1
      }
  )

  data = res.json()

  if data["riskScore"] >= 40 or any(c["triggered"] for c in data["checks"]):
      triggered = [c["name"] for c in data["checks"] if c["triggered"]]
      print(f"BLOCK: verdict={data['verdict']}, flags={triggered}")
  ```
</CodeGroup>

***

## Error Responses

| Status | Error                               | Meaning                       |
| ------ | ----------------------------------- | ----------------------------- |
| `400`  | `targetContractAddress is required` | Missing address in body       |
| `400`  | `chainId is required`               | Missing chainId in body       |
| `401`  | `Missing Authorization header`      | No API key provided           |
| `403`  | `Invalid API key`                   | Key not found or revoked      |
| `429`  | `Analysis rate limit hit`           | Exceeded 10 req/min           |
| `500`  | `Phishing check failed`             | Internal error or RPC failure |

***

<CardGroup cols={2}>
  <Card title="EVM Honeypot Detection" icon="spider-web" href="/evm-honeypot-copied-1">
    Detect time-delayed tax traps and blacklists alongside phishing checks.
  </Card>

  <Card title="Authentication" icon="lock" href="/authentication">
    How to generate and use your API key.
  </Card>
</CardGroup>
