# Web3 Fraud Prevention

### The Problem

Smart contracts are blind to who calls them. When a user interacts directly with your smart contract (via Etherscan, a script, or any Web3 wallet), they bypass your frontend entirely, and any fraud detection you've implemented there.

This creates vulnerabilities in:

* **NFT marketplaces**: Wash trading, bot sniping during drops, Sybil attacks for airdrops
* **DeFi platforms**: Multi-account farming, bot-driven arbitrage abuse, reward exploitation
* **Token sales**: Bot purchases, unfair distribution, automated sniping
* **Web3 gaming**: Multi-accounting, automated gameplay, reward farming
* **DAOs**: Sybil attacks on voting, proposal spam, governance manipulation

### The Solution: Signature-Based Verification

GuardianStack acts as an **identity oracle** for your smart contracts. Here's how it works:

1. User interacts with your **frontend**
2. GuardianStack verifies they're a real human (not a bot/script)
3. Your **backend** checks the Guardian event and generates a **cryptographic signature**
4. User's wallet submits the transaction **with the signature**
5. Your **smart contract** validates the signature before executing

<div data-with-frame="true"><figure><img src="https://3773527904-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaQpbYgAyZEBrDPFZtNaq%2Fuploads%2FxVUVROMeLqrsPB8aaiqk%2FGuardian-Powered%20Registration%20Flow%20-%20visual%20selection%20(2).png?alt=media&#x26;token=44e54c71-723b-4fb1-9cbe-31123c3bac60" alt=""><figcaption></figcaption></figure></div>

{% hint style="success" %}
**Result**: Even if someone calls your contract directly, the transaction fails without a valid Guardian signature.
{% endhint %}

***

### Implementation Guide

#### Step 1: Frontend - Collect Guardian Event

Install the Guardian SDK:

```bash
npm install @guardianstack/guardian-js
```

Initialize Guardian and get the `requestId`:

```typescript
import { loadAgent } from "@guardianstack/guardian-js";

async function getGuardianRequestId(): Promise<string> {
  const agent = await loadAgent({
    siteKey: "YOUR_SITE_KEY",
  });

  const response = await agent.get();
  
  return response?.requestId;
}
```

#### Step 2: Backend - Verify & Generate Signature

Install the Guardian Server SDK:

```bash
npm install @guardianstack/guardianjs-server
```

Create an endpoint that verifies the Guardian event and returns a signature:

```typescript
import {
  createGuardianClient,
  isBot,
  isVPN,
  isTampering,
  isVirtualized,
} from "@guardianstack/guardianjs-server";
import { ethers } from "ethers";

const guardianClient = createGuardianClient({
  secret: process.env.GUARDIAN_SECRET!,
});

// Your backend signer (keep this private!)
const signer = new ethers.Wallet(process.env.BACKEND_PRIVATE_KEY!);

interface SignatureRequest {
  requestId: string;      // Guardian event ID
  userAddress: string;    // User's wallet address
  action: string;         // e.g., "mint", "trade", "claim"
  params?: any;           // Action-specific parameters
}

app.post("/api/get-signature", async (req, res) => {
  const { requestId, userAddress, action, params } = req.body as SignatureRequest;

  // 1. Fetch Guardian event
  const event = await guardianClient.getEvent(requestId);

  // 2. Apply your fraud rules
  if (isBot(event)) {
    return res.status(403).json({ error: "Bot detected" });
  }

  if (isVPN(event) || isTampering(event) || isVirtualized(event)) {
    return res.status(403).json({ error: "Suspicious activity detected" });
  }

  // Optional: Additional checks
  const botScore = event.bot?.score ?? 0;
  if (botScore > 80) {
    return res.status(403).json({ error: "High bot score" });
  }

  // 3. Generate signature
  const expiresAt = Math.floor(Date.now() / 1000) + 300; // 5 minutes

  // Create message hash (EIP-712 recommended for production)
  const messageHash = ethers.utils.solidityKeccak256(
    ["address", "string", "uint256", "bytes32"],
    [
      userAddress,
      action,
      expiresAt,
      ethers.utils.keccak256(ethers.utils.toUtf8Bytes(JSON.stringify(params || {}))),
    ]
  );

  // Sign the message
  const signature = await signer.signMessage(ethers.utils.arrayify(messageHash));

  res.json({
    signature,
    expiresAt,
    messageHash,
  });
});
```

#### Step 3: Frontend - Request Signature & Submit Transaction

```typescript
import { ethers } from "ethers";

async function mintNFT() {
  // 1. Get Guardian requestId
  const requestId = await getGuardianRequestId();

  // 2. Get user's wallet
  const provider = new ethers.providers.Web3Provider(window.ethereum);
  const signer = provider.getSigner();
  const userAddress = await signer.getAddress();

  // 3. Request signature from your backend
  const response = await fetch("/api/get-signature", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      requestId,
      userAddress,
      action: "mint",
      params: { tokenId: 123 },
    }),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error || "Failed to get signature");
  }

  const { signature, expiresAt, messageHash } = await response.json();

  // 4. Call smart contract with signature
  const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
  
  const tx = await contract.mint(
    userAddress,
    123, // tokenId
    expiresAt,
    signature
  );

  await tx.wait();
  console.log("Minted successfully!");
}
```

#### Step 4: Smart Contract - Verify Signature

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract GuardianProtectedNFT is Ownable {
    using ECDSA for bytes32;

    address public guardianSigner;

    constructor(address _guardianSigner) {
        guardianSigner = _guardianSigner;
    }

    function setGuardianSigner(address _newSigner) external onlyOwner {
        guardianSigner = _newSigner;
    }

    modifier onlyVerified(
        address user,
        string memory action,
        uint256 expiresAt,
        bytes32 paramsHash,
        bytes memory signature
    ) {
        // 1. Check expiration
        require(block.timestamp <= expiresAt, "Signature expired");

        // 2. Reconstruct message hash
        bytes32 messageHash = keccak256(
            abi.encodePacked(user, action, expiresAt, paramsHash)
        );

        // 3. Verify signature
        bytes32 ethSignedMessageHash = messageHash.toEthSignedMessageHash();
        address recoveredSigner = ethSignedMessageHash.recover(signature);

        require(recoveredSigner == guardianSigner, "Invalid signature");
        _;
    }

    function mint(
        address to,
        uint256 tokenId,
        uint256 expiresAt,
        bytes memory signature
    ) external onlyVerified(
        to,
        "mint",
        expiresAt,
        keccak256(abi.encodePacked('{"tokenId":', tokenId, '}')),
        signature
    ) {
        // Your mint logic here
        _safeMint(to, tokenId);
    }
}
```

***

### Use Case Examples

#### NFT Marketplace - Prevent Wash Trading

**Problem**: Users create multiple wallets to trade with themselves and inflate prices.

**Solution**: Link all transactions to Guardian's `visitorId` (persistent device fingerprint).

```typescript
// Backend: Store visitorId with each signature
app.post("/api/get-trade-signature", async (req, res) => {
  const event = await guardianClient.getEvent(req.body.requestId);
  
  const visitorId = event.visitorId;
  
  // Check if buyer and seller share the same visitorId
  const buyerVisitorId = await getVisitorIdForAddress(req.body.buyerAddress);
  const sellerVisitorId = await getVisitorIdForAddress(req.body.sellerAddress);
  
  if (buyerVisitorId === sellerVisitorId) {
    return res.status(403).json({ error: "Wash trading detected" });
  }
  
  // Store visitorId for future checks
  await storeVisitorId(req.body.buyerAddress, visitorId);
  
  // Generate signature...
});
```

#### DeFi - Prevent Reward Farming

**Problem**: Users create hundreds of accounts to farm airdrops/rewards.

**Solution**: Rate-limit rewards per device and detect virtualized environments.

```typescript
app.post("/api/get-claim-signature", async (req, res) => {
  const event = await guardianClient.getEvent(req.body.requestId);
  
  // Detect virtual machines (common in farming operations)
  if (isVirtualized(event)) {
    return res.status(403).json({ error: "Virtualized environment detected" });
  }
  
  // Check how many claims from this device in the last 24h
  const visitorId = event.visitorId;
  const recentClaims = await getClaimCountByVisitor(visitorId, 24 * 60 * 60 * 1000);
  
  if (recentClaims >= 5) {
    return res.status(403).json({ error: "Too many claims from this device" });
  }
  
  // Generate signature...
});
```

#### Token Sale - Fair Launch

**Problem**: Bots buy entire supply in milliseconds.

**Solution**: Require human verification and rate-limit per device.

```typescript
app.post("/api/get-purchase-signature", async (req, res) => {
  const event = await guardianClient.getEvent(req.body.requestId);
  
  // Block bots
  if (isBot(event)) {
    return res.status(403).json({ error: "Bot detected" });
  }
  
  // Block high-risk indicators
  const botScore = event.bot?.score ?? 0;
  const tamperingScore = event.tampering?.score ?? 0;
  
  if (botScore > 70 || tamperingScore > 70) {
    return res.status(403).json({ error: "Suspicious activity" });
  }
  
  // Limit purchases per device
  const visitorId = event.visitorId;
  const purchaseCount = await getPurchaseCountByVisitor(visitorId);
  
  if (purchaseCount >= 1) {
    return res.status(403).json({ error: "Purchase limit reached" });
  }
  
  // Generate signature...
});
```

***

### Advanced: EIP-712 Typed Data Signing

For production applications, use EIP-712 for better UX and security:

```typescript
// Backend
const domain = {
  name: "MyProtocol",
  version: "1",
  chainId: 1,
  verifyingContract: CONTRACT_ADDRESS,
};

const types = {
  Action: [
    { name: "user", type: "address" },
    { name: "action", type: "string" },
    { name: "expiresAt", type: "uint256" },
    { name: "nonce", type: "uint256" },
  ],
};

const value = {
  user: userAddress,
  action: "mint",
  expiresAt,
  nonce: await getNonce(userAddress),
};

const signature = await signer._signTypedData(domain, types, value);
```

```solidity
// Smart Contract
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";

contract GuardianProtectedNFT is EIP712, Ownable {
    bytes32 private constant ACTION_TYPEHASH =
        keccak256("Action(address user,string action,uint256 expiresAt,uint256 nonce)");

    mapping(address => uint256) public nonces;

    constructor() EIP712("MyProtocol", "1") {}

    function mint(
        address to,
        string memory action,
        uint256 expiresAt,
        bytes memory signature
    ) external {
        require(block.timestamp <= expiresAt, "Signature expired");

        bytes32 structHash = keccak256(
            abi.encode(ACTION_TYPEHASH, to, keccak256(bytes(action)), expiresAt, nonces[to])
        );

        bytes32 digest = _hashTypedDataV4(structHash);
        address signer = ECDSA.recover(digest, signature);

        require(signer == guardianSigner, "Invalid signature");

        nonces[to]++;
        _safeMint(to, tokenId);
    }
}
```

### Conclusion

Traditional Web3 security focuses on smart contract vulnerabilities: reentrancy attacks, integer overflows, access control bugs. But the biggest threat to your protocol isn't a code exploit; it's **systematic abuse by bots and fraudsters**.

GuardianStack solves this by bringing **identity verification** to the blockchain without compromising decentralization. Your smart contracts remain permissionless and trustless, but they gain the ability to distinguish between legitimate users and automated attackers.

#### Key Benefits

✅ **Stop bots at the contract level** - Not just your frontend\
✅ **Prevent Sybil attacks** - Link wallets to real devices\
✅ **Fair token distributions** - No more bot-dominated launches\
✅ **Protect protocol economics** - Stop multi-account farming\
✅ **Maintain decentralization** - Users still control their keys

#### The Bottom Line

If your protocol has value, it will be attacked. Bots will farm it. Scripts will exploit it. Bad actors will game it.

GuardianStack gives you the tools to fight back without sacrificing the core principles of Web3.

{% hint style="success" %}
👉 **Stop fraud before it costs you, Get started for free:** [Sign up and get your API keys](https://dashboard.guardianstack.ai)
{% endhint %}


---

# 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.guardianstack.ai/documentation/protect-your-implementation/web3-fraud-prevention.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.
