Core Concepts
Understanding the fundamental concepts of DCA.fun protocol
What is DCA.fun?
DCA.fun enables users to automate their token purchases over time using a unique isolated vault system. Unlike traditional DCA protocols, each order operates in its own secure vault, providing enhanced security and capital efficiency.
Dollar Cost Averaging (DCA)
DCA is an investment strategy where you purchase a fixed dollar amount of an asset at regular intervals, regardless of price. This strategy helps reduce the impact of volatility and eliminates the need to time the market.
Key Features
- Isolated Vault Architecture: Each order has its own dedicated vault
- Yield Generation: Earn Aave yield on idle funds
- Decentralized Execution: Anyone can fill orders and earn rewards through scaling incentives
- Price Protection: Chainlink Data Streams ensure accurate real-time pricing
- Flexible Configuration: Customize frequency, amounts, slippage, and duration
- Order Quote API: Quote fills before execution with our serverless API
How DCA.fun Works
User Creates Order → Vault Deployed → Funds Deposited → Time Passes → Earn Yield (optional)
Filler quotes (API) → Filler Executes → Tokens Swapped → Recipient Receives → RepeatKey Participants
Order Creator
The wallet that initiates a DCA order:
- Defines order parameters
- Deposits funds
- Earns yield on deposited funds (optional)
- Can cancel anytime with no penalties
- Owns the vault contract
Order Recipient
The address that receives output tokens:
- Is the benefecial owner of the proceeds in the vault
- Can be different from the order creator
- Can withdraw accumulated tokens anytime
Order Filler
Direct Execution
Anyone who fills an order directly from their wallet:
- Monitors orders for profitable opportunities
- Requests quotes via the API to get exact amounts
- Must have pre-approved Permit2 for token transfers
- Provides output tokens and receives input tokens atomically
Callback Execution (Atomic Routing)
Anyone using a router contract with fillOrderCallback():
- Deploys a router contract implementing
IFillOrderCallbackinterface - Router receives input tokens first via the callback
- Router must provide output tokens to complete the fill
- Enables flash loan strategies and DEX aggregation
- Router handles all token approvals and transfers
Callback Execution Flow
1. Filler calls: fillOrder(data, router, recipient, callbackData)
2. DcaDotFun transfers tokenIn to recipient
3. DcaDotFun calls: router.fillOrderCallback(callbackData)
4. Router executes swap logic (DEX, flash loan, etc.)
5. Router ensures tokenOut is available
6. DcaDotFun pulls tokenOut via Permit2
7. Transaction completes or reverts entirelySecurity Notice: As identified in our Nethermind audit, leftover Permit2 allowances in callback contracts can potentially be exploited by malicious fillers.
Risk: If a router contract maintains persistent Permit2 allowances for tokens, a malicious filler could craft callback data to drain those allowances.
Mitigation: Router implementations should: • Reset token allowances to 0 after each fill • Use exact approval amounts rather than max approvals • Implement access controls on the callback function • Consider using separate routers per token pair
Token Definitions
TokenIn (Input Token)
The token you're selling/spending in your DCA strategy:
- What: The token you deposit when creating the order (e.g., USDC)
- When: Supplied upfront at order creation for all executions
- Who receives: Fillers receive TokenIn when they execute your order
- Example: If DCAing from USDC → ETH, USDC is your TokenIn
TokenOut (Output Token)
The token you're buying/accumulating through DCA:
- What: The token you want to acquire over time (e.g., ETH)
- When: Provided by fillers at each execution interval
- Who receives: You (the order recipient) accumulate TokenOut in your vault
- Example: If DCAing from USDC → ETH, ETH is your TokenOut
Vault Isolation Model
Each order deploys a dedicated vault using the Clone pattern for gas efficiency:
Order #1 → Vault #1 (Deterministic address from orderId)
Order #2 → Vault #2 (Clone of vault implementation)
Order #3 → Vault #3 (CREATE2 with orderId as salt)Technical Implementation
- Factory Pattern:
DcaVaultFactorydeploys minimal proxy clones for gas efficiency - Deterministic Addresses: Vaults use orderId as CREATE2 salt for predictable addresses
- Ownership: Order creator owns the vault contract via Ownable pattern
- Initialization: Each vault stores order-specific parameters on deployment
- Gas Optimization: Clone pattern reduces deployment costs by ~95%
Security Benefits
- Complete Isolation: No cross-contamination between orders
- Direct Ownership: Users control their vault via Ownable pattern
- Transparent State: All funds and yields clearly attributed
- Upgrade Safety: Individual vaults are immutable clones
The Scaling Factor Mechanism
DCA.fun uses a linear scaling mechanism to incentivize timely order execution:
*assume: 100bp (1%) slippage with a 60 second scaling interval
Time Since Executable → Asset Ratio Adjustment → Filler Incentive
0 seconds → tokenIn valued +1% above market → Worst for filler (user pays premium)
30 seconds → tokenIn at market value → Fair market execution
60 seconds → tokenIn valued -1% below market → Best for filler (max profit)Technical Implementation
The scaling factor adjusts the input token price linearly over time:
adjustedTokenInPrice = tokenInPrice * (BASIS_POINTS + slippage - scalingFactor) / BASIS_POINTSWhere scalingFactor increases from 0 to 2 * slippage over the scaling interval:
- At t=0: scalingFactor = 0, so price = tokenInPrice × (1 + slippage%)
- At t=interval/2: scalingFactor = slippage, so price = tokenInPrice (market)
- At t=interval: scalingFactor = 2 × slippage, so price = tokenInPrice × (1 - slippage%)
This creates a Dutch auction effect:
- Orders start expensive for fillers (protecting users from MEV)
- Price gradually improves for fillers over time
- Competition ensures fills occur near fair market value
Price Feed Architecture
Chainlink Data Streams Integration
// Price Verification Flow
1. Filler obtains unverified price reports from DCA API
2. Reports include cryptographic proofs and timestamps
3. VerifierDotFun validates signatures via VerifierProxy
4. Protocol enforces maximum price feed age (staleness check)
5. Protocol enforces timestamp tolerance between token pairs
6. Validated prices determine exact swap amountsPrice Feed Security
- Data Streams: Uses Chainlink's low-latency price feeds (not standard oracles)
- Cryptographic Verification: All prices are cryptographically signed
- Freshness Checks: Configurable max age for price data (e.g., 5 seconds)
- Timestamp Tolerance: Max difference between tokenIn and tokenOut reports
- Feed ID Validation: Ensures correct price feeds are used for each token
Fee Structure
DCA.fun implements a transparent fee model:
Protocol Fee
- Charged on each fill from the output token amount
- Configurable up to 50 basis points (0.5%)
- Uses the lesser of: fee at order creation time vs current protocol fee (ensures users always get the best rate)
- Example: 100 USDC output with 30bp fee = 99.7 USDC to user, 0.3 USDC to protocol
Yield Fee (yieldSplit)
- Only charged on earned yield from staked assets
- Configurable percentage of yield earned
- Fee percentage is locked in at order creation (never changes, ensuring deterministic expectations)
- Principal is never touched
- Applied only at withdrawal/cancellation
Fee Distribution
- All fees sent to configurable
feeCollectoraddress - Fees help maintain infrastructure and development
- No hidden fees or additional charges
Order Parameters
Required Parameters
- tokenIn: Input token address (use address(0) for ETH)
- tokenOut: Output token to receive
- amountIn: Amount per execution
- repeats: Number of times to execute the order
- frequencyInterval: Time between executions (seconds)
- slippage: Maximum acceptable slippage in basis points (e.g., 100 = 1%)
- scalingInterval: Time window for price scaling (seconds)
- recipient: Address to receive output tokens
Optional Parameters
- stakingEnabled: Whether to stake in Aave for yield
- startTime: When the order becomes first executable (default: now)
Order Lifecycle
Order Creation
- User specifies parameters (tokens, amounts, frequency, etc.)
- Protocol validation
- Vault is deployed with deterministic address
- Funds transferred to vault (or staked in Aave if enabled)
- Order becomes fillable after first execution time
Order Execution
- Order becomes executable at
lastRun + frequencyInterval - Fillers monitor for profitable opportunities
- Filler requests quote from API with current prices
- Filler executes with quote data and Chainlink reports
repeatscounter decrements,lastRunupdates- Output tokens accumulate in vault for recipient
Order Completion
- Automatic: When
repeatsreaches 0 after final fill - Manual: Creator can cancel anytime, receiving remaining funds
- Recipient can withdraw accumulated output tokens anytime
Order States
- Active:
repeats > 0and not cancelled - Executable: Current time ≥
lastRun + frequencyInterval - Cancelled: Creator has cancelled, vault enters withdrawal-only mode
- Completed: All repeats executed or order cancelled
Access Control & Security
Roles
- DEFAULT_ADMIN_ROLE: Protocol configuration and management
- PAUSER_ROLE: Can pause create/fill operations independently
- Order Creator: Can cancel orders and owns their vault
- Order Recipient: Can withdraw accumulated output tokens
Security Features
- Reentrancy Protection: All external functions use
nonReentrantmodifier - Pause Mechanism: Create and fill operations can be independently paused
- Order Validation: Multiple checks prevent invalid order creation
- Price Feed Validation: Extensive validation of Chainlink reports
Implementation Details
- ETH sent with transaction is automatically handled
- No separate approval step needed for ETH
- WETH unwrapping handled automatically for recipients if configured
- Gas efficient compared to manual WETH wrapping
Yield Generation
When staking is enabled for a token:
- Tokens are supplied to Aave V3 lending pool
- Vault receives corresponding aTokens (rebasing)
- Yield accrues automatically through aToken balance increases
- Protocol fee is taken only on earned yield at withdrawal
- Users keep 100% of principal plus remaining yield
Technical Details
- Supply Caps: Respects Aave's per-asset supply limits
- Reserve Validation: Checks token is active Aave reserve
- Withdrawal Logic: Handles both aToken and underlying transfers
- Yield Calculation:
(aTokenBalance - originalDeposit) * yieldSplit / 10000
Yield Distribution Example
Initial Deposit: 1,000 USDC → 1,000 aUSDC
After 6 months: 1,025 aUSDC (2.5% APY)
Yield Earned: 25 USDC
Protocol Fee (10% of yield): 2.5 USDC
User Receives: 1,022.5 USDC (principal + 90% of yield)
Note: Yield fee is only taken when order completes or is cancelled