@cfxdevkit/automation
Automation strategies (limit, dca, stop-loss, scheduled).
Install
pnpm
pnpm add @cfxdevkit/automationScope: Off-chain automation strategies executable by framework/executor.
Responsibilities
- Strategy interface (
evaluate,buildTx,verify) - Built-in strategies: limit order, DCA, TWAP, condition-based swaps
- Strategy persistence schema (via
JobRepository,ExecutionRepository) - Backtesting helpers (via
PriceChecker,SwappiPriceSource,GeckoTerminalPriceSource) - Safety checks and retry logic (via
SafetyConfig,RetryQueue)
Non-goals
- Keeper transport / queue (lives in
framework/executor). - Project-specific UI (lives in
projects/cas/apps/frontend).
Origin: cas/conflux-cas/worker patterns + cas/conflux-sdk automation types.
Installation
npm install @cfxdevkit/automationSub-paths
| Sub-path | Exports |
|---|---|
. | 92 symbols |
./db | 11 symbols |
Key Concepts
Strategies
Strategies define when and how to execute an automation job. Each strategy implements:
evaluate(context: StrategyEvalContext): EvalResult— determines if job should runbuildTx(params: TParams): BuildSwapCalldataInput— constructs transaction calldataverify(result: ExecuteResult): boolean— confirms successful execution
Supported strategies:
LimitOrderStrategyDCAStrategyTWAPStrategySwapStrategy(for condition-based swaps)
Execution Flow
KeeperpollsJobRepositoryfor active jobs.- For each job,
StrategyEvaluatorrunsevaluate()usingPriceSource. - If triggered,
buildTx()prepares calldata. SwapExecutorClientsubmits transaction viaAutomationManager.ExecutionRepositoryrecords outcome;RetryQueuehandles failures.
Safety & Reliability
SafetyConfigdefines thresholds (e.g., max slippage, price deviation).SafetyContextandSafetyCheckResultenforce constraints before execution.RetryQueuemanages transient failures with exponential backoff.
Usage
Create a Limit Order Job
import {
LimitOrderStrategy,
MemoryJobRepository,
SwappiPriceSource,
PriceChecker,
DEFAULT_SAFETY_CONFIG,
} from '@cfxdevkit/automation';
const priceSource = new SwappiPriceSource({
routerAddress: SWAPPI_ADDRESSES.router,
quoteReader: new SwappiQuoteReader(),
});
const jobRepo = new MemoryJobRepository();
const priceChecker = new PriceChecker(priceSource, DEFAULT_SAFETY_CONFIG);
const strategy = new LimitOrderStrategy(priceChecker);
const job = {
id: 'limit-0x123',
type: 'LIMIT_ORDER',
params: {
tokenIn: '0x...',
tokenOut: '0x...',
amountIn: 1_000_000n,
amountOutMin: 950_000n,
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
},
strategy: strategy,
};
await jobRepo.add(job);Run the Keeper
import { Keeper } from '@cfxdevkit/automation';
const keeper = new Keeper({
jobRepo,
executionRepo: new MemoryExecutionRepository(),
retryQueue: { maxRetries: 3, baseDelayMs: 1000 },
priceSource,
safetyConfig: DEFAULT_SAFETY_CONFIG,
});
keeper.start();Backtest with Historical Prices
import { GeckoTerminalPriceSource } from '@cfxdevkit/automation';
const historicalSource = new GeckoTerminalPriceSource({
poolAddress: '0x...',
network: 'conflux-espace',
});
const backtestResult = await priceChecker.evaluate({
job,
timestamp: 1700000000,
priceSource: historicalSource,
});API Reference
See full exports in API.md. Key classes include:
AutomationManagerClient— interacts with on-chain automation managerKeeperClientImpl— low-level keeper client for custom transportsSwappiPriceSource/GeckoTerminalPriceSource— price feedsMemoryJobRepository/MemoryExecutionRepository— in-memory storage (use for testing)RetryQueue— handles failed executions
Note: For production use, implement custom
JobRepositoryandExecutionRepositorybacked by persistent storage (e.g., PostgreSQL, Redis).
API REFERENCE EXCERPT
export declare const __packageName: "@cfxdevkit/automation";
export declare const PRICE_SCALE: bigint;
export declare const DEFAULT_DCA_DUE_BUFFER_SECONDS = 15;
export declare const AUTOMATION_MANAGER_ADDRESSES: {
confluxESpace: string;
confluxCore: string;
};
export declare const AUTOMATION_MANAGER_ABI: readonly [
// ... ABI entries
];
export declare const DEFAULT_SAFETY_CONFIG: SafetyConfig;
export declare const SWAPPI_ROUTER_ABI: readonly [
// ... ABI entries
];
export declare const SWAPPI_ADDRESSES: {
confluxESpace: string;
confluxCore: string;
};
export interface PriceSource {
getSpotPrice(tokenIn: string, tokenOut: string): Promise<bigint>;
getHistoricalPrice(tokenIn: string, tokenOut: string, timestamp: number): Promise<bigint>;
}
export interface LimitOrderCheckResult {
triggered: boolean;
reason?: string;
}
export interface DCACheckResult {
due: boolean;
nextDueTime?: number;
}
export interface KeeperClient {
execute(job: ExecutableJob): Promise<ExecuteResult>;
}
export interface AutomationManagerActions {
execute(job: ExecutableJob): Promise<ExecuteResult>;
}
export interface AutomationManagerClientConfig {
chainId: number;
signer: Signer;
}
export interface KeeperClientImplConfig {
automationManager: AutomationManagerActions;
}
export interface KeeperConfig {
jobRepo: JobRepository;
executionRepo: ExecutionRepository;
retryQueue: RetryQueueOptions;
priceSource: PriceSource;
safetyConfig: SafetyConfig;
}
export interface KeeperDeps {
jobRepo: JobRepository;
executionRepo: ExecutionRepository;
retryQueue: RetryQueue;
priceSource: PriceSource;
safetyConfig: SafetyConfig;
}
export interface GeckoTerminalPriceSourceOptions {
poolAddress: string;
network: 'conflux-espace' | 'conflux-core';
}
export interface SwappiQuoteReader {
getSwapQuote(input: SwapQuoteInput): Promise<SwapExecuteResult>;
}
export interface SwappiPriceSourceOptions {
routerAddress: string;
quoteReader: SwappiQuoteReader;
}
export interface JobListFilter {
status?: 'ACTIVE' | 'COMPLETED' | 'FAILED';
type?: JobType;
}
export interface JobUpdate {
status?: 'ACTIVE' | 'COMPLETED' | 'FAILED';
}
export interface JobRepository {
add(job: ExecutableJob): Promise<void>;
get(id: string): Promise<ExecutableJob | null>;
list(filter?: JobListFilter): Promise<ExecutableJob[]>;
update(id: string, update: JobUpdate): Promise<void>;
}
export interface ExecutionRecord {
id: string;
jobId: string;
status: 'PENDING' | 'SUCCESS' | 'FAILED';
timestamp: number;
txHash?: string;
error?: string;
}
export interface NewExecutionRecord {
jobId: string;
status: 'PENDING' | 'SUCCESS' | 'FAILED';
txHash?: string;
error?: string;
}
export interface ExecutionRepository {
add(record: NewExecutionRecord): Promise<void>;
listByJob(jobId: string): Promise<ExecutionRecord[]>;
}
export interface RetryQueueOptions {
maxRetries: number;
baseDelayMs: number;
}
export interface SafetyConfig {
maxSlippageBps: number;
maxPriceDeviationBps: number;
}
export interface SafetyContext {
job: ExecutableJob;
currentPrice: bigint;
expectedPrice?: bigint;
}
export interface SafetyViolation {
type: 'SLIPPAGE' | 'PRICE_DEVIATION';
message: string;
}
export interface SafetyCheckResult {
passed: boolean;
violations?: SafetyViolation[];
}
export interface LimitOrderStrategy {
evaluate(context: StrategyEvalContext): Promise<LimitOrderCheckResult>;
buildTx(params: LimitOrderParams): BuildSwapCalldataInput;
verify(result: ExecuteResult): boolean;
}
export interface DCAStrategy {
evaluate(context: StrategyEvalContext): Promise<DCACheckResult>;
buildTx(params: DCAParams): BuildSwapCalldataInput;
verify(result: ExecuteResult): boolean;
}
export interface TWAPStrategy {
evaluate(context: StrategyEvalContext): Promise<EvalResult>;
buildTx(params: TWAPParams): BuildSwapCalldataInput;
verify(result: ExecuteResult): boolean;
}
export interface SwapStrategy {
evaluate(context: StrategyEvalContext): Promise<EvalResult>;
buildTx(params: SwapParams): BuildSwapCalldataInput;
verify(result: ExecuteResult): boolean;
}
export interface StrategyEvalContext {
job: ExecutableJob;
timestamp: number;
priceSource: PriceSource;
}
export interface StrategyEvaluator<TJob extends ExecutableJob = ExecutableJob> {
evaluate(job: TJob, context: StrategyEvalContext): Promise<EvalResult>;
}
export interface BuildSwapCalldataInput {
calldata: string;
value?: bigint;
}
export interface SwapExecutorClient {
execute(input: SwapExecuteInput): Promise<ExecuteResult>;
}
export interface SwapExecutorOptions {
chainId: number;
signer: Signer;
}
export interface SwapRouterAddresses {
router: string;
factory: string;
}
export interface SwapQuoteInput {
tokenIn: string;
tokenOut: string;
amountIn: bigint;
recipient: string;
}
export interface SwapExecuteInput extends SwapQuoteInput {
deadline: bigint;
slippageTolerance: number;
}
export interface SwapExecuteResult {
amountOut: bigint;
path: string[];
}
export interface SwapReceiptLog {
address: string;
topics: string[];
data: string;
}
export interface SwapReceipt {
status: number;
logs: SwapReceiptLog[];
}
export interface BaseJobParams {
tokenIn: string;
tokenOut: string;
amountIn: bigint;
}
export interface LimitOrderParams extends BaseJobParams {
amountOutMin: bigint;
deadline: bigint;
}
export interface DCAParams extends BaseJobParams {
amountOutMin: bigint;
intervalSeconds: number;
totalDurationSeconds: number;
totalAmountIn: bigint;
}
export interface TWAPParams extends BaseJobParams {
amountOutMin: bigint;
durationSeconds: number;
intervalSeconds: number;
}
export interface SwapParams extends BaseJobParams {
amountOutMin: bigint;
deadline: bigint;
condition: 'BELOW' | 'ABOVE';
threshold: bigint;
}
export interface BaseJob<TType extends JobType, TParams> {
id: string;
type: TType;
params: TParams;
strategy: Strategy;
}
export interface ExecuteResult {
success: boolean;
txHash?: string;
error?: string;
}
export interface TickResult {
nextTickTime?: number;
}
export interface EvalResult {
triggered: boolean;
tickResult?: TickResult;
}
export declare class PriceChecker {
constructor(priceSource: PriceSource, config: SafetyConfig);
evaluate(context: StrategyEvalContext): Promise<EvalResult>;
}
export declare class AutomationManagerClient implements KeeperClient {
constructor(config: AutomationManagerClientConfig);
execute(job: ExecutableJob): Promise<ExecuteResult>;
}
export declare class KeeperClientImpl implements KeeperClient {
constructor(config: KeeperClientImplConfig);
execute(job: ExecutableJob): Promise<ExecuteResult>;
}
export declare class Keeper {
constructor(config: KeeperConfig);
start(): void;
stop(): void;
}
export declare class GeckoTerminalPriceSource implements PriceSource {
constructor(options: GeckoTerminalPriceSourceOptions);
getSpotPrice(tokenIn: string, tokenOut: string): Promise<bigint>;
getHistoricalPrice(tokenIn: string, tokenOut: string, timestamp: number): Promise<bigint>;
}
export declare class SwappiPriceSource implements PriceSource {
constructor(options: SwappiPriceSourceOptions);
getSpotPrice(tokenIn: string, tokenOut: string): Promise<bigint>;
getHistoricalPrice(tokenIn: string, tokenOut: string, timestamp: number): Promise<bigint>;
}
export declare class MemoryJobRepository implements JobRepository {
add(job: ExecutableJob): Promise<void>;
get(id: string): Promise<ExecutableJob | null>;
list(filter?: JobListFilter): Promise<ExecutableJob[]>;
update(id: string, update: JobUpdate): Promise<void>;
}
export declare class MemoryExecutionRepository implements ExecutionRepository {
add(record: NewExecutionRecord): Promise<void>;
listByJob(jobId: string): Promise<ExecutionRecord[]>;
}Tier
Tier 2 — domains — May import Tier 0 and Tier 1 packages.
API Reference
.
Usage
import { Keeper, DCAStrategy } from '@cfxdevkit/automation';
const keeper = new Keeper(config);
await keeper.run();// Package name identifier for runtime introspection
export declare const __packageName: "@cfxdevkit/automation";
// Scaling factor used for price calculations
export declare const PRICE_SCALE: bigint;
// Default buffer in seconds for DCA execution timing
export declare const DEFAULT_DCA_DUE_BUFFER_SECONDS = 15;
// Mapping of automation manager addresses across networks
export declare const AUTOMATION_MANAGER_ADDRESSES: {
// ABI for the automation manager contract
export declare const AUTOMATION_MANAGER_ABI: readonly [
// Default safety configuration for automation execution
export declare const DEFAULT_SAFETY_CONFIG: SafetyConfig;
// ABI for the Swappi router contract
export declare const SWAPPI_ROUTER_ABI: readonly [
// Mapping of Swappi related addresses
export declare const SWAPPI_ADDRESSES: {
// Interface for providing token price data
export interface PriceSource {
// Result of a limit order condition check
export interface LimitOrderCheckResult {
// Result of a DCA timing check
export interface DCACheckResult {
// Interface for the keeper client implementation
export interface KeeperClient {
// Available actions for the automation manager
export interface AutomationManagerActions {
// Configuration for the automation manager client
export interface AutomationManagerClientConfig {
// Configuration for the keeper client implementation
export interface KeeperClientImplConfig {
// Configuration for the keeper instance
export interface KeeperConfig {
// Dependencies required by the keeper
export interface KeeperDeps {
// Options for the GeckoTerminal price source
export interface GeckoTerminalPriceSourceOptions {
// Interface for reading quotes from Swappi
export interface SwappiQuoteReader {
// Options for the Swappi price source
export interface SwappiPriceSourceOptions {
// Filter criteria for retrieving job lists
export interface JobListFilter {
// Represents an update to a job
export interface JobUpdate {
// Interface for job persistence and retrieval
export interface JobRepository {
// Record of a single job execution
export interface ExecutionRecord {
// Data required to create a new execution record
export interface NewExecutionRecord {
// Interface for execution history persistence
export interface ExecutionRepository {
// Options for the retry queue mechanism
export interface RetryQueueOptions {
// Configuration for safety constraints
export interface SafetyConfig {
// Context provided during safety evaluations
export interface SafetyContext {
// Details of a safety constraint violation
export interface SafetyViolation {
// Result of a safety check evaluation
export interface SafetyCheckResult {
// Parameters for a limit order strategy
export interface LimitOrderStrategy {
// Parameters for a DCA strategy
export interface DCAStrategy {
// Parameters for a TWAP strategy
export interface TWAPStrategy {
// Parameters for a swap strategy
export interface SwapStrategy {
// Context for strategy evaluation
export interface StrategyEvalContext {
// Interface for evaluating if a job should be executed
export interface StrategyEvaluator<TJob extends ExecutableJob = ExecutableJob> {
// Input for building swap calldata
export interface BuildSwapCalldataInput {
// Interface for the swap executor client
export interface SwapExecutorClient {
// Options for the swap executor
export interface SwapExecutorOptions {
// Addresses for various swap routers
export interface SwapRouterAddresses {
// Input for requesting a swap quote
export interface SwapQuoteInput {
// Input for executing a swap
export interface SwapExecuteInput extends SwapQuoteInput {
// Result of a swap execution
export interface SwapExecuteResult {
// Log data extracted from a swap receipt
export interface SwapReceiptLog {
// Receipt containing details of a swap transaction
export interface SwapReceipt {
// Common parameters for all job types
export interface BaseJobParams {
// Parameters specific to limit order jobs
export interface LimitOrderParams extends BaseJobParams {
// Parameters specific to DCA jobs
export interface DCAParams extends BaseJobParams {
// Parameters specific to TWAP jobs
export interface TWAPParams extends BaseJobParams {
// Parameters specific to swap jobs
export interface SwapParams extends BaseJobParams {
// Base structure for a job
export interface BaseJob<TType extends JobType, TParams> {
// Result of a job execution attempt
export interface ExecuteResult {
// Result of a single strategy tick
export interface TickResult {
// Result of a strategy evaluation
export interface EvalResult {
// Class for checking price conditions
export declare class PriceChecker {
// Client for interacting with the automation manager
export declare class AutomationManagerClient implements KeeperClient {
// Implementation of the keeper client
export declare class KeeperClientImpl implements KeeperClient {
// Main class for managing and running automation jobs
export declare class Keeper {
// Price source implementation using GeckoTerminal
export declare class GeckoTerminalPriceSource implements PriceSource {
// Price source implementation using Swappi
export declare class SwappiPriceSource implements PriceSource {
// In-memory implementation of the job repository
export declare const MemoryJobRepository implements JobRepository {
// In-memory implementation of the execution repository
export declare const MemoryExecutionRepository implements ExecutionRepository {
// Queue for managing job retries
export declare class RetryQueue {
// Class for enforcing safety rules during execution
export declare class SafetyGuard {
// Evaluator for DCA strategy jobs
export declare class DCAEvaluator implements StrategyEvaluator<DCAJob> {
// Evaluator for limit order strategy jobs
export declare class LimitOrderEvaluator implements StrategyEvaluator<LimitOrderJob> {
// Evaluator for swap strategy jobs
export declare class SwapEvaluator implements StrategyEvaluator<SwapJob> {
// Evaluator for TWAP strategy jobs
export declare class TWAPEvaluator implements StrategyEvaluator<TWAPJob> {
// Class responsible for executing swap transactions
export declare class SwapExecutor {
// Estimates the USD value of an amount
export declare function estimateUsdValue(amount: bigint, price: bigint, tokenDecimals?: number): number;
// Checks if a DCA job is due for execution
export declare function isDCADue(params: DCAParams, nowSec: number, bufferSec?: number): boolean;
// Checks if a job has expired
export declare function isExpired(job: Job, nowSec: number): boolean;
// Converts a decimal number to a scaled bigint
export declare function decimalToScaled(value: number, decimals?: number): bigint;
// Builds calldata for a swap transaction
export declare function buildSwapCalldata(input: BuildSwapCalldataInput): Hex;
// Decodes the amount out from a swap receipt
export declare function decodeAmountOut(receipt: SwapReceipt, recipient: HexAddress): bigint | undefined;
// Returns the transfer event topic hash
export declare function transferTopic(): Hex;
// Type guard to check if a job is executable
export declare function isExecutableJob(job: Job): job is ExecutableJob;
// Type for creating a new job from partial data
export type NewJobInput = Omit<Job, 'id' | 'status' | 'createdAt' | 'updatedAt' | 'retries'> & {
// Union of all available strategy types
export type Strategy = LimitOrderStrategy | DCAStrategy | TWAPStrategy | SwapStrategy;
// Hexadecimal address type
export type HexAddress = HexAddress;
// Hexadecimal hash type
export type HexHash = HexAddress;
// Union of valid job type strings
export type JobType = 'limit_order' | 'dca' | 'twap' | 'swap';
// Union of valid job status strings
export type JobStatus = 'pending' | 'active' | 'executed' | 'cancelled' | 'failed' | 'expired' | 'paused';
// Comparison direction for triggers
export type TriggerDirection = 'gte' | 'lte';
// Type for limit order jobs
export declare type LimitOrderJob = BaseJob<'limit_order', LimitOrderParams>;
// Type for DCA jobs
export declare type DCAJob = BaseJob<'dca', DCAParams>;
// Type for TWAP jobs
export declare type TWAPJob = BaseJob<'twap', TWAPParams>;
// Type for swap jobs
export declare type SwapJob = BaseJob<'swap', SwapParams>;
// Union of all job types
export type Job = LimitOrderJob | DCAJob | TWAPJob | SwapJob;
// Union of jobs that are eligible for execution
export type ExecutableJob = LimitOrderJob | DCAJob | TWAPJob | SwapJob;
// Statuses as they appear on-chain
export type OnChainJobStatus = 'active' | 'executed' | 'cancelled' | 'expired';./db
Usage
import { createSqliteDb, DrizzleJobRepository } from '@cfxdevkit/automation/db';
const db = createSqliteDb('automation.db');
const jobRepo = new DrizzleJobRepository(db);// Options for the Drizzle execution repository
export { DrizzleExecutionRepositoryOptions }
// Drizzle-based implementation of the execution repository
export { DrizzleExecutionRepository }
// Options for the Drizzle job repository
export { DrizzleJobRepositoryOptions }
// Drizzle-based implementation of the job repository
export { DrizzleJobRepository }
// SQLite database instance for automation
export declare const AutomationSqlite
// Helper to create a SQLite database connection
export { createSqliteDb }
// Helper to initialize the SQLite database schema
export { initializeSqliteSchema }
// Drizzle table definition for jobs
export declare const jobs: import('drizzle-orm/sqlite-core').SQLiteTableWithColumns<{
// Drizzle table definition for executions
export declare const executions: import('drizzle-orm/sqlite-core').SQLiteTableWithColumns<{
// Drizzle table definition for worker heartbeats
export declare const workerHeartbeat: import('drizzle-orm/sqlite-core').SQLiteTableWithColumns<{
// Drizzle table definition for settings
export declare const settings: import('drizzle-orm/sqlite-core').SQLiteTableWithColumns<{Last updated on