Security

The short version: your wallet owns your Safe, and the bot's key can only swap the token pairs you allowed, back into that same Safe. Everything below is enforced by contracts, so you don't have to trust our server — and you can check it yourself.

Who holds which key

  • Your wallet — the only owner of your Safe and of its validator. Needed for withdrawals, adding a pair, upgrading or revoking the bot.
  • The bot's session key (kept encrypted on our server) — signs trades only inside ERC-7579 Smart Sessions, where on-chain policies decide what is allowed.
  • Our server deploys your Safe and sets up the first sessions, then, in the next transaction, hands the validator to your wallet and removes its own key.

What the bot's session can do

One session per swap direction you allowed (for example USDC → WETH and WETH → USDC). Each one may only:

  • approve the Uniswap V3 router to spend the token it sells;
  • call exactInputSingle with exactly that tokenIn, exactly that tokenOut, and your Safe as the recipient;
  • transfer the platform fee, in the token it sells, to our fee address.

No ETH can be sent by any call. Transfers to any other address, approvals to any other spender, swaps into any other token or to any other recipient, and every validator / Safe-settings call are rejected when the operation is validated — before anything executes.

Threat model

  • Our server is fully compromised. The attacker gets the session key. They still cannot withdraw your funds, change owners, or swap into a token of their choosing. What remains: they could make unfavourable swaps of your allowed pairs inside your Safe (price impact / MEV) — the minimum-output check on each swap is computed by our server, so it would not stop them — and spend some of the Safe's gas balance. Revoke the session and the key is useless. One more honest caveat: the fee transfer is pinned to our fee wallet but its amount is computed by our server, so a compromised server could move more than the fee there. That wallet's key is not on the server — the money would land with us, not the attacker, and we would return it. A per-transfer cap on-chain is planned.
  • dcabot.app is down or gone. Your Safe keeps working without us: open it in Safe{Wallet} and withdraw.
  • Phishing. We never ask for a seed phrase. Every wallet signature in the app either signs you in (bound to dcabot.app) or authorizes one specific Safe operation.
  • Stale or manipulated prices. The bot refuses to trade on Chainlink prices that are stale, non-positive, or while an L2 sequencer is down; swaps carry a quote-based minimum output computed by our server.

Contracts

Same addresses on Arbitrum, Base and Ethereum (CREATE2 deployments by their authors; bytecode checked identical on all three).

Verify it yourself

  • Who owns your validator: call getOwners(yourSafe) on the OwnableValidator — it must return only your wallet.
  • Which sessions exist: the SmartSession contract emits SessionCreated(permissionId, account) for every session on your Safe; the Smart Account page lists your allowed pairs.
  • What a session may call: isActionIdEnabled(yourSafe, permissionId, keccak256(target ‖ selector)) on SmartSession — e.g. only approve on the token that session sells.
  • Every trade is a public transaction linked from your order history.
  • All of the above in one command: our open-source verifier — coming soon (MIT, read-only, needs only an RPC URL and your Safe address) prints your Safe's owners, modules, validator owners and every bot session with its decoded rules, and flags anything that isn't an approve / pinned swap / fee transfer.

Revoke

Smart Account page → Revoke — one wallet signature removes every bot session on that Safe. Without the app, any Safe{Wallet}-compatible tool can call removeSession(permissionId) on SmartSession from your Safe. Safes created before the October 2026 permission upgrade need a one-time Upgrade security click first; the app shows the button on any Safe that still needs it.

Incident log

Newest first. We record every security issue that affected, or could have affected, the bot's permissions.

  1. Our "non-custodial" claim was not accurate; fixed the same day

    What was wrong. An internal audit found that our earlier "non-custodial" claim was not accurate. The server's key could control Safes through a broad session that allowed transfers and approvals to any address, and on some Safes it was also the owner of the validator, the module that authorises everything else. Anyone who took over our server could have moved funds out of those Safes.

    What we changed the same day. Sessions were narrowed to pinned pairs: one session per swap direction, which may only approve the Uniswap router, swap that exact pair back into the same Safe, and pay the fee to our fee wallet. New Safes now hand the validator to the user's own wallet automatically in a second transaction right after deployment, which also removes the server's key; if that step can't run yet (not enough ETH for gas), we retry every 15 minutes and the app tells you.

    Safes created before 2026-10-11. Where our server still held the validator and the Safe had gas, we migrated it ourselves; the user did nothing. Every other existing Safe gets a one-click Upgrade security on the Smart Account page (one wallet signature); the app shows that button on every Safe that still needs it. A few empty test Safes are still pending.

    Impact. No external user funds were affected. At the time the platform had no external users with funds in their Safes.

Audits and reporting a vulnerability

Audits: Safe and Rhinestone's modules (Safe7579, Smart Sessions, OwnableValidator) have third-party audits commissioned by their authors, as do Uniswap V3 and the ERC-4337 EntryPoint. DCA Bot's own code has not been audited yet. It has had an internal security review (October 2026; see the incident log above). The rules that limit the bot are on-chain, so you can check them yourself with the open-source verifier — coming soon.

Found a problem? Please report it privately via Telegram @defi_dca_bot (write “security”) before disclosing it publicly. We reply within 72 hours and credit reporters who want it.