# Implementation

Post-conditions are a powerful security feature in Stacks that protect users from unexpected transaction outcomes. This tutorial will walk you through implementing post-conditions in your applications to ensure transactions behave exactly as users expect.

### What you'll learn

* Construct post-conditions using the `Pc` helper API
* Add post-conditions to different transaction types
* Configure post-condition modes for transaction security
* Implement post-conditions for STX, fungible tokens, and NFTs
* Handle semi-fungible tokens (SFTs) with post-conditions

## Constructing post-conditions

The Pc helper in Stacks.js provides a fluent, BDD-inspired API for constructing post-conditions. Start with `Pc.principal()` to specify which address will be verified, then chain methods to define the condition.

```ts
import { Pc } from '@stacks/transactions';

// Basic structure of a post-condition
const postCondition = Pc
  .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
  .willSendEq(1000)
  .ustx();
```

The `Pc` helper uses method chaining for intuitive condition building. Your IDE will provide auto-completion for available methods at each step.

***

## Available transfer methods

Post-conditions support different comparison operators and asset types. Choose the appropriate method based on your security requirements.

### STX and fungible token methods

```ts
// Exact amount
Pc.principal(address).willSendEq(1000).ustx();

// Greater than or equal
Pc.principal(address).willSendGte(500).ustx();

// Less than
Pc.principal(address).willSendLt(2000).ustx();
```

Comparison methods available:

* `.willSendEq(amount)` - Exactly equal to amount
* `.willSendGte(amount)` - Greater than or equal to amount
* `.willSendGt(amount)` - Greater than amount
* `.willSendLte(amount)` - Less than or equal to amount
* `.willSendLt(amount)` - Less than amount

### Asset type methods

```ts
// STX transfers
.ustx()

// Fungible token transfers
.ft(contractAddress, tokenName)

// NFT transfers
.nft(assetIdentifier, tokenId)
```

### NFT-specific methods

```ts
// Ensure NFT is sent
Pc.principal(address).willSendAsset().nft(...);

// Ensure NFT is NOT sent
Pc.principal(address).willNotSendAsset().nft(...);
```

***

## Setting the post-condition mode

The post-condition mode determines how the Stacks protocol handles asset transfers not explicitly covered by your post-conditions. This is a critical security setting.

```ts
import { PostConditionMode, makeContractCall } from '@stacks/transactions';

const tx = await makeContractCall({
  // ... other transaction properties
  postConditionMode: PostConditionMode.Deny, // Recommended default
  postConditions: [
    // your post-conditions here
  ],
});
```

Mode options:

* PostConditionMode.Deny (default): Transaction fails if any unspecified transfers occur
* PostConditionMode.Allow: Transaction allows transfers beyond specified post-conditions

***

## Common implementation patterns

### STX transfer post-conditions

Protect STX transfers by specifying exact amounts or ranges.

```ts
import { Pc, makeSTXTokenTransfer } from '@stacks/transactions';

// Exact amount post-condition
const exactAmountCondition = Pc
  .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
  .willSendEq(1000)
  .ustx();

// Use in a transaction
const tx = await makeSTXTokenTransfer({
  recipient: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM',
  amount: 1000,
  postConditions: [exactAmountCondition],
  postConditionMode: PostConditionMode.Deny,
  // ... other properties
});
```

### Fungible token post-conditions

Ensure fungible tokens are transferred as expected in contract calls.

```ts
import { Pc, makeContractCall } from '@stacks/transactions';

// Minimum amount condition
const ftCondition = Pc
  .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
  .willSendGte(500)
  .ft('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.token-ft', 'token');

// Use in a contract call
const tx = await makeContractCall({
  contractAddress: 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6',
  contractName: 'token-transfer',
  functionName: 'transfer',
  functionArgs: [
    // ... function arguments
  ],
  postConditions: [ftCondition],
  // ... other properties
});
```

### NFT transfer post-conditions

Control NFT ownership changes with specific post-conditions.

```ts
import { Pc, Cl } from '@stacks/transactions';

// Ensure NFT is sent
const sendNftCondition = Pc
  .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
  .willSendAsset()
  .nft('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.nft-contract::nft-name', Cl.uint(1));

// Ensure NFT is NOT sent (protection against unwanted transfers)
const keepNftCondition = Pc
  .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
  .willNotSendAsset()
  .nft('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.nft-contract::nft-name', Cl.uint(1));
```

Use `willNotSendAsset()` to protect valuable NFTs from being transferred unexpectedly.

### Semi-fungible token (SFT) post-conditions

SFTs require special handling as they have both fungible and non-fungible properties.

```ts
import { Cl, Pc } from '@stacks/transactions';

// SFT as NFT (specific token ID)
const sftNftCondition = Pc
  .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
  .willSendAsset()
  .nft(
    'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.sft-contract::sft-id',
    Cl.tuple({
      'token-id': Cl.uint(1),
      owner: Cl.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
    })
  );

// SFT as FT (amount-based)
const sftFtCondition = Pc
  .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
  .willSendEq(500)
  .ft('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.sft-contract', 'sft-token');
```

## Multiple post-conditions

Complex transactions often require multiple post-conditions to fully protect all asset transfers.

```ts
const tx = await makeContractCall({
  // ... transaction properties
  postConditions: [
    // Sender must send exactly 1000 uSTX
    Pc.principal(senderAddress).willSendEq(1000).ustx(),

    // Contract must send at least 100 tokens to user
    Pc.principal(contractAddress).willSendGte(100)
      .ft(contractAddress + '.my-token', 'my-token'),

    // User must not lose their NFT
    Pc.principal(senderAddress).willNotSendAsset()
      .nft(nftContract + '::my-nft', Cl.uint(1)),
  ],
  postConditionMode: PostConditionMode.Deny,
});
```


---

# 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.stacks.co/post-conditions/implementation.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.
