Overview
Learn how post-conditions protect users from unexpected transaction outcomes.
Post-conditions are security features in Stacks that protect users by ensuring transactions execute exactly as expected. They verify that specific asset transfers occur during a transaction, and if the conditions aren't satisfied, the entire transaction fails with no state changes.
What are post-conditions?
Post-conditions act as safeguards that verify asset transfers match your expectations. They can check STX transfers, fungible tokens, and non-fungible token ownership changes.
import { Pc } from '@stacks/transactions';
const tx = await makeContractCall({
// ...
postConditions: [
Pc.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6').willSendEq(1000).ustx(),
],
});Post-conditions only verify that assets are sent, not received. They cannot guarantee the final recipient of tokens.
Using the Pc helper
The Pc helper provides a fluent API for creating post-conditions with better type safety and readability.
import { Pc } from '@stacks/transactions';
// STX transfer post-condition
const stxCondition = Pc
.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
.willSendGte(1000)
.ustx();
// Fungible token post-condition
const ftCondition = Pc
.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
.willSendEq(50)
.ft('SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-token', 'my-token');
// NFT post-condition
const nftCondition = Pc
.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6')
.willSendAsset()
.nft('SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-nft::my-asset', Cl.uint(1));Manual creation
Create post-conditions manually using type definitions when building conditions dynamically.
import {
StxPostCondition,
FungiblePostCondition,
NonFungiblePostCondition
} from '@stacks/transactions';
// STX post-condition
const stxPostCondition: StxPostCondition = {
type: 'stx-postcondition',
address: 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B',
condition: 'gte', // 'eq' | 'gt' | 'gte' | 'lt' | 'lte'
amount: '100',
};Available condition types:
eq: Exactly equal to amountgt: Greater than amountgte: Greater than or equal to amountlt: Less than amountlte: Less than or equal to amount
Fungible tokens
const ftPostCondition: FungiblePostCondition = {
type: 'ft-postcondition',
address: 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B',
condition: 'eq',
amount: '100',
asset: 'SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-ft-token::my-token',
};Non-fungible tokens
const nftPostCondition: NonFungiblePostCondition = {
type: 'nft-postcondition',
address: 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B',
condition: 'sent', // 'sent' | 'not-sent'
asset: 'SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-nft::my-asset',
assetId: Cl.uint(602),
};Post-condition mode
Control how unspecified asset transfers are handled with post-condition mode.
import { PostConditionMode } from '@stacks/transactions';
const tx = await makeContractCall({
// ...
postConditionMode: PostConditionMode.Deny,
postConditions: [
// your conditions
],
});Post-condition mode
Always use Deny mode unless you have a specific reason to allow additional transfers. This provides maximum security for users.
Was this helpful?
