> ## 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.

# Signaling Intents (Taker Side)

> Learn how to signal trading intents as a taker in the ZKP2P protocol

As a taker (buyer), you signal intents to purchase USDC from maker deposits in exchange for fiat payments. This guide explains how to create and manage intents.

## Overview

Signaling an intent:

1. Locks liquidity from a maker's deposit on the Escrow contract
2. Creates a commitment to send fiat payment off-chain
3. Records all trade parameters on-chain for later verification
4. Starts an expiration timer (typically 24 hours)

Intents are created through the **Orchestrator contract** which coordinates the entire trade lifecycle.

## Finding Available Deposits

Before signaling an intent, query available deposits:

```typescript theme={null}
import { ethers } from "ethers";
import { ProtocolViewer } from "@typechain/ProtocolViewer";

const PROTOCOL_VIEWER_ADDRESS = "0x...";
const viewer = new ethers.Contract(
  PROTOCOL_VIEWER_ADDRESS,
  ProtocolViewer_ABI,
  provider
) as ProtocolViewer;

// Get all deposits with available liquidity
const deposits = await viewer.getDeposits();

// Filter for deposits that:
// - Are accepting intents
// - Have sufficient liquidity
// - Support your desired payment method
const venmoHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("venmo"));

const availableDeposits = deposits.filter(d => 
  d.deposit.acceptingIntents &&
  d.deposit.remainingDeposits.gte(ethers.utils.parseUnits("50", 6)) &&
  d.paymentMethods.some(pm => pm.paymentMethod === venmoHash && pm.active)
);

console.log(`Found ${availableDeposits.length} available deposits`);
```

## Signaling an Intent

<Steps>
  <Step title="Prepare intent parameters">
    Gather all required information for the trade:

    ```typescript theme={null}
    import { Orchestrator } from "@typechain/Orchestrator";

    const ORCHESTRATOR_ADDRESS = "0x...";
    const ESCROW_ADDRESS = "0x...";

    const orchestrator = new ethers.Contract(
      ORCHESTRATOR_ADDRESS,
      Orchestrator_ABI,
      signer
    ) as Orchestrator;

    // Trade parameters
    const depositId = 0; // From your deposit search
    const amount = ethers.utils.parseUnits("50", 6); // 50 USDC
    const recipientAddress = await signer.getAddress(); // Where to receive USDC

    // Payment details
    const paymentMethod = ethers.utils.keccak256(
      ethers.utils.toUtf8Bytes("venmo")
    );
    const fiatCurrency = ethers.utils.keccak256(
      ethers.utils.toUtf8Bytes("USD")
    );

    // Conversion rate (must meet deposit's minimum)
    const conversionRate = ethers.utils.parseEther("1.02"); // 1 USDC = 1.02 USD
    ```
  </Step>

  <Step title="Get gating signature (if required)">
    If the deposit requires intent gating, obtain a signature from the gating service:

    ```typescript theme={null}
    // Check if gating is required
    const escrow = new ethers.Contract(ESCROW_ADDRESS, Escrow_ABI, provider);
    const gatingService = await escrow.getDepositGatingService(
      depositId,
      paymentMethod
    );

    let gatingSignature = "0x";
    let signatureExpiration = 0;

    if (gatingService !== ethers.constants.AddressZero) {
      // Request signature from gating service API
      const response = await fetch('https://gating-service.example.com/sign', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          orchestrator: ORCHESTRATOR_ADDRESS,
          escrow: ESCROW_ADDRESS,
          depositId,
          amount: amount.toString(),
          to: recipientAddress,
          paymentMethod,
          fiatCurrency,
          conversionRate: conversionRate.toString()
        })
      });
      
      const data = await response.json();
      gatingSignature = data.signature;
      signatureExpiration = data.expiration;
    }
    ```
  </Step>

  <Step title="Call signalIntent">
    Submit the intent transaction:

    ```typescript theme={null}
    const tx = await orchestrator.signalIntent({
      escrow: ESCROW_ADDRESS,
      depositId: depositId,
      amount: amount,
      to: recipientAddress,
      paymentMethod: paymentMethod,
      fiatCurrency: fiatCurrency,
      conversionRate: conversionRate,
      referrer: ethers.constants.AddressZero, // Optional referrer
      referrerFee: 0, // Fee for referrer (0-50% in 18 decimals)
      gatingServiceSignature: gatingSignature,
      signatureExpiration: signatureExpiration || 0,
      postIntentHook: ethers.constants.AddressZero, // Optional hook
      data: "0x" // Hook-specific data
    });

    const receipt = await tx.wait();
    console.log("Intent signaled!", receipt.transactionHash);

    // Extract intentHash from event
    const event = receipt.events?.find(e => e.event === "IntentSignaled");
    const intentHash = event?.args?.intentHash;
    console.log("Intent hash:", intentHash);
    ```
  </Step>
</Steps>

## Signaling with Referrers

Include a referrer to share fees:

```typescript theme={null}
const REFERRER_ADDRESS = "0x...";
const REFERRER_FEE = ethers.utils.parseEther("0.01"); // 1% fee to referrer

await orchestrator.signalIntent({
  escrow: ESCROW_ADDRESS,
  depositId: depositId,
  amount: amount,
  to: recipientAddress,
  paymentMethod: paymentMethod,
  fiatCurrency: fiatCurrency,
  conversionRate: conversionRate,
  referrer: REFERRER_ADDRESS,
  referrerFee: REFERRER_FEE, // Paid from your USDC amount
  gatingServiceSignature: gatingSignature,
  signatureExpiration: signatureExpiration,
  postIntentHook: ethers.constants.AddressZero,
  data: "0x"
});
```

<Note>
  Referrer fees are capped at 50% and are deducted from the USDC you receive. For example, with a 1% referrer fee on 100 USDC, you receive \~99 USDC (after protocol fees too).
</Note>

## Multiple Intents

By default, accounts can only have one active intent at a time. To signal multiple intents:

<CodeGroup>
  ```typescript Check Allowance theme={null}
  // Check if multiple intents are allowed globally
  const allowMultiple = await orchestrator.allowMultipleIntents();

  // Or check if you're a whitelisted relayer
  const relayerRegistry = await orchestrator.relayerRegistry();
  const registry = new ethers.Contract(
    relayerRegistry,
    RelayerRegistry_ABI,
    provider
  );
  const isRelayer = await registry.isWhitelistedRelayer(await signer.getAddress());

  if (!allowMultiple && !isRelayer) {
    console.log("Can only have one active intent");
  }
  ```

  ```typescript Use Different Addresses theme={null}
  // Alternative: Use separate addresses for each intent
  const wallet1 = new ethers.Wallet(PRIVATE_KEY_1, provider);
  const wallet2 = new ethers.Wallet(PRIVATE_KEY_2, provider);

  // Signal from wallet1
  await orchestrator.connect(wallet1).signalIntent(params1);

  // Signal from wallet2
  await orchestrator.connect(wallet2).signalIntent(params2);
  ```
</CodeGroup>

## Reading Intent State

```typescript theme={null}
// Get intent details
const intent = await orchestrator.getIntent(intentHash);

console.log("Owner:", intent.owner);
console.log("Recipient:", intent.to);
console.log("Amount:", ethers.utils.formatUnits(intent.amount, 6), "USDC");
console.log("Timestamp:", new Date(intent.timestamp.toNumber() * 1000));
console.log("Payment method:", intent.paymentMethod);
console.log("Currency:", intent.fiatCurrency);
console.log("Rate:", ethers.utils.formatEther(intent.conversionRate));

// Calculate fiat amount to send
const fiatAmount = intent.amount
  .mul(intent.conversionRate)
  .div(ethers.utils.parseEther("1"));
console.log("Send fiat:", ethers.utils.formatUnits(fiatAmount, 6));

// Get all your active intents
const myIntents = await orchestrator.getAccountIntents(
  await signer.getAddress()
);
console.log("Active intents:", myIntents);
```

## Canceling an Intent

Cancel before making the fiat payment if you change your mind:

```typescript theme={null}
try {
  const tx = await orchestrator.cancelIntent(intentHash);
  await tx.wait();
  console.log("Intent canceled, liquidity returned to deposit");
} catch (error) {
  console.error("Failed to cancel:", error.message);
}
```

<Warning>
  You can only cancel intents you own. Once you've sent the fiat payment and obtained a proof, you should fulfill the intent instead of canceling.
</Warning>

## After Signaling

Once your intent is signaled:

<Steps>
  <Step title="Send fiat payment">
    Use the payment method specified in your intent to send fiat to the maker's payee details.

    **Amount to send:** Calculate from the intent's amount and conversion rate.
  </Step>

  <Step title="Obtain payment proof">
    Get a zkTLS attestation or proof from the attestation service proving your payment.
  </Step>

  <Step title="Fulfill the intent">
    Submit the payment proof to unlock your USDC. See [Fulfilling Intents](/guides/fulfilling-intents).
  </Step>
</Steps>

## Validation Checks

The Orchestrator performs these validations when signaling:

<AccordionGroup>
  <Accordion title="Deposit Checks">
    * Deposit exists and is accepting intents
    * Sufficient liquidity available
    * Amount within deposit's intent range
  </Accordion>

  <Accordion title="Payment Method Checks">
    * Payment method is registered in PaymentVerifierRegistry
    * Payment method is active for this deposit
    * Currency is supported by the payment method
    * Conversion rate meets deposit's minimum
  </Accordion>

  <Accordion title="Access Control Checks">
    * Gating signature is valid (if required)
    * Signature hasn't expired
    * Account can have multiple intents (if not first)
  </Accordion>

  <Accordion title="Fee Checks">
    * Referrer fee ≤ 50%
    * Referrer address is set if fee > 0
    * Post-intent hook is whitelisted (if specified)
  </Accordion>
</AccordionGroup>

## Common Errors

| Error                          | Cause                                       | Solution                                          |
| ------------------------------ | ------------------------------------------- | ------------------------------------------------- |
| `InsufficientDepositLiquidity` | Deposit doesn't have enough available funds | Choose a different deposit or reduce amount       |
| `AmountBelowMin`               | Amount less than deposit's minimum          | Increase your intent amount                       |
| `AmountAboveMax`               | Amount exceeds deposit's maximum            | Reduce your intent amount                         |
| `RateBelowMinimum`             | Conversion rate too low                     | Increase conversion rate to match deposit         |
| `AccountHasActiveIntent`       | You already have an active intent           | Cancel existing intent or wait for it to complete |
| `InvalidSignature`             | Gating signature is invalid or expired      | Request a new signature from gating service       |

## Best Practices

<CardGroup cols={2}>
  <Card title="Check Rates" icon="money-bill-trend-up">
    Verify the conversion rate meets your expectations before signaling.
  </Card>

  <Card title="Act Quickly" icon="clock">
    Signal and fulfill promptly - intents typically expire in 24 hours.
  </Card>

  <Card title="Save Intent Hash" icon="hashtag">
    Store the intentHash from the event - you'll need it for fulfillment.
  </Card>

  <Card title="Monitor Expiry" icon="hourglass">
    Track when your intent expires and fulfill before the deadline.
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Fulfill Intent" icon="circle-check" href="/guides/fulfilling-intents">
    Learn how to submit payment proofs and receive your USDC
  </Card>

  <Card title="Contract Reference" icon="book" href="/contracts/orchestrator">
    View the complete Orchestrator contract documentation
  </Card>
</CardGroup>
