@txfence/core
Policy engine, agent orchestration, cap locking, receipt storage, webhook approval, temporal rules, intent execution, and a chain-agnostic registry. This is the only required package — every other @txfence/* package plugs into it.
createAgent
Primary entry point. Wires policy, adapters, RPC URLs, and an executor into an agent with submit, dryRun, executeIntent, shutdown, health, and isShuttingDown methods. The signature is positional with 16 slots — pass undefined for slots you don’t need.
function createAgent(
config: AgentConfig, // 1
adapters: AdapterMap, // 2
rpcUrls: Partial<Record<ChainId, string>>, // 3
executor?: (action, chainId, rpcUrl, evaluation, simulation) => Promise<SuccessReceipt>, // 4
capLockProvider?: CapLockProvider, // 5
metadataVerifier?: MetadataVerifier, // 6
approvalProvider?: ApprovalProvider, // 7
receiptStore?: ReceiptStore, // 8
auditLog?: AuditLog, // 9
telemetryProvider?: TelemetryProvider, // 10
capLockConfigs?: CapConfig[], // 11
policyNode?: PolicyNode, // 12
notificationProvider?: NotificationProvider, // 13
provenanceChain?: ProvenanceChain, // 14
eventStore?: EventStore, // 15
agentId?: string, // 16
): AgentAgentConfig is { chains, policies: Policy, signer }. policies is required even when you also pass a policyNode — see the Composite Policies guide.
Agent
type Agent = {
submit: (input: { action: Action; policy: Policy }) => Promise<ExecutionResult>
dryRun: (input: { action: Action; policy: Policy }) => Promise<DryRunResult>
executeIntent: (intent: Intent, options?: Partial<IntentExecutionOptions>) => Promise<IntentExecutionResult>
shutdown: (timeoutMs?: number) => Promise<AgentShutdownResult>
isShuttingDown: () => boolean
health: () => AgentHealth
config: AgentConfig
}Default shutdown timeout is 30 seconds. See Agent Health & Shutdown, Dry Run Mode, Intent Execution.
runPipeline
Low-level pipeline if you don’t want to wrap state in an Agent. Same 16 positional slots after the leading action and policy.
async function runPipeline(
action: Action,
policy: Policy,
adapters: AdapterMap,
rpcUrls: Partial<Record<ChainId, string>>,
executor?: /* ... same as createAgent ... */,
// capLockProvider, metadataVerifier, approvalProvider, receiptStore,
// auditLog, telemetryProvider, policyNode, notificationProvider,
// (capLockConfigs is on createAgent only — pipeline doesn't need it)
// provenanceChain, eventStore, agentId
): Promise<ExecutionResult>Pipeline stages
- Policy evaluation — six static checks against action and policy
- Temporal rules — if
policy.temporalRulesand aneventStoreare set - Simulation —
adapter.simulate(); gas buffer and revert check - Staleness check — if
policy.simulationStalenessMsis set, reject if simulation is older - Cap lock acquire — two-phase reservation
- Human approval — if action value ≥
humanApprovalThreshold - Execution —
executorsigns and broadcasts - Cap lock commit/release — based on execution result
ExecutionResult
type ExecutionResult =
| { status: 'success'; receipt: SuccessReceipt }
| { status: 'policy_rejected'; action: Action; evaluation: PolicyEvaluation }
| { status: 'simulation_failed'; action: Action; simulation: SimulationResult }
| { status: 'simulation_stale'; action: Action; simulation: SimulationResult; stalenessMs: number }
| { status: 'approval_timeout'; action: BoundAction }
| { status: 'execution_failed'; action: Action; txHash: string; reason: ExecutionFailureReason }ExecutionFailureReason is a discriminated union with codes no_executor, executor_threw, signing_failed, broadcast_failed. Use formatExecutionFailureReason(reason) for a human-readable string.
Policy
type Policy = {
chains: ChainId[]
maxSpendPerTx: TokenAmount
allowedContracts: ContractEntry[]
requireSimulation: boolean
gasBufferMultiplier: number
humanApprovalThreshold: TokenAmount
humanApprovalTimeoutMs: number
capLockMode: 'per-agent' | 'shared'
capLocks?: CapConfig[]
simulationStalenessMs?: number
mevProtection?: 'flashbots' | 'mev-blocker' | 'none'
temporalRules?: TemporalRule[]
}
type TokenAmount = { token: string; amount: bigint; decimals: number }
type ContractEntry = { address: string; chain: ChainId; bytecodeHash?: string; ownerAddress?: string; expiresAt?: number }See MEV Protection, Temporal Rules, Config Validation.
Action
Three action kinds — transfer, swap, contract_call.
type TransferAction = {
kind: 'transfer'
chain: ChainId
token: TokenAmount
to: string
calldata?: `0x${string}`
solanaTransaction?: Uint8Array
cosmosTransaction?: Uint8Array
}
type SwapAction = {
kind: 'swap'
chain: ChainId
from: TokenAmount // source token + amount
to: string // destination token symbol
via: string // router contract address
maxSlippage: number // basis points; 0 is rejected as 'not declared'
calldata?: `0x${string}`
solanaTransaction?: Uint8Array
cosmosTransaction?: Uint8Array
}
type ContractCallAction = {
kind: 'contract_call'
chain: ChainId
contract: string
method: string
args: unknown[]
value?: TokenAmount
calldata?: `0x${string}`
solanaAccounts?: SolanaAccountMeta[]
solanaData?: Uint8Array
solanaTransaction?: Uint8Array
cosmosTransaction?: Uint8Array
}
type Action = TransferAction | SwapAction | ContractCallActionmaxSlippage: 0 is treated as not declared and is always rejected. Set a non-zero value even for zero-tolerance swaps — use 1 for one-basis-point tolerance.
ReceiptStore
type ReceiptStore = {
save: (receipt: SuccessReceipt) => Promise<void>
get: (txHash: string) => Promise<SuccessReceipt | null>
list: (filter?: ReceiptFilter) => Promise<SuccessReceipt[]>
}
type ReceiptFilter = {
chain?: ChainId
from?: number // start block, inclusive
to?: number // end block, inclusive
}In-memory: createMemoryReceiptStore(). File-backed NDJSON: createFileReceiptStore(path). PostgreSQL: @txfence/storage-pg. SQLite: @txfence/storage-sqlite.
CapLockProvider
type CapLockProvider = {
acquire: (capId: string, amount: bigint, token: string) => Promise<CapLockResult>
release: (capId: string, lockId: string, amount: bigint) => Promise<void>
commit: (capId: string, lockId: string, amount: bigint) => Promise<void>
onCapWarning?: (event: CapWarningEvent) => void
inspect?: (capId: string) => Promise<CapInspection>
}
type CapLockResult =
| { granted: true; lockId: string }
| { granted: false; reason: 'absolute_cap_exceeded' | 'rolling_window_exceeded' }commit and release take the lockId returned by acquire — the pipeline tracks IDs internally so callers rarely interact with them directly.
createMemoryCapLockProvider
function createMemoryCapLockProvider(
configs: CapConfig[],
options?: { onCapWarning?: (event: CapWarningEvent) => void },
): CapLockProvider & { inspect: (capId: string) => Promise<CapInspection> }
type CapConfig = {
capId: string
absoluteCap?: { maxAmount: bigint; token: string }
rollingWindow?: { windowMs: number; maxAmount: bigint; token: string }
warningThresholdPct?: number // 0-100, triggers onCapWarning
}Single-process only. For distributed deployments, use @txfence/redis.
ApprovalProvider
type ApprovalProvider = {
request: (req: ApprovalRequest) => Promise<void>
poll: (token: string) => Promise<ApprovalDecision | null>
}
type ApprovalDecision = 'approved' | 'rejected'request dispatches the approval (e.g. webhook POST). poll returns null while pending. Cancel-on-timeout is enforced by the pipeline — a timeout returns { status: 'approval_timeout' }, never falls through to execution.
createWebhookApprovalProvider
function createWebhookApprovalProvider(
webhookUrl: string,
pollUrl: string,
options?: {
webhookSecret?: string // enables HMAC-SHA256 signing of the payload
pollIntervalMs?: number
},
): ApprovalProviderPOSTs the ApprovalRequest to webhookUrl with an X-TXFence-Signature header when webhookSecret is set. The payload includes approveUrl and rejectUrl for one-click approval via email.
Low-level helpers
Exports useful when building custom infrastructure around the engine. None of these are needed for normal createAgent usage.
Policy evaluation
function evaluate(
policy: Policy,
action: Action,
simulationResult?: SimulationResult,
): PolicyEvaluationPure synchronous policy evaluator. Returns { passed, checksRun, rejectionReason? }. Used internally by runPipeline.
function evaluateNode(
node: PolicyNode,
action: Action,
simulationResult?: SimulationResult,
): PolicyNodeEvaluationTree-aware version for composite policies. Returns a tree-structured result that preserves which branches passed.
Adapter composition
function createMultiChainAdapter(adapters: AdapterMap): ChainAdapterWraps an AdapterMap ({ ethereum: { simulate }, arbitrum: { simulate } }) into a single ChainAdapter that dispatches by action.chain. Used when a downstream API expects one adapter.
function wrapAdapterWithCircuitBreaker(
adapter: ChainAdapter,
breaker: CircuitBreaker,
chainId: ChainId,
): ChainAdapterSee Circuit Breaker.
Error formatting
function getPolicyRejectionMessage(reason: PolicyRejectionReason): string
function getSimulationFailureMessage(result: SimulationResult): string
function formatExecutionFailureReason(reason: ExecutionFailureReason): stringAll three produce human-readable strings for logs, CLI output, or UI display.
Serialization
function bigintReplacer(key: string, value: unknown): unknown
function serializeWithBigInt(obj: unknown): string
function parseWithBigInt(json: string): unknown
function reviveTokenAmount(raw: unknown): TokenAmount
function revivePolicy(raw: unknown): Policy
function reviveAction(raw: unknown): Action
function reviveSimulationResult(raw: unknown): SimulationResult
function reviveSuccessReceipt(raw: unknown): SuccessReceiptUsed by every storage backend (file, SQLite, PostgreSQL, audit, provenance) so the same bigint-handling round-trip is consistent across the project.
Contract test suites
Every interface has a portable contract suite — run them against your own implementation to verify substitutability:
import { receiptStoreContract, capLockProviderContract, auditLogContract, approvalProviderContract } from '@txfence/core/contracts'
receiptStoreContract('My S3 receipt store', () => createS3ReceiptStore({ ... }))Other exports
The registry (asset, protocol, maxSpend, createRegistry, defaultRegistry, BUILT_IN_ASSETS, BUILT_IN_PROTOCOLS) is documented in Chain-Agnostic Policy.
Notification providers (createConsoleNotificationProvider, createWebhookNotificationProvider, createCompositeNotificationProvider) are documented in Notifications.
Telemetry (noopTelemetry, TelemetryProvider, Span) is documented in Telemetry.
Policy versioning (getPolicyVersionId, createPolicyVersion, createPolicyVersionStore) is documented in Policy Versioning.
Multi-agent coordination (createMemoryAgentCoordinator, getIntentId, getIntentIdWithNonce) is documented in Multi-Agent Coordination.
Intent helpers (executeIntent, evaluateIntent, validateIntentGraph, getExecutionPlan, getPoisonedSteps, analyzeIntentPosition, getActionPositionChanges, mergePositionChanges, isSingleTokenIntent, getDominantToken, checkMaxSteps) are documented in Intent Execution.
Temporal rules (createMemoryEventStore, evaluateTemporalRules) are documented in Temporal Rules.
Replay (replayAuditLog) is documented in Replay & Backtesting.
Diff (diffPolicies, createTestActions) is documented in Policy Diff.
Validation (validateConfig) is documented in Config Validation.