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

# Transaction Simulation

> Add pre-signing transaction simulation, EIP-712 permit analysis, and approval risk checks to your wallet or dApp so users see what they sign in advance.

You visit a "token claim" site. The transaction looks normal—just a contract interaction. You approve it without a second thought. 48 hours later, your entire wallet is drained through a permit signature you didn't understand. Transaction simulation would have caught it. This guide shows you how to build that last line of defense.

## Why Transaction Simulation Matters

<CardGroup cols={3}>
  <Card title="Last Line of Defense" icon="shield-check">
    Users trust your app to protect them—make that trust count
  </Card>

  <Card title="Permit Protection" icon="signature">
    Catch EIP-712 signatures that can drain wallets without transactions
  </Card>

  <Card title="Approval Awareness" icon="list-check">
    Show users their existing exposure before they add more
  </Card>
</CardGroup>

**Why developers choose Webacy:**

* **Pre-signing simulation** — Show users exactly what will happen before they approve
* **EIP-712 signature analysis** — Decode and risk-score permit/signature requests
* **Approval risk detection** — Identify dangerous existing approvals
* **Recipient verification** — Catch address poisoning before users confirm
* **Multi-chain support** — Same APIs for ETH, Polygon, Arbitrum, Base, and more

***

## Prerequisites

Before implementing transaction simulation, ensure you have:

* A Webacy API key ([sign up here](https://developers.webacy.co/billing))
* Basic familiarity with REST APIs or the [Webacy SDK](../sdk/installation)
* Your application's transaction signing flow identified for integration

***

## Pre-Signing Protection

The moment before a user signs a transaction is your last chance to protect them.

### Transaction Simulation

Simulate every transaction before it gets signed.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.webacy.com/scan/transaction" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "walletAddress": "0xUserAddress...",
      "tx": {
        "from": "0xUserAddress...",
        "to": "0xContractAddress...",
        "data": "0xTransactionData...",
        "value": "0x0"
      },
      "chain": 1,
      "domain": "app.example.com"
    }'
  ```

  ```typescript TypeScript theme={null}
  import { ThreatClient, Chain } from '@webacy-xyz/sdk';

  const client = new ThreatClient({ apiKey: process.env.WEBACY_API_KEY });

  const simulation = await client.scan.scanTransaction(
    '0xUserAddress...',
    {
      tx: {
        from: '0xUserAddress...',
        to: '0xContractAddress...',
        data: '0xTransactionData...',
        value: '0x0',
      },
      chain: 1, // Ethereum mainnet
      domain: 'connected-dapp.com',
    }
  );

  // Display to user before signing
  console.log(`Risk level: ${simulation.riskLevel}`);

  for (const change of simulation.assetChanges ?? []) {
    if (change.type === 'transfer_out') {
      console.log(`⚠️ You will SEND: ${change.amount} ${change.symbol}`);
    } else if (change.type === 'transfer_in') {
      console.log(`✓ You will RECEIVE: ${change.amount} ${change.symbol}`);
    } else if (change.type === 'approval') {
      console.log(`🔓 APPROVAL: ${change.spender} can spend ${change.amount} ${change.symbol}`);
    }
  }
  ```

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

  response = requests.post(
      "https://api.webacy.com/scan/transaction",
      headers={
          "x-api-key": "YOUR_API_KEY",
          "Content-Type": "application/json"
      },
      json={
          "walletAddress": "0xUserAddress...",
          "tx": {
              "from": "0xUserAddress...",
              "to": "0xContractAddress...",
              "data": "0xTransactionData...",
              "value": "0x0"
          },
          "chain": 1,
          "domain": "app.example.com"
      }
  )
  simulation = response.json()
  ```
</CodeGroup>

**What to show users:**

| Field            | What It Means               | Display Priority            |
| ---------------- | --------------------------- | --------------------------- |
| `assetChanges[]` | Tokens/ETH moving in or out | Always show                 |
| `riskLevel`      | Overall transaction risk    | Show with color coding      |
| `warnings[]`     | Specific threats detected   | Show prominently if present |

<Warning>
  **Supported chain IDs:** 1 (ETH), 56 (BSC), 137 (Polygon), 10 (Optimism), 42161 (Arbitrum), 8453 (Base)
</Warning>

### EIP-712 Permit Signature Verification

Permit signatures are one of the most dangerous attack vectors. A single signature can authorize unlimited token spending without any on-chain transaction.

<Warning>
  **The Silent Drain**: Unlike regular transactions, permit signatures don't show up on block explorers until the attacker uses them. A user can sign a permit and see nothing happen—until days later when everything is gone.
</Warning>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.webacy.com/scan/eip712" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "walletAddress": "0xUserAddress...",
      "msg": {
        "from": "0xUserAddress...",
        "data": {
          "types": {
            "EIP712Domain": [
              {"name": "name", "type": "string"},
              {"name": "version", "type": "string"},
              {"name": "chainId", "type": "uint256"},
              {"name": "verifyingContract", "type": "address"}
            ],
            "Permit": [
              {"name": "owner", "type": "address"},
              {"name": "spender", "type": "address"},
              {"name": "value", "type": "uint256"},
              {"name": "nonce", "type": "uint256"},
              {"name": "deadline", "type": "uint256"}
            ]
          },
          "primaryType": "Permit",
          "domain": {
            "name": "USD Coin",
            "version": "2",
            "chainId": 1,
            "verifyingContract": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
          },
          "message": {
            "owner": "0xUserAddress...",
            "spender": "0xSuspiciousContract...",
            "value": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
            "nonce": 0,
            "deadline": 1893456000
          }
        }
      },
      "domain": "suspicious-site.com"
    }'
  ```

  ```typescript TypeScript theme={null}
  const result = await client.scan.scanEip712(
    '0xUserAddress...',
    {
      msg: {
        from: '0xUserAddress...',
        data: {
          types: {
            EIP712Domain: [
              { name: 'name', type: 'string' },
              { name: 'version', type: 'string' },
              { name: 'chainId', type: 'uint256' },
              { name: 'verifyingContract', type: 'address' },
            ],
            Permit: [
              { name: 'owner', type: 'address' },
              { name: 'spender', type: 'address' },
              { name: 'value', type: 'uint256' },
              { name: 'nonce', type: 'uint256' },
              { name: 'deadline', type: 'uint256' },
            ],
          },
          primaryType: 'Permit',
          domain: {
            name: 'USD Coin',
            version: '2',
            chainId: 1,
            verifyingContract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
          },
          message: {
            owner: '0xUserAddress...',
            spender: '0xSuspiciousContract...',
            value: '115792089237316195423570985008687907853269984665640564039457584007913129639935',
            nonce: 0,
            deadline: 1893456000,
          },
        },
      },
      domain: 'suspicious-site.com',
    }
  );

  // Critical: Check risk level before allowing signature
  if (result.riskLevel === 'critical' || result.riskLevel === 'high') {
    console.error('⛔ DANGEROUS SIGNATURE DETECTED');
    console.error(`Type: ${result.messageType}`);
    console.error('This signature could drain your entire wallet.');
    // Block the signature
  }
  ```

  ```python Python theme={null}
  response = requests.post(
      "https://api.webacy.com/scan/eip712",
      headers={
          "x-api-key": "YOUR_API_KEY",
          "Content-Type": "application/json"
      },
      json={
          "walletAddress": "0xUserAddress...",
          "msg": {
              "from": "0xUserAddress...",
              "data": {
                  "types": {
                      "EIP712Domain": [
                          {"name": "name", "type": "string"},
                          {"name": "version", "type": "string"},
                          {"name": "chainId", "type": "uint256"},
                          {"name": "verifyingContract", "type": "address"}
                      ],
                      "Permit": [
                          {"name": "owner", "type": "address"},
                          {"name": "spender", "type": "address"},
                          {"name": "value", "type": "uint256"},
                          {"name": "nonce", "type": "uint256"},
                          {"name": "deadline", "type": "uint256"}
                      ]
                  },
                  "primaryType": "Permit",
                  "domain": {
                      "name": "USD Coin",
                      "version": "2",
                      "chainId": 1,
                      "verifyingContract": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
                  },
                  "message": {
                      "owner": "0xUserAddress...",
                      "spender": "0xSuspiciousContract...",
                      "value": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
                      "nonce": 0,
                      "deadline": 1893456000
                  }
              }
          },
          "domain": "suspicious-site.com"
      }
  )
  ```
</CodeGroup>

**Key red flags to surface:**

| Red Flag              | What It Means                       |
| --------------------- | ----------------------------------- |
| `value` = max uint256 | Unlimited token approval            |
| Unknown `spender`     | Approving unknown contract          |
| Long `deadline`       | Signature valid for extended period |
| High `riskLevel`      | Known malicious pattern detected    |

***

## Approval Risk Management

Users often have approvals they've forgotten about. Help them understand their exposure.

### Current Approval Scanning

Show users their existing approvals before they add more.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.webacy.com/wallets/0xUserAddress.../approvals?chain=eth" \
    -H "x-api-key: YOUR_API_KEY"
  ```

  ```typescript TypeScript theme={null}
  const approvals = await client.wallets.getApprovals(
    '0xUserAddress...',
    { chain: Chain.ETH }
  );

  // Categorize by risk
  const dangerous = approvals.approvals.filter(a =>
    a.isUnlimited && (a.spenderRisk === 'high' || !a.spenderVerified)
  );

  const unlimited = approvals.approvals.filter(a =>
    a.isUnlimited && a.spenderRisk !== 'high'
  );

  console.log(`⛔ ${dangerous.length} dangerous approvals`);
  console.log(`⚠️ ${unlimited.length} unlimited approvals`);
  console.log(`Total approvals: ${approvals.approvals.length}`);
  ```

  ```python Python theme={null}
  response = requests.get(
      "https://api.webacy.com/wallets/0xUserAddress.../approvals",
      params={"chain": "eth"},
      headers={"x-api-key": "YOUR_API_KEY"}
  )
  ```
</CodeGroup>

### Dangerous Approval Detection

Flag approvals that could be used to drain the wallet.

```typescript theme={null}
interface ApprovalRisk {
  token: string;
  spender: string;
  riskFactors: string[];
  recommendedAction: 'revoke' | 'review' | 'ok';
}

function assessApprovalRisk(approval: Approval): ApprovalRisk {
  const riskFactors: string[] = [];

  // Check for unlimited approval
  if (approval.isUnlimited) {
    riskFactors.push('Unlimited approval amount');
  }

  // Check if spender is known
  if (!approval.spenderName) {
    riskFactors.push('Unknown spender contract');
  }

  // Check if spender contract still exists
  if (approval.spenderDeployed === false) {
    riskFactors.push('Spender contract no longer exists');
  }

  // Check spender risk score
  if (approval.spenderRisk === 'high') {
    riskFactors.push('Spender flagged as high risk');
  }

  // Determine action
  let recommendedAction: 'revoke' | 'review' | 'ok' = 'ok';
  if (riskFactors.length >= 2 || approval.spenderRisk === 'high') {
    recommendedAction = 'revoke';
  } else if (riskFactors.length === 1) {
    recommendedAction = 'review';
  }

  return {
    token: approval.symbol,
    spender: approval.spender,
    riskFactors,
    recommendedAction,
  };
}
```

***

## Recipient Verification

Before a user sends funds, verify the recipient address isn't part of an attack.

### Address Poisoning Check

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.webacy.com/addresses/0xRecipient.../poisoning?chain=eth" \
    -H "x-api-key: YOUR_API_KEY"
  ```

  ```typescript TypeScript theme={null}
  const poisoning = await client.addresses.checkPoisoning(
    '0xRecipient...',
    { chain: Chain.ETH }
  );

  if (poisoning.is_poisoned) {
    // Stop the transaction
    return {
      blocked: true,
      reason: 'Address Poisoning Detected',
      message: 'This address appears to be part of an address poisoning attack. ' +
               'It closely resembles a legitimate address you\'ve interacted with. ' +
               'Please verify the full address character by character.',
      similarTo: poisoning.similar_addresses
    };
  }
  ```

  ```python Python theme={null}
  response = requests.get(
      "https://api.webacy.com/addresses/0xRecipient.../poisoning",
      params={"chain": "eth"},
      headers={"x-api-key": "YOUR_API_KEY"}
  )
  ```
</CodeGroup>

### Risk Profiling

Get a quick risk assessment of any recipient.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.webacy.com/addresses/0xRecipient.../quick-profile?chain=eth" \
    -H "x-api-key: YOUR_API_KEY"
  ```

  ```typescript TypeScript theme={null}
  const profile = await client.addresses.getQuickProfile(
    '0xRecipient...',
    { chain: Chain.ETH }
  );

  // Show risk information on the confirmation screen
  const riskDisplay = {
    score: profile.riskScore,
    level: profile.riskLevel,
    accountAge: profile.accountAge,
    warning: profile.riskLevel === 'high' ?
      'This address has been flagged as high risk' : null
  };
  ```

  ```python Python theme={null}
  response = requests.get(
      "https://api.webacy.com/addresses/0xRecipient.../quick-profile",
      params={"chain": "eth"},
      headers={"x-api-key": "YOUR_API_KEY"}
  )
  ```
</CodeGroup>

***

## Complete Integration Workflow

### Pre-Signing Check Flow

```mermaid theme={null}
flowchart TD
    A[Transaction/Signature Request] --> B{Is EIP-712 Signature?}
    B -->|Yes| C[Scan EIP-712]
    B -->|No| D{Has Recipient?}
    D -->|Yes| E[Check Address Poisoning]
    D -->|No| F[Simulate Transaction]
    E --> F
    C --> G{Risk Level?}
    F --> G
    G -->|Critical| H["⛔ BLOCK - Don't allow signing"]
    G -->|High| I["⚠️ Strong Warning + Details"]
    G -->|Medium| J["Show Caution + Asset Changes"]
    G -->|Low| K["Show Asset Changes"]
    I --> L{User Confirms Warning?}
    J --> M[Allow Signing]
    K --> M
    L -->|Yes| M
    L -->|No| N[Cancel Transaction]
```

### Approval Health Check Flow

```mermaid theme={null}
flowchart TD
    A[User Opens Wallet] --> B[Fetch Current Approvals]
    B --> C{Any Dangerous Approvals?}
    C -->|Yes| D["Show Alert Badge"]
    C -->|No| E[Normal View]
    D --> F[User Views Approvals]
    F --> G[Categorize by Risk]
    G --> H["🔴 Dangerous (Revoke Now)"]
    G --> I["🟡 Unlimited (Review)"]
    G --> J["🟢 Safe"]
```

### Full TypeScript Implementation

<Accordion title="Complete Transaction Security Module">
  ```typescript theme={null}
  import { ThreatClient, Chain } from '@webacy-xyz/sdk';

  const client = new ThreatClient({
    apiKey: process.env.WEBACY_API_KEY,
    defaultChain: Chain.ETH,
  });

  type RiskLevel = 'low' | 'medium' | 'high' | 'critical';

  interface PreSigningResult {
    allowed: boolean;
    riskLevel: RiskLevel;
    assetChanges?: Array<{
      type: string;
      symbol: string;
      amount: string;
    }>;
    warnings: string[];
    blockReason?: string;
  }

  // Main pre-signing check
  async function preSigningCheck(
    userAddress: string,
    request: TransactionRequest | EIP712Request,
    chain: number,
    dappDomain?: string
  ): Promise<PreSigningResult> {

    // Determine request type
    if (isEIP712Request(request)) {
      return checkEIP712Signature(userAddress, request, dappDomain);
    } else {
      return checkTransaction(userAddress, request, chain, dappDomain);
    }
  }

  // EIP-712 signature check
  async function checkEIP712Signature(
    userAddress: string,
    request: EIP712Request,
    dappDomain?: string
  ): Promise<PreSigningResult> {
    const result = await client.scan.scanEip712(userAddress, {
      msg: {
        from: userAddress,
        data: request.typedData,
      },
      domain: dappDomain,
    });

    const warnings: string[] = [];

    // Check for permit signatures
    if (result.messageType?.toLowerCase().includes('permit')) {
      warnings.push('This is a PERMIT signature - it can authorize token spending');

      // Check for unlimited approval
      const message = request.typedData.message;
      if (message.value === '115792089237316195423570985008687907853269984665640564039457584007913129639935') {
        warnings.push('UNLIMITED token approval requested');
      }
    }

    // Block critical risk
    if (result.riskLevel === 'critical') {
      return {
        allowed: false,
        riskLevel: 'critical',
        warnings,
        blockReason: 'This signature matches known malicious patterns and could drain your wallet.',
      };
    }

    return {
      allowed: true,
      riskLevel: result.riskLevel as RiskLevel,
      warnings,
    };
  }

  // Transaction check
  async function checkTransaction(
    userAddress: string,
    request: TransactionRequest,
    chain: number,
    dappDomain?: string
  ): Promise<PreSigningResult> {
    const warnings: string[] = [];

    // Check recipient for poisoning if present
    if (request.to) {
      const poisoning = await client.addresses.checkPoisoning(request.to, {
        chain: chainIdToChain(chain),
      });

      if (poisoning.is_poisoned) {
        return {
          allowed: false,
          riskLevel: 'critical',
          warnings: ['Address poisoning attack detected'],
          blockReason: 'This address appears to be part of an address poisoning scam.',
        };
      }
    }

    // Simulate transaction
    const simulation = await client.scan.scanTransaction(userAddress, {
      tx: {
        from: userAddress,
        to: request.to,
        data: request.data,
        value: request.value || '0x0',
      },
      chain,
      domain: dappDomain,
    });

    // Collect warnings
    warnings.push(...(simulation.warnings || []));

    // Check for dangerous approvals in asset changes
    const approvals = simulation.assetChanges?.filter(c => c.type === 'approval') || [];
    for (const approval of approvals) {
      if (approval.amount === 'unlimited') {
        warnings.push(`Unlimited approval to ${approval.spender}`);
      }
    }

    // Block critical transactions
    if (simulation.riskLevel === 'critical') {
      return {
        allowed: false,
        riskLevel: 'critical',
        assetChanges: simulation.assetChanges,
        warnings,
        blockReason: 'This transaction has been identified as dangerous.',
      };
    }

    return {
      allowed: true,
      riskLevel: simulation.riskLevel as RiskLevel,
      assetChanges: simulation.assetChanges,
      warnings,
    };
  }

  // Approval health check
  async function getApprovalHealth(
    userAddress: string,
    chain: Chain
  ): Promise<{
    dangerous: number;
    unlimited: number;
    total: number;
    needsAttention: boolean;
    approvals: ApprovalInfo[];
  }> {
    const result = await client.wallets.getApprovals(userAddress, { chain });

    const categorized = result.approvals.map(a => ({
      ...a,
      category: categorizeApproval(a),
    }));

    const dangerous = categorized.filter(a => a.category === 'dangerous').length;
    const unlimited = categorized.filter(a => a.category === 'unlimited').length;

    return {
      dangerous,
      unlimited,
      total: result.approvals.length,
      needsAttention: dangerous > 0,
      approvals: categorized,
    };
  }

  function categorizeApproval(approval: Approval): 'dangerous' | 'unlimited' | 'safe' {
    if (approval.spenderRisk === 'high' || !approval.spenderVerified) {
      return 'dangerous';
    }
    if (approval.isUnlimited) {
      return 'unlimited';
    }
    return 'safe';
  }

  // Helper to convert chain ID to Chain enum
  function chainIdToChain(chainId: number): Chain {
    const mapping: Record<number, Chain> = {
      1: Chain.ETH,
      56: Chain.BSC,
      137: Chain.POL,
      10: Chain.OPT,
      42161: Chain.ARB,
      8453: Chain.BASE,
    };
    return mapping[chainId] || Chain.ETH;
  }

  // Type guards
  function isEIP712Request(request: any): request is EIP712Request {
    return 'typedData' in request;
  }

  interface TransactionRequest {
    to?: string;
    data?: string;
    value?: string;
  }

  interface EIP712Request {
    typedData: {
      types: Record<string, Array<{ name: string; type: string }>>;
      primaryType: string;
      domain: Record<string, any>;
      message: Record<string, any>;
    };
  }
  ```
</Accordion>

***

## Example Addresses for Testing

### Permit Phishing

| Address                                      | Chain | Description                      |
| -------------------------------------------- | ----- | -------------------------------- |
| `0x84672cc56b6dad30cfa5f9751d9ccae6c39e29cd` | ETH   | AI Protocol user permit phishing |
| `0x624Fc3Dc249E37E8BFd3e834C4dF81Ff2dA1D0Ca` | BSC   | Malicious permit scammer         |

### Address Poisoning

| Address                                      | Chain | Description           |
| -------------------------------------------- | ----- | --------------------- |
| `0xd9A1C3788D81257612E2581A6ea0aDa244853a91` | ETH   | \$68M WBTC attack     |
| `0x5f90e59d0a03fd2f8c56b8cc896c5b42594eb3a0` | ETH   | \$50M poisoning drain |

### Known Drainers

| Address                                      | Chain | Attribution                     |
| -------------------------------------------- | ----- | ------------------------------- |
| `0xe7d13137923142a0424771e1778865b88752b3c7` | ETH   | WalletConnect phishing campaign |
| `0x1aDf5DAc035AE7FEC116e8345e005FB88d542f53` | ETH   | Phishing scammer                |

### Clean Addresses (for comparison)

| Address                                      | Chain | Description                 |
| -------------------------------------------- | ----- | --------------------------- |
| `0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045` | ETH   | Vitalik's wallet (low risk) |
| `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | ETH   | USDC contract (verified)    |

***

## API Quick Reference

| Endpoint                                 | Use Case               | Response Time |
| ---------------------------------------- | ---------------------- | ------------- |
| `POST /scan/transaction`                 | Transaction simulation | \~500ms       |
| `POST /scan/eip712`                      | Signature analysis     | \~300ms       |
| `GET /addresses/{address}/poisoning`     | Address poisoning      | \~300ms       |
| `GET /addresses/{address}/quick-profile` | Recipient risk         | \~200ms       |
| `GET /wallets/{address}/approvals`       | Approval list          | \~400ms       |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Get Your API Key" icon="key" href="https://developers.webacy.co/billing">
    Start protecting your users
  </Card>

  <Card title="API Reference" icon="book" href="../api-reference/introduction">
    Complete endpoint documentation
  </Card>

  <Card title="Install the SDK" icon="download" href="../sdk/installation">
    TypeScript SDK for integration
  </Card>

  <Card title="Address Poisoning" icon="warning" href="../glossary/address-poisoning">
    Learn more about this attack vector
  </Card>
</CardGroup>
