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

# Intent Lifecycle

> Complete flow from intent signal to fulfillment, including all contract interactions

## Overview

An **intent** represents a buyer's commitment to pay off-chain fiat currency in exchange for on-chain tokens. This page walks through the complete lifecycle from signal to fulfillment, showing all contract interactions and state changes.

<Info>
  The intent lifecycle is the core workflow of the zkp2p protocol, coordinating between Orchestrator, Escrow, payment verifiers, and registries.
</Info>

## Lifecycle Stages

```mermaid theme={null}
stateDiagram-v2
    [*] --> Discovery: Buyer finds deposit
    Discovery --> GatingService: Request signature
    GatingService --> Signal: signalIntent()
    Signal --> AwaitingPayment: Intent signaled
    AwaitingPayment --> PaymentMade: Buyer pays off-chain
    PaymentMade --> AttestationService: Request attestation
    AttestationService --> Fulfillment: fulfillIntent()
    Fulfillment --> Complete: Tokens released
    Complete --> [*]
    
    AwaitingPayment --> Cancelled: cancelIntent()
    Cancelled --> [*]
    
    AwaitingPayment --> Expired: Time > expiryTime
    Expired --> Pruned: pruneIntents()
    Pruned --> [*]
    
    AwaitingPayment --> ManualRelease: releaseFundsToPayer()
    ManualRelease --> Complete
```

## Stage 1: Discovery

The buyer browses available deposits (off-chain indexing or on-chain queries) to find one matching their requirements:

```typescript theme={null}
// Off-chain: Query deposits via event indexing or ProtocolViewer
const deposits = await protocolViewer.getActiveDeposits();

// Find deposits that:
// - Accept the buyer's payment method (e.g., Venmo)
// - Support the buyer's currency (e.g., USD)
// - Have sufficient liquidity
// - Offer acceptable conversion rates
const matchingDeposits = deposits.filter(d => 
    d.paymentMethods.includes(venmoHash) &&
    d.currencies[venmoHash].includes(usdHash) &&
    d.remainingDeposits >= desiredAmount &&
    d.conversionRates[venmoHash][usdHash] >= minAcceptableRate
);
```

## Stage 2: Gating Service (Optional)

If the deposit has a `intentGatingService`, the buyer must obtain a signature:

### Request Signature

```typescript theme={null}
// Buyer calls gating service API (off-chain)
const gatingRequest = {
    orchestrator: orchestratorAddress,
    escrow: escrowAddress,
    depositId: depositId,
    amount: intentAmount,
    to: buyerAddress,
    paymentMethod: venmoHash,
    fiatCurrency: usdHash,
    conversionRate: agreedRate,
    signatureExpiration: timestamp + 3600, // 1 hour validity
    chainId: chainId
};

const signature = await gatingService.requestSignature(gatingRequest);
```

### Gating Service Validation

The gating service may:

* Perform KYC/AML checks
* Verify the buyer is on an allowlist
* Check reputation scores
* Rate-limit signaling frequency
* Enforce geographic restrictions

<Note>
  If the deposit has **no** gating service (`intentGatingService == address(0)`), this stage is skipped.
</Note>

## Stage 3: Signal Intent

The buyer calls `signalIntent()` on the Orchestrator.

### Transaction Flow

```mermaid theme={null}
sequenceDiagram
    participant Buyer
    participant Orchestrator
    participant Escrow
    participant Registry as PaymentVerifierRegistry
    
    Buyer->>Orchestrator: signalIntent(params)
    Orchestrator->>Orchestrator: _validateSignalIntent()
    Orchestrator->>Registry: Check payment method exists
    Registry-->>Orchestrator: Verifier address
    Orchestrator->>Escrow: getDepositPaymentMethodActive()
    Escrow-->>Orchestrator: isActive = true
    Orchestrator->>Escrow: getDepositCurrencyMinRate()
    Escrow-->>Orchestrator: minRate
    Orchestrator->>Orchestrator: Validate gating signature
    Orchestrator->>Orchestrator: Generate intentHash
    Orchestrator->>Orchestrator: Store Intent struct
    Orchestrator->>Escrow: lockFunds(depositId, intentHash, amount)
    Escrow->>Escrow: Validate deposit state
    Escrow->>Escrow: Prune expired intents if needed
    Escrow->>Escrow: Lock liquidity
    Escrow-->>Orchestrator: Success
    Orchestrator->>Buyer: Emit IntentSignaled event
```

### Code Example

```solidity theme={null}
IOrchestrator.SignalIntentParams memory params = IOrchestrator.SignalIntentParams({
    escrow: escrowAddress,
    depositId: depositId,
    amount: 1000e6,  // 1000 USDC
    to: buyerAddress,
    paymentMethod: keccak256("venmo"),
    fiatCurrency: keccak256("USD"),
    conversionRate: 1.02e18,  // 1.02 USD per USDC
    referrer: referrerAddress,
    referrerFee: 1e16,  // 1%
    gatingServiceSignature: signature,
    signatureExpiration: expiration,
    postIntentHook: IPostIntentHook(bridgeHook),
    data: bridgeHookData
});

orchestrator.signalIntent(params);
```

### State Changes

**Orchestrator**:

* Creates Intent struct with all parameters
* Adds intent hash to `accountIntents[buyer]`
* Stores `intentMinAtSignal[intentHash]` (deposit's min amount at signal time)
* Increments `intentCounter`

**Escrow**:

* Validates deposit is accepting intents
* Validates amount is within deposit's `intentAmountRange`
* Prunes expired intents if liquidity is needed
* Moves `amount` from `remainingDeposits` to `outstandingIntentAmount`
* Creates Intent struct with `expiryTime = timestamp + intentExpirationPeriod`
* Adds intent hash to `depositIntentHashes[depositId]`

### Validation Checks

The Orchestrator performs extensive validation:

1. **Multiple Intent Check**: Buyer must be whitelisted relayer OR `allowMultipleIntents == true`
2. **Zero Address Check**: `to` address must not be zero
3. **Fee Validation**: Referrer fee ≤ 50%, and must be 0 if no referrer
4. **Hook Validation**: If post-intent hook set, must be whitelisted
5. **Escrow Validation**: Must be whitelisted OR registry accepts all escrows
6. **Payment Method**: Must exist in PaymentVerifierRegistry and be active on deposit
7. **Currency**: Must be supported by payment method with non-zero min rate
8. **Conversion Rate**: Must be ≥ deposit's min conversion rate
9. **Gating Signature**: If deposit has gating service, signature must be valid and not expired

## Stage 4: Awaiting Payment

After signaling, the buyer has `intentExpirationPeriod` (configured on Escrow) to complete the off-chain payment.

### Buyer Actions

1. **Make Off-Chain Payment**: Send fiat to the depositor's payment account (Venmo username, PayPal email, etc.)
2. **Include Intent Hash**: Some payment methods allow notes/memos - buyer should include intent hash
3. **Wait for Confirmation**: Payment service confirms transaction

### Intent Status

```solidity theme={null}
// Query intent on Orchestrator
IOrchestrator.Intent memory intent = orchestrator.getIntent(intentHash);

// Check if expired
IEscrow.Intent memory escrowIntent = escrow.getDepositIntent(depositId, intentHash);
bool isExpired = block.timestamp > escrowIntent.expiryTime;
```

## Stage 5: Payment Attestation

Once the off-chain payment is confirmed, an attestation service generates a cryptographic proof.

### Attestation Service Flow

```mermaid theme={null}
graph TD
    A[Monitor Payment Service] -->|Detect payment| B[Extract Payment Details]
    B --> C[Fetch Intent from Orchestrator]
    C --> D[Validate Payment Matches Intent]
    D --> E[Calculate Release Amount]
    E --> F[Construct PaymentAttestation]
    F --> G[Sign with EIP-712]
    G --> H[Return to Buyer/Relayer]
    
    style F fill:#e1f5ff
    style G fill:#fff4e6
```

### Constructing the Attestation

```typescript theme={null}
// Extract from payment service
const paymentDetails = {
    method: keccak256("venmo"),
    payeeId: keccak256(depositorVenmoUsername),
    amount: 1020, // 1020 cents = $10.20
    currency: keccak256("USD"),
    timestamp: paymentTimestampMillis,
    paymentId: keccak256(venmoTransactionId)
};

// Fetch intent data
const intent = await orchestrator.getIntent(intentHash);

// Create snapshot
const intentSnapshot = {
    intentHash: intentHash,
    amount: intent.amount,
    paymentMethod: intent.paymentMethod,
    fiatCurrency: intent.fiatCurrency,
    payeeDetails: intent.payeeId,
    conversionRate: intent.conversionRate,
    signalTimestamp: intent.timestamp,
    timestampBuffer: 3600000  // 1 hour in milliseconds
};

// Calculate release amount (FX conversion)
const fiatPaidCents = paymentDetails.amount;
const fiatPaidUnits = fiatPaidCents / 100;  // 10.20 USD
const conversionRate = intent.conversionRate / 1e18;  // 1.02
const releaseAmount = Math.floor((fiatPaidUnits / conversionRate) * 1e6);  // USDC (6 decimals)

// Encode data
const data = ethers.utils.defaultAbiCoder.encode(
    ["tuple(bytes32,bytes32,uint256,bytes32,uint256,bytes32)", 
     "tuple(bytes32,uint256,bytes32,bytes32,bytes32,uint256,uint256,uint256)"],
    [paymentDetails, intentSnapshot]
);
const dataHash = keccak256(data);

// Construct EIP-712 digest
const domain = {
    name: "UnifiedPaymentVerifier",
    version: "1",
    chainId: chainId,
    verifyingContract: verifierAddress
};

const types = {
    PaymentAttestation: [
        { name: "intentHash", type: "bytes32" },
        { name: "releaseAmount", type: "uint256" },
        { name: "dataHash", type: "bytes32" }
    ]
};

const value = {
    intentHash: intentHash,
    releaseAmount: releaseAmount,
    dataHash: dataHash
};

// Sign with attestor private key(s)
const signature = await attestor._signTypedData(domain, types, value);

// Construct final attestation
const attestation = {
    intentHash: intentHash,
    releaseAmount: releaseAmount,
    dataHash: dataHash,
    signatures: [signature],  // Can be multiple for threshold attestation
    data: data,
    metadata: "0x"  // Optional additional data not signed
};
```

## Stage 6: Fulfill Intent

Anyone can submit the attestation to fulfill the intent.

### Transaction Flow

```mermaid theme={null}
sequenceDiagram
    participant Relayer
    participant Orchestrator
    participant Verifier
    participant Attestation as AttestationVerifier
    participant Nullifier as NullifierRegistry
    participant Escrow
    participant Hook as PostIntentHook
    participant Buyer
    
    Relayer->>Orchestrator: fulfillIntent(attestation)
    Orchestrator->>Orchestrator: Fetch Intent
    Orchestrator->>Verifier: verifyPayment(intentHash, proof)
    Verifier->>Verifier: Decode attestation
    Verifier->>Orchestrator: getIntent(intentHash)
    Orchestrator-->>Verifier: Intent data
    Verifier->>Verifier: Validate intent snapshot
    Verifier->>Attestation: verify(digest, signatures)
    Attestation-->>Verifier: isValid = true
    Verifier->>Nullifier: isNullified(paymentId)
    Nullifier-->>Verifier: false
    Verifier->>Nullifier: addNullifier(paymentId)
    Verifier-->>Orchestrator: PaymentVerificationResult
    Orchestrator->>Orchestrator: Validate min-at-signal
    Orchestrator->>Orchestrator: _pruneIntent()
    Orchestrator->>Escrow: unlockAndTransferFunds()
    Escrow->>Escrow: Unlock liquidity
    Escrow->>Orchestrator: Transfer tokens
    Orchestrator->>Orchestrator: Calculate fees
    Orchestrator->>ProtocolFeeRecipient: Transfer protocol fee
    Orchestrator->>Referrer: Transfer referrer fee
    alt Has PostIntentHook
        Orchestrator->>Hook: execute(intent, netAmount)
        Hook->>Hook: Perform action (bridge, swap, etc.)
    else No Hook
        Orchestrator->>Buyer: Transfer net amount
    end
    Orchestrator->>Relayer: Emit IntentFulfilled
```

### Code Example

```solidity theme={null}
IOrchestrator.FulfillIntentParams memory params = IOrchestrator.FulfillIntentParams({
    paymentProof: abi.encode(attestation),
    intentHash: intentHash,
    verificationData: "",  // Additional data for verifier (if needed)
    postIntentHookData: bridgeData  // Data for post-intent hook
});

orchestrator.fulfillIntent(params);
```

### State Changes

**Verifier**:

* Validates attestation signatures
* Adds payment ID to NullifierRegistry
* Emits `PaymentVerified` event

**Orchestrator**:

* Deletes Intent from storage
* Removes intent hash from `accountIntents`
* Deletes `intentMinAtSignal`
* Emits `IntentPruned` and `IntentFulfilled` events

**Escrow**:

* Moves amount from `outstandingIntentAmount` back to available balance
* Deletes Intent struct
* Removes intent hash from `depositIntentHashes`
* Transfers tokens to Orchestrator
* May close deposit if dust threshold reached
* Emits `FundsUnlockedAndTransferred` event

### Fee Distribution

```solidity theme={null}
// Protocol fee (e.g., 1%)
protocolFeeAmount = (releaseAmount * protocolFee) / 1e18;
transfer(protocolFeeRecipient, protocolFeeAmount);

// Referrer fee (e.g., 1%)
referrerFeeAmount = (releaseAmount * intent.referrerFee) / 1e18;
transfer(intent.referrer, referrerFeeAmount);

// Net amount to buyer
netAmount = releaseAmount - protocolFeeAmount - referrerFeeAmount;
transfer(intent.to, netAmount);  // Or to post-intent hook
```

## Alternative Paths

### Path A: Cancellation

If the buyer changes their mind before making payment:

```solidity theme={null}
orchestrator.cancelIntent(intentHash);
```

**Flow**:

1. Orchestrator validates caller is intent owner
2. Orchestrator deletes intent from storage
3. Orchestrator calls `escrow.unlockFunds(depositId, intentHash)`
4. Escrow returns amount to `remainingDeposits`
5. Escrow deletes intent from storage

**Use Cases**:

* Buyer found better rate elsewhere
* Payment service is down
* Buyer doesn't have sufficient fiat balance

### Path B: Expiration

If the intent is not fulfilled before `expiryTime`:

```solidity theme={null}
// Anyone can call to cleanup expired intents
escrow.pruneExpiredIntents(depositId);
```

**Flow**:

1. Escrow iterates through `depositIntentHashes[depositId]`
2. For each expired intent (`block.timestamp > expiryTime`):
   * Return amount to `remainingDeposits`
   * Delete intent from storage
   * Remove from `depositIntentHashes`
   * Emit `FundsUnlocked` event
3. Escrow calls `orchestrator.pruneIntents(expiredIntents)`
4. Orchestrator deletes each intent from storage

**Automatic Pruning**:
Expired intents are automatically pruned when:

* Depositor calls `removeFunds()` or `withdrawDeposit()`
* New intent is signaled and liquidity is needed
* Anyone calls `pruneExpiredIntents()`

### Path C: Manual Release

If there's a dispute or special arrangement, the depositor can manually release funds:

```solidity theme={null}
orchestrator.releaseFundsToPayer(intentHash);
```

**Flow**:

1. Orchestrator validates caller is the deposit's depositor
2. Orchestrator deletes intent from storage
3. Orchestrator calls `escrow.unlockAndTransferFunds()` with **full intent amount**
4. Escrow transfers tokens to Orchestrator
5. Orchestrator calculates and transfers fees
6. Orchestrator transfers net amount to buyer
7. Emit `IntentFulfilled(intentHash, to, netAmount, isManualRelease: true)`

<Warning>
  **Security**: Manual release still charges protocol and referrer fees. Depositors cannot bypass fee collection.
</Warning>

## Intent States Summary

| State         | Description                        | Can Cancel? | Can Fulfill? | Liquidity   |
| ------------- | ---------------------------------- | ----------- | ------------ | ----------- |
| **Signaled**  | Intent active, awaiting payment    | Yes (owner) | Yes (anyone) | Locked      |
| **Expired**   | Past expiryTime, not yet pruned    | No          | No           | Locked      |
| **Cancelled** | Owner cancelled before fulfillment | N/A         | N/A          | Released    |
| **Fulfilled** | Payment verified and released      | N/A         | N/A          | Transferred |
| **Pruned**    | Expired and cleaned up             | N/A         | N/A          | Released    |
| **Released**  | Manually released by depositor     | N/A         | N/A          | Transferred |

## Timeline Example

```
T=0:00    Buyer signals intent
          └─ Orchestrator: Intent created, hash = 0xabc...
          └─ Escrow: 1000 USDC locked

T=0:05    Buyer makes Venmo payment
          └─ Off-chain: $1020 sent to depositor

T=0:10    Attestation service detects payment
          └─ Off-chain: Verify payment matches intent
          └─ Off-chain: Generate EIP-712 signature

T=0:15    Relayer submits fulfillment
          └─ Orchestrator: verifyPayment()
          └─ Verifier: Validate attestation ✓
          └─ Nullifier: Add payment ID ✓
          └─ Escrow: Release 1000 USDC
          └─ Fees: 10 USDC protocol, 10 USDC referrer
          └─ Buyer receives: 980 USDC

T=0:15    Intent fulfilled ✓
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Monitor Expiry" icon="clock">
    Buyers should complete payment well before `expiryTime` to account for attestation delays
  </Card>

  <Card title="Include Intent Hash" icon="hashtag">
    If payment service supports memos, include intent hash for faster attestation matching
  </Card>

  <Card title="Use Relayers" icon="network-wired">
    Buyers can outsource fulfillment submission to relayers to save gas and improve UX
  </Card>

  <Card title="Verify Deposit" icon="magnifying-glass">
    Before signaling, verify deposit has sufficient liquidity and acceptable parameters
  </Card>
</CardGroup>

## Related Contracts

* [Orchestrator](/concepts/orchestrator) - Manages intent lifecycle
* [Escrow](/concepts/escrow) - Locks and releases liquidity
* [Payment Verification](/concepts/payment-verification) - Validates off-chain payments
* [Registry System](/concepts/registry-system) - Whitelisting and configuration
