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

# Testing Guide

> Learn how to test ZKP2P integrations and run the protocol test suite

This guide covers testing your ZKP2P integrations and understanding the protocol's test suite.

## Overview

The zkp2p-contracts project includes:

* **Hardhat tests**: TypeScript-based integration and unit tests
* **Foundry tests**: Solidity-based fuzz and invariant tests
* **Test utilities**: Helpers for common testing patterns
* **Mock contracts**: Simulated external dependencies

## Running Protocol Tests

### Hardhat Tests

<CodeGroup>
  ```bash All Tests theme={null}
  # Run full test suite
  yarn test

  # Fast mode (skip slower tests)
  yarn test:fast
  ```

  ```bash Specific Tests theme={null}
  # Test specific contract
  yarn test test/escrow/
  yarn test test/orchestrator/

  # Test specific file
  yarn test test/escrow/escrow.spec.ts

  # Test with gas reporting
  REPORT_GAS=true yarn test
  ```

  ```bash Coverage theme={null}
  # Generate coverage report
  yarn coverage

  # Coverage opens in browser at coverage/index.html
  ```
</CodeGroup>

### Foundry Tests

<CodeGroup>
  ```bash Standard Tests theme={null}
  # Run all Foundry tests
  yarn test:forge

  # Verbose output
  yarn test:forge -vvv

  # Test specific contract
  forge test --match-contract EscrowTest

  # Test specific function
  forge test --match-test testCreateDeposit
  ```

  ```bash Fuzz Tests theme={null}
  # Run with fuzzing (100+ random inputs)
  yarn test:forge:fuzz

  # Increase fuzz runs
  forge test --fuzz-runs 1000
  ```

  ```bash Invariant Tests theme={null}
  # Protocol invariant testing
  yarn test:forge:invariant
  ```

  ```bash Coverage theme={null}
  # Foundry coverage report
  yarn test:forge:coverage
  ```
</CodeGroup>

## Testing Your Integration

### Local Development Setup

<Steps>
  <Step title="Clone and install">
    ```bash theme={null}
    git clone https://github.com/zkp2p/zkp2p-v2-contracts.git
    cd zkp2p-v2-contracts
    yarn install
    ```
  </Step>

  <Step title="Configure environment">
    ```bash theme={null}
    cp .env.default .env
    # Edit .env with your API keys (optional for local testing)
    ```
  </Step>

  <Step title="Start local node">
    ```bash theme={null}
    # Terminal 1: Start Hardhat node
    yarn chain

    # Terminal 2: Deploy contracts
    yarn deploy:localhost
    ```
  </Step>

  <Step title="Get contract addresses">
    ```bash theme={null}
    # Addresses saved in deployments/localhost/
    cat deployments/localhost/Escrow.json | jq .address
    cat deployments/localhost/Orchestrator.json | jq .address
    ```
  </Step>
</Steps>

### Testing Deposit Creation

```typescript theme={null}
import { expect } from "chai";
import { ethers } from "hardhat";
import { Escrow, USDCMock } from "../typechain";

describe("Integration: Create Deposit", () => {
  let escrow: Escrow;
  let usdc: USDCMock;
  let maker: SignerWithAddress;

  before(async () => {
    [maker] = await ethers.getSigners();

    // Get deployed contracts
    const escrowAddress = "0x..."; // From deployments/
    escrow = await ethers.getContractAt("Escrow", escrowAddress);

    const usdcAddress = "0x...";
    usdc = await ethers.getContractAt("USDCMock", usdcAddress);

    // Mint test USDC
    await usdc.mint(maker.address, ethers.utils.parseUnits("10000", 6));
  });

  it("should create a deposit with multiple payment methods", async () => {
    // Approve USDC
    const amount = ethers.utils.parseUnits("1000", 6);
    await usdc.connect(maker).approve(escrow.address, amount);

    // Payment method hashes
    const venmo = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("venmo"));
    const paypal = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("paypal"));

    // Currency codes
    const USD = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("USD"));

    // Create deposit
    const tx = await escrow.connect(maker).createDeposit({
      token: usdc.address,
      amount: amount,
      intentAmountRange: {
        min: ethers.utils.parseUnits("10", 6),
        max: ethers.utils.parseUnits("500", 6)
      },
      paymentMethods: [venmo, paypal],
      paymentMethodData: [
        {
          intentGatingService: ethers.constants.AddressZero,
          payeeDetails: ethers.utils.keccak256(
            ethers.utils.toUtf8Bytes("maker-venmo")
          ),
          data: "0x"
        },
        {
          intentGatingService: ethers.constants.AddressZero,
          payeeDetails: ethers.utils.keccak256(
            ethers.utils.toUtf8Bytes("maker-paypal")
          ),
          data: "0x"
        }
      ],
      currencies: [
        [{ code: USD, minConversionRate: ethers.utils.parseEther("1.0") }],
        [{ code: USD, minConversionRate: ethers.utils.parseEther("1.0") }]
      ],
      delegate: ethers.constants.AddressZero,
      intentGuardian: ethers.constants.AddressZero,
      retainOnEmpty: true
    });

    const receipt = await tx.wait();
    const event = receipt.events?.find(e => e.event === "DepositReceived");
    const depositId = event?.args?.depositId;

    // Verify deposit
    const deposit = await escrow.getDeposit(depositId);
    expect(deposit.depositor).to.equal(maker.address);
    expect(deposit.remainingDeposits).to.equal(amount);
    expect(deposit.acceptingIntents).to.be.true;

    // Verify payment methods
    const methods = await escrow.getDepositPaymentMethods(depositId);
    expect(methods).to.have.lengthOf(2);
    expect(methods[0]).to.equal(venmo);
    expect(methods[1]).to.equal(paypal);

    console.log("✓ Deposit created successfully, ID:", depositId.toString());
  });
});
```

### Testing Intent Flow

```typescript theme={null}
import { expect } from "chai";
import { ethers } from "hardhat";
import { Orchestrator, Escrow, PaymentVerifierMock } from "../typechain";

describe("Integration: Intent Lifecycle", () => {
  let orchestrator: Orchestrator;
  let escrow: Escrow;
  let verifier: PaymentVerifierMock;
  let maker: SignerWithAddress;
  let taker: SignerWithAddress;
  let depositId: number;
  let intentHash: string;

  before(async () => {
    [maker, taker] = await ethers.getSigners();
    
    // Setup contracts and create deposit
    // ... (similar to previous example)
  });

  it("should signal an intent", async () => {
    const venmo = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("venmo"));
    const USD = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("USD"));

    const tx = await orchestrator.connect(taker).signalIntent({
      escrow: escrow.address,
      depositId: depositId,
      amount: ethers.utils.parseUnits("50", 6),
      to: taker.address,
      paymentMethod: venmo,
      fiatCurrency: USD,
      conversionRate: ethers.utils.parseEther("1.0"),
      referrer: ethers.constants.AddressZero,
      referrerFee: 0,
      gatingServiceSignature: "0x",
      signatureExpiration: 0,
      postIntentHook: ethers.constants.AddressZero,
      data: "0x"
    });

    const receipt = await tx.wait();
    const event = receipt.events?.find(e => e.event === "IntentSignaled");
    intentHash = event?.args?.intentHash;

    // Verify intent created
    const intent = await orchestrator.getIntent(intentHash);
    expect(intent.owner).to.equal(taker.address);
    expect(intent.amount).to.equal(ethers.utils.parseUnits("50", 6));

    // Verify liquidity locked
    const deposit = await escrow.getDeposit(depositId);
    expect(deposit.outstandingIntentAmount).to.equal(
      ethers.utils.parseUnits("50", 6)
    );

    console.log("✓ Intent signaled, hash:", intentHash);
  });

  it("should fulfill the intent", async () => {
    // Mock payment proof (in real scenario, this comes from attestation service)
    const mockProof = "0x" + "00".repeat(100);

    // Configure mock verifier to return success
    await verifier.setVerificationResult({
      success: true,
      intentHash: intentHash,
      releaseAmount: ethers.utils.parseUnits("50", 6)
    });

    const balanceBefore = await usdc.balanceOf(taker.address);

    // Fulfill intent
    const tx = await orchestrator.fulfillIntent({
      intentHash: intentHash,
      paymentProof: mockProof,
      verificationData: "0x",
      postIntentHookData: "0x"
    });

    await tx.wait();

    const balanceAfter = await usdc.balanceOf(taker.address);
    const received = balanceAfter.sub(balanceBefore);

    // Should receive 50 USDC minus protocol fees
    expect(received).to.be.closeTo(
      ethers.utils.parseUnits("50", 6),
      ethers.utils.parseUnits("1", 6) // Allow 1 USDC variance for fees
    );

    // Verify intent removed
    const intent = await orchestrator.getIntent(intentHash);
    expect(intent.timestamp).to.equal(0); // Intent deleted

    console.log("✓ Intent fulfilled, received:", ethers.utils.formatUnits(received, 6), "USDC");
  });
});
```

### Testing Custom Hooks

```typescript theme={null}
import { expect } from "chai";
import { ethers } from "hardhat";

describe("Custom Hook Integration", () => {
  let hook: MyCustomHook;
  let orchestrator: Orchestrator;
  let usdc: IERC20;

  beforeEach(async () => {
    // Deploy contracts
    const Hook = await ethers.getContractFactory("MyCustomHook");
    hook = await Hook.deploy(orchestrator.address, usdc.address);

    // Register hook
    const registry = await orchestrator.postIntentHookRegistry();
    await registry.addPostIntentHook(hook.address);
  });

  it("should execute hook on fulfillment", async () => {
    // Create deposit and signal intent with hook
    // ... setup code ...

    // Encode hook data
    const hookData = ethers.utils.defaultAbiCoder.encode(
      ["address", "uint256"],
      [destination.address, minAmount]
    );

    // Fulfill with hook
    await orchestrator.fulfillIntent({
      intentHash: intentHash,
      paymentProof: proof,
      verificationData: "0x",
      postIntentHookData: hookData
    });

    // Verify hook execution
    // Check events, balances, state changes
    expect(await hook.executionCount()).to.equal(1);
  });

  it("should handle hook failures gracefully", async () => {
    // Configure hook to fail
    await hook.setShouldFail(true);

    // Fulfillment should still succeed with fallback
    await expect(
      orchestrator.fulfillIntent(params)
    ).to.not.be.reverted;

    // Verify funds went to fallback recipient
    // ...
  });
});
```

## Test Utilities

The protocol provides helpful test utilities:

### Intent Hash Calculation

```typescript theme={null}
import { calculateIntentHash } from "@utils/protocolUtils";

const intentHash = calculateIntentHash(
  orchestrator.address,
  intentCounter // Current intent counter value
);
```

### Mock Signatures

```typescript theme={null}
import { generateGatingServiceSignature } from "@utils/test/helpers";

const signature = await generateGatingServiceSignature(
  gatingServiceSigner,
  orchestrator.address,
  escrow.address,
  depositId,
  amount,
  recipient,
  paymentMethod,
  currency,
  conversionRate,
  chainId,
  expiration
);
```

### Signal Intent Helper

```typescript theme={null}
import { createSignalIntentParams } from "@utils/test/helpers";

const params = await createSignalIntentParams(
  orchestrator.address,
  escrow.address,
  depositId,
  amount,
  recipient,
  paymentMethod,
  currency,
  conversionRate,
  referrer,
  referrerFee,
  gatingService,
  chainId,
  hook,
  hookData,
  signatureExpiration
);
```

## Common Test Patterns

### Time Manipulation

```typescript theme={null}
import { ethers } from "hardhat";

// Advance time by 1 day
await ethers.provider.send("evm_increaseTime", [86400]);
await ethers.provider.send("evm_mine", []);

// Get current timestamp
const block = await ethers.provider.getBlock("latest");
const timestamp = block.timestamp;
```

### Event Testing

```typescript theme={null}
import { expect } from "chai";

// Expect specific event
await expect(tx)
  .to.emit(contract, "EventName")
  .withArgs(arg1, arg2, arg3);

// Multiple events
await expect(tx)
  .to.emit(contract, "Event1")
  .and.to.emit(contract, "Event2");

// Extract event data
const receipt = await tx.wait();
const event = receipt.events?.find(e => e.event === "EventName");
const value = event?.args?.paramName;
```

### Revert Testing

```typescript theme={null}
// Expect revert with custom error
await expect(
  contract.functionCall()
).to.be.revertedWithCustomError(contract, "CustomError");

// With error arguments
await expect(
  contract.functionCall()
).to.be.revertedWithCustomError(contract, "CustomError")
  .withArgs(expectedArg1, expectedArg2);

// Generic revert
await expect(contract.functionCall()).to.be.reverted;
```

## Continuous Integration

Tests run automatically on GitHub Actions:

```yaml theme={null}
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]

jobs:
  hardhat-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: yarn install
      - run: yarn test
      - run: yarn coverage

  foundry-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: foundry-rs/foundry-toolchain@v1
      - run: forge test
```

## Coverage Reports

View coverage at [codecov.io](https://codecov.io/gh/zkp2p/zkp2p-v2-contracts):

* **Overall coverage**: >95%
* **Escrow\.sol**: 100%
* **Orchestrator.sol**: 100%
* **UnifiedPaymentVerifier.sol**: 100%

## Best Practices

<CardGroup cols={2}>
  <Card title="Test Reverts" icon="xmark">
    Always test failure cases and revert conditions, not just happy paths.
  </Card>

  <Card title="Use Beforehooks" icon="arrow-rotate-left">
    Setup common state in `beforeEach` to keep tests isolated and maintainable.
  </Card>

  <Card title="Check Events" icon="signal-stream">
    Verify events are emitted with correct parameters for all state changes.
  </Card>

  <Card title="Fuzz Important Functions" icon="shuffle">
    Use Foundry fuzz tests for functions with complex input validation.
  </Card>
</CardGroup>

## Resources

<CardGroup cols={2}>
  <Card title="Hardhat Docs" icon="hat-wizard" href="https://hardhat.org/docs">
    Complete Hardhat documentation and guides
  </Card>

  <Card title="Foundry Book" icon="book" href="https://book.getfoundry.sh">
    Comprehensive Foundry testing guide
  </Card>

  <Card title="Chai Matchers" icon="check" href="https://ethereum-waffle.readthedocs.io/en/latest/matchers.html">
    Waffle/Chai assertion matchers for Ethereum
  </Card>

  <Card title="Test Repository" icon="github" href="https://github.com/zkp2p/zkp2p-v2-contracts/tree/main/test">
    Browse the full test suite on GitHub
  </Card>
</CardGroup>
