Skip to content

Instantly share code, notes, and snippets.

@al-maisan
Created December 17, 2025 06:20
Show Gist options
  • Select an option

  • Save al-maisan/427867eccf82b7bca0fa150c95280bc9 to your computer and use it in GitHub Desktop.

Select an option

Save al-maisan/427867eccf82b7bca0fa150c95280bc9 to your computer and use it in GitHub Desktop.
DCA as a service

DCA-as-a-Service on Solana (Non-Custodial) — Proposal

Goals

  • Let users create recurring buy campaigns (DCA) without sharing private keys or giving up custody.
  • Execute swaps via Jupiter across top Solana liquidity venues.
  • Keep backend minimal while ensuring reliability via monitoring + retries.

Key Assumptions

  • Pattern A (Delegate Allowance): user grants a capped SPL-token delegate allowance for the campaign input (USDC/WSOL).

  • Swaps use Jupiter routing.

  • Bought assets are sent to the user’s ATA for the output mint.

  • DCA executions are monitored and retried up to max_retries attempts (configurable).

  • Detect and react to:

    • delegate overwritten
    • allowance exhausted
    • insufficient funds (user moved funds away)

High-Level Architecture

On-chain program

  • Stores campaign state + enforces rules (mints, cadence, per-step amount, destination ATA, etc.).
  • Validates readiness (due, active, delegate/allowance/funds checks).
  • Performs per-step accounting and emits events for analytics.

Automation

  • TukTuk task(s) trigger execute_step(campaign) on schedule (daily).
  • Each step is idempotent and safe to retry.

Minimal backend

  • Observability + reliability layer:

    • monitors due steps, tx outcomes, and campaign health
    • retries failed steps up to max_retries
    • builds/refreshes Jupiter quote/route if needed
  • Indexes on-chain events + tx memos to power user metrics.

Frontend

  • Campaign creation/start/cancel
  • Progress + “gamified” stats dashboard (streaks, avg execution price, best route labels, total DCA’d, etc.)

On-Chain State (Minimal)

Campaign account

  • owner (user wallet)
  • input_mint (USDC or WSOL)
  • output_mint (e.g., xavier)
  • funding_token_account (user-owned token account; delegate set to program/campaign PDA)
  • destination_ata (user-owned ATA for output_mint)
  • start_ts, cadence_secs (e.g., 86400), steps_total, steps_executed
  • per_step_amount (or derived from total / days)
  • allowance_expected_total (for sanity checks)
  • status: Active | Paused | Cancelled | Completed | Error(code)
  • next_due_ts
  • retry_count_current_step (optional; can be off-chain only)

Events

  • CampaignStarted
  • StepExecuted (in_amount, out_amount, route info hash/label, ts)
  • StepFailed (error code, ts)
  • CampaignCancelled
  • CampaignPaused (reason: delegate_overwritten / allowance_exhausted / insufficient_funds)

Use Case 1 — User Starts a DCA Campaign

UX Flow

  1. User selects:

    • input token (USDC/WSOL), total amount, duration (days), output token
    • optional settings: slippage, max_retries, “catch-up” behavior
  2. Frontend prepares:

    • destination ATA = ATA(user, output_mint) (create if missing; payer = executor/backend)
    • a dedicated funding token account per campaign (recommended to avoid delegate conflicts)

On-Chain Actions

  • create_campaign(...) initializes the campaign state.

  • User performs ApproveChecked:

    • delegate = campaign/program PDA
    • allowance = total campaign amount
  • TukTuk task is registered to call execute_step(campaign) daily.

Execution Step (execute_step)

  • Validates campaign is Active and now >= next_due_ts.
  • Validates delegate/allowance/funds (see “Detection”).
  • Pulls today’s spend from user funding token account via delegate.
  • Swaps via Jupiter.
  • Sends output to destination_ata (user ATA).
  • Updates steps_executed, next_due_ts, status transitions.

Use Case 2 — User Cancels a DCA Campaign

UX Flow

  • User clicks “Cancel”.

On-Chain Actions

  • cancel_campaign(campaign):

    • sets status to Cancelled
    • prevents further steps from executing

Allowance Revocation

  • Recommended: user also calls Revoke on their funding token account delegate (or sets allowance to 0).
  • TukTuk task should be disabled/removed (or it can keep calling and get a clean “Cancelled” failure code).

Use Case 3 — User Sees Campaign Progress / Status / Metrics

Data Sources

  • Campaign account state: authoritative status + counters + next due time.
  • Program events: step-by-step metrics (amounts, timestamps, failures).
  • Transaction memos (optional): searchable tags like dca:v1 campaign=<PDA> step=<n>.

UI Metrics (Examples)

  • Progress: steps_executed / steps_total, next run time, remaining budget (derived)
  • Execution stats: avg buy price (computed), total acquired, best/worst day, slippage realized
  • Reliability: success rate, retry counts, last error reason, current “streak”
  • Route stats: most-used route labels (from Jupiter quote metadata or backend logs)

Monitoring & Retry (max_retries)

Backend/TukTuk operator logic

  • For each due step:

    • submit tx
    • if dropped/expired: resubmit (same idempotent call)
    • if swap failed: re-quote via Jupiter and retry up to max_retries
  • After max_retries failures for the same step:

    • mark campaign Paused (or Error) with reason code
    • notify user in UI

Idempotency rule: a step can be executed at most once for a given step_index.


Detection & Automated Responses

1) Delegate got overwritten

Detect

  • Read funding token account state:

    • delegate != expected_campaign_pda OR delegated_amount == 0 Response
  • Set campaign Paused(delegate_overwritten)

  • UI prompts user to re-approve delegate allowance.

2) Allowance ran out

Detect

  • delegated_amount < required_step_amount (or < remaining required) Response
  • Set campaign Paused(allowance_exhausted)
  • UI prompts user to top-up allowance (approve additional) or end campaign.

3) User moved funds away

Detect

  • funding token account balance < required_step_amount Response
  • Set campaign Paused(insufficient_funds)
  • UI prompts user to refill funding account.

Security & Trust Boundaries

  • Program enforces:

    • fixed owner, mints, cadence, destination ATA, and max spend per step
    • no executor can redirect output away from the user ATA
  • Users retain custody:

    • funds remain in user token account until a due step executes under capped allowance
    • user can revoke anytime by removing delegate allowance
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment