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:
| Kind | Description |
|---|---|
transfer | Send native tokens or fungible assets to an address |
swap | Exchange tokens via a DEX or aggregator |
contract_call | Invoke 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.