Skip to Content
Core Concepts

Core Concepts

The Policy Object

A policy is a flat typed configuration object that declares what an agent is allowed to do. You pass it to createAgent or runPipeline — there are no rules arrays, no match clauses, no defaults object.

import type { Policy } from '@txfence/core' const policy: Policy = { chains: ['ethereum'], maxSpendPerTx: { token: 'USDC', amount: 1000n, decimals: 6 }, allowedContracts: [{ address: '0xYOUR_CONTRACT', chain: 'ethereum' }], requireSimulation: true, gasBufferMultiplier: 1.2, humanApprovalThreshold: { token: 'USDC', amount: 10000n, decimals: 6 }, humanApprovalTimeoutMs: 30000, capLockMode: 'per-agent', }

Key fields

chains — the only chains this agent is permitted to operate on. Any action on an unlisted chain is rejected immediately.

maxSpendPerTx — maximum spend per transaction, denominated in a specific token. The policy engine converts action amounts to USD using token prices at evaluation time.

allowedContracts — explicit allowlist of contract addresses per chain. Swaps and contract calls to unlisted addresses are rejected.

requireSimulation — when true, the pipeline rejects any action whose simulation could not be completed.

gasBufferMultiplier — minimum gas buffer multiplier. The pipeline rejects actions whose gas estimate does not include at least this buffer.

humanApprovalThreshold — actions above this amount require a human to approve before execution. Cancel-on-timeout is the hard default.

temporalRules — optional array of TemporalRule objects evaluated after static checks. Used to detect behavioral patterns like spend velocity, failure rate, and approval floods over sliding time windows.

Action Types

txfence normalizes every chain operation into one of three action kinds:

KindDescription
transferSend native tokens or fungible assets to an address
swapExchange tokens via a DEX or aggregator
contract_callInvoke an arbitrary contract function
import type { Action } from '@txfence/core' const action: Action = { kind: 'transfer', chain: 'ethereum', token: { token: 'ETH', amount: 100000000000000000n, decimals: 18 }, to: '0xRECIPIENT', }

For swaps, the action includes maxSlippage in basis points. For contract calls, the action includes pre-encoded calldata — the builder encodes, txfence handles policy, simulation, and execution.

The Evaluation Pipeline

Every action passes through a fixed sequence of stages. No stage can be skipped.

1. Policy check

Six checks run synchronously against the action and policy:

  • Chain allowed — is the action’s chain in policy.chains?
  • Contract allowlist — for swaps and contract calls, is the target on allowedContracts?
  • Spend cap — does the action’s value exceed maxSpendPerTx?
  • Slippage declared — for swaps, is maxSlippage > 0?
  • Simulation required — if requireSimulation: true, is simulation configured?
  • Gas buffer — does the gas estimate include at least gasBufferMultiplier?

The first failing check short-circuits evaluation and returns a PolicyRejectionReason.

2. Temporal rules (optional)

If policy.temporalRules is set and an EventStore is provided, temporal predicates are evaluated after static checks. Six predicate kinds are supported: simulation_failure_rate, contract_call_frequency, success_drought, spend_velocity, consecutive_failures, and approval_flood.

3. Simulation

The action is executed against current chain state via eth_call or Tenderly deep simulation. This catches reverts, insufficient balances, and gas estimation failures without spending gas or mutating state.

Simulation results include gas, coverageLevel (basic or deep), wouldRevert, and caveats. The state_may_diverge caveat is always present — simulation is a snapshot, execution happens later.

4. Human approval (optional)

If the action’s value exceeds humanApprovalThreshold, the pipeline dispatches a webhook and waits up to humanApprovalTimeoutMs for a human decision. The webhook payload is signed with HMAC-SHA256. Cancel-on-timeout is the hard default — approval timeout never defaults to execution.

5. Execution

If all checks pass and approval is granted (or not required), the signed transaction is broadcast to the chain. The receipt is stored, the audit log is updated, and the monitor reconciles the on-chain result.

createAgent

createAgent is the primary entry point. It wires policy, adapters, RPC URLs, and an executor into a single agent instance with a submit() method.

import { createAgent } from '@txfence/core' import { simulateEvmAction, executeEvmAction, privateKeySigner } from '@txfence/evm' const signer = privateKeySigner(process.env.PRIVATE_KEY as `0x${string}`) const agent = createAgent( { chains: ['ethereum'], policies: policy, signer }, { ethereum: { simulate: simulateEvmAction } }, { ethereum: 'https://ethereum.publicnode.com' }, (action, chainId, rpcUrl, evaluation, simulation) => executeEvmAction(action, chainId, rpcUrl, signer, evaluation, simulation) ) const result = await agent.submit({ action, policy })

result.status is one of: success, policy_rejected, simulation_failed, approval_timeout, execution_failed, or simulation_stale.

Dry Run Mode

Call agent.dryRun(input) to run the full pipeline — including simulation and policy evaluation — without executing on-chain. Returns a DryRunResult with wouldProceed, blockers, and approvalRequired.

const dry = await agent.dryRun({ action, policy }) console.log(dry.wouldProceed, dry.blockers)

txfence submit defaults to dry-run mode in the CLI. Pass --execute to broadcast a real transaction.

Composite Policies

For complex authorization logic, wrap flat policies in an AND/OR tree using policyAnd, policyOr, and policyLeaf:

import { policyAnd, policyOr, policyLeaf } from '@txfence/core' const treasuryPolicy = policyAnd([ policyOr([ policyLeaf(smallSpendPolicy, 'small-spend'), policyLeaf(largeSpendPolicy, 'large-spend'), ], 'spend-tier'), policyLeaf(contractAllowlistPolicy, 'allowlist'), ], 'treasury')

Pass policyNode to createAgent instead of a flat policy to use composite evaluation.

Last updated on