rotki

rotki.com
rotki

A self-hosted, opensource, privacy-first portfolio tracking and accounting tool for crypto and more. Support for all 3 major Operating systems and docker. The tool focus is on data self-sovereignty.

Crypto Payments Accepted Open Source

rotki Source Code

Author

rotki

Description

A portfolio tracking, analytics, accounting and management application that protects your privacy

#accounting#analytics#bitcoin#blockchain#cryptocurrencies#cryptocurrency-exchanges#cryptocurrency-portfolio#cryptocurrency-portfolio-tracking#ethereum#hacktoberfest#management#management-system#portfolio-tracker#tracking

Homepage

https://rotki.com

License

AGPL-3.0

Created

05 Mar 18

Last Updated

07 Jul 26

Latest version

v1.43.2

Primary Language

Python

Size

505,966 KB

Stars

3,939

Forks

740

Watchers

3,939

Language Usage

Language Usage

Star History

Star History

Top Contributors

Recent Commits

  • Konstantinos Paparas (06 Jul 26)

    test: add e2e coverage for remaining settings subpages Cover the settings subpages and external services that had no e2e coverage: - interface: sections render + animations toggle inline-save - modules: toggle a module and verify it persists after re-login - oracle: sections render + penalty duration inline-save and persistence - database: every section renders (non-destructive) - api-keys/external: lists providers + mocked etherscan key save Adds a shared openSettingsTab helper and two data-testid hooks (animations switch, oracle penalty duration input) for stable selectors.

  • Konstantinos Paparas (06 Jul 26)

    test: add e2e coverage for the airdrops page Cover the /airdrops route with a Playwright suite: source listing, the status filter (claimed/unclaimed/missed/unknown), the unknown info alert, manual refresh, and POAP delivery expansion. The page object mocks the async airdrops task pipeline at the network layer with a deterministic dataset, merging the completed task id into the real /tasks response so unrelated background tasks keep resolving. Adds data-testid hooks to the page for stable selectors.

  • Konstantinos Paparas (06 Jul 26)

    fix: drop duplicate nginx Host header for uvicorn Each proxied REST/colibri location set `proxy_set_header Host` twice ($host and $http_host), so nginx forwarded two Host headers. The old gevent WSGI server tolerated that; the uvicorn server the gevent-removal flip switched to rejects it with "Invalid HTTP request received." (400), breaking every /api and /colibri request in the Docker deployment. Keep a single Host ($host, matching the /ws location).

  • Yรกbir Benchakhtir (06 Jul 26)

    Merge pull request #12542 from yabirgb/wsmessages Fix ws messages in asyncio

  • Yabir Benchakhtir (06 Jul 26)

    Fix ws messages in asyncio

  • Yabir Benchakhtir (03 Jul 26)

    Add more tools to mcp - adds tool to query assets info - adds tool to query cached prices

  • Konstantinos Paparas (06 Jul 26)

    settings: add useSetting read primitive and facade layer Introduce useSetting(key), a typed per-key read accessor that routes a logical setting key to its owning store via an explicit table and returns a readonly getter ref. Callers no longer import a settings store to read one value; the routing table is the single place that knows where a setting lives, so a later backing swap changes one spot. Layer domain facades on top of it: rebuild useAmountDisplaySettings to read every entry through useSetting (no store import, no storeToRefs), and migrate DateDisplay, a leaf reading a single key, to a direct useSetting call. The routing table starts with the keys these two consumers need and grows as more migrate. use-setting.spec.ts asserts every mapped key is exposed by its store and that the returned ref tracks store updates.

  • Lefteris Karapetsas (06 Jul 26)

    fix: test failures from gevent-to-threads move - Remove 'threading' from freezegun's ignore list so task threads see the frozen clock (greenlets had detached stacks, threads do not) -- fixes the VCR cassette misses in time-frozen tests - Serialize vcrpy's global force_reset unpatch window against connection checkout with an RLock: a background task could race into another thread's unpatched window and hit the real network mid-cassette - Make savepoint tests assert before the write tx/savepoint releases, since the blocked task may be scheduled at any moment afterwards - Keep liquity proxy patches active while the async task runs - Allow either refresh order in the concurrent balance-queries test

  • Lefteris Karapetsas (06 Jul 26)

    fix: logging f-string lint and docs build setuptools dep

  • Lefteris Karapetsas (05 Jul 26)

    docs: spin harvest follow-ups out of the migration The interpreter upgrade (one hop 3.11 -> 3.13), the GIL-convoy measurements, the native-async hot-path conversions and the free-threading experiments are ordinary roadmap items that the gevent removal enables but does not own. What still closes the migration is the phase-6 release validation: benchmark comparison, 3-platform manual QA and a nightly release cycle.

  • Lefteris Karapetsas (05 Jul 26)

    chore: remove transitional dual-mode driver code Phase 7 (harvest) of the gevent to asyncio migration (docs/designs/gevent_to_asyncio.md), which also absorbs the retired phase 0 as a future single interpreter hop to 3.13. - The SchedulingMode enum, the GEVENT branch of the sqlite progress callback (the yield machinery), the runtime progress-handler toggling in critical_section and the scheduling_mode constructor parameter are gone: the callback is now purely a cancellation checkpoint. The db_scheduling_mode test fixture goes with them. - The driver module is renamed to rotkehlchen/db/drivers/sqlite.py -- nothing about it is gevent anymore. - run_in_native_thread() is inlined away: since the flip the caller already runs on a real OS thread.

  • Lefteris Karapetsas (05 Jul 26)

    chore: the flip -- remove gevent Phase 6 of the gevent to asyncio migration (docs/designs/gevent_to_asyncio.md): monkeypatching and gevent are gone. - monkey.patch_all() removed from all entrypoints; pytestgeventwrapper.py and its urllib3 hack deleted -- plain pytest runs the suite (Makefile, CI, docs updated; api-asgi CI leg folded into the now uvicorn-only api leg). - The gevent WSGI server and geventwebsocket are deleted: uvicorn serves REST (WSGI bridge) and native /ws as the only backend, its event loop on a dedicated thread; --api-server-backend is gone. Websocket keepalive pings are disabled for parity with the old server, which never pinged (a late pong must not drop a client). server.py uses stdlib signal handling. - DB driver defaults to THREADING scheduling. Real preemption exposed two driver bugs the cooperative dual-mode tests could not see: a GIL/db-mutex deadlock (pysqlite takes sqlite's connection mutex while holding the GIL, while another thread's in-flight statement holds the mutex with its progress callback waiting for the GIL) -- fixed with a per-connection statement lock around all sqlite C entry points and by not toggling the progress handler at runtime; and wal_checkpoint hitting 'database table is locked' whenever a concurrent reader's statement is active -- now retried within a deadline and skipped with a warning (checkpoints are opportunistic). - gevent, greenlet, gevent-websocket and wsaccel are removed from the dependencies. The TID251 gevent import ban stays with an empty allowlist. The greenlet-switch profiler tooling is deleted. - All 32 gevent-importing test files converted to the concurrency seam / stdlib: joinall to wait(), gevent.Timeout watchdogs to monotonic deadline asserts, the websocket reader fixture unblocks recv() via socket close. Four latent test races that only passed under cooperative scheduling now wait on their actual conditions. - A GIL convoy effect on large row fetches next to CPU-bound threads is documented as a phase-7 measurement item (risk #6 in the doc).

  • Lefteris Karapetsas (05 Jul 26)

    chore: thread-backed task orchestration Phase 5 of the gevent to asyncio migration (docs/designs/gevent_to_asyncio.md): task orchestration moves off greenlets onto threads, which the still-active monkeypatching runs as cooperative greenlets today and become real threads at the flip. - rotkehlchen/concurrency gets a real Task handle: one daemon thread per task, capturing result/exception/exc_info, cancellation-token aware, with two-phase construction so done-callbacks cannot observe a half-initialized handle under preemption. spawn/spawn_later/wait now operate on Tasks; run_in_native_thread() covers premium sync's CPU-bound DB encryption (gevent hub threadpool until the flip). - GreenletManager becomes TaskSupervisor (rotkehlchen/tasks/), with the same tracking and exception-logging behavior via done-callbacks. greenlet_manager/api_task_greenlets/running_greenlets renamed. - Every gevent.killall is gone: logout and shutdown cancel all task tokens, wait one grace period and abandon survivors; a cancelled task that later dies (e.g. on the closed DB of a logged-out user) logs at debug instead of raising a user-facing error. - No bounded shared pool, deliberately: pooled parents that spawn-and-wait on queued children would deadlock, and capacity is already gated. Rationale recorded in the design doc; revisited in phase 7. - Log records are tagged with thread names instead of greenlet ids. - TID251 allowlist shrinks to the phase-6 entries only. - Tests: joinall/killall on task collections became seam wait() or cancellation; test_deadlock_logout now exercises cancellation while holding the global DB lock; latent bugs exposed by eager thread start fixed (never-called mock signature, caplog race, stale patch target).

  • Lefteris Karapetsas (02 Jul 26)

    chore: asgi api server behind a flag Phase 4 of the gevent to asyncio migration (docs/designs/gevent_to_asyncio.md): an asyncio (uvicorn) server can serve the API as a transitional opt-in, with the gevent server remaining the default until the flip. - New rotkehlchen/api/asgi.py: one uvicorn server on one port serves the unchanged Flask REST app through a2wsgi's WSGI bridge and a native websocket route at /ws, matching the gevent server layout so the frontend needs no changes. Verified that uvicorn's event loop cooperates with the active monkeypatching: it runs in a greenlet over gevent's selector and bridge workers are greenlet-threads, so request concurrency is preserved. - RotkiNotifier works unchanged in both modes: AsgiWebsocketSubscriber duck-types the websocket surface it uses, enqueueing messages onto a per-client asyncio queue via call_soon_threadsafe with a per-connection coroutine draining it. Send failures surface as the new WebsocketSendError. - Selection via --api-server-backend {gevent,asgi} (default gevent), wired through APIServer.start(); stop() handles both backends. - The test API server honors ROTKI_API_SERVER_BACKEND=asgi and CI got an api-asgi matrix leg running the api test group (websocket tests included) against the new server. - Dependencies: uvicorn and a2wsgi added, websockets promoted to a direct dependency as uvicorn's websocket protocol backend.

  • Lefteris Karapetsas (02 Jul 26)

    chore: DB driver dual-mode scheduling Phase 3 of the gevent to asyncio migration (docs/designs/gevent_to_asyncio.md): the DB driver no longer imports gevent and supports both scheduling models. - A SchedulingMode enum replaces gevent-specific machinery: in GEVENT mode the sqlite progress handler yields to other greenlets (time.sleep(0), monkeypatched); in THREADING mode it remains solely a cancellation checkpoint. Everything else is stdlib in both modes: threading.Lock for the connection locks (.locked() replaces .ready()) and threading.get_ident() for writer/savepoint identity. - The poll-a-field wait loops in write_ctx/_enter_savepoint were check-then-act races that only worked because savepoint statements never trigger a progress-handler yield. Write transactions and outermost savepoint stacks now claim the transaction lock through a cancellation-responsive acquire, released when the stack empties. Lock order is transaction slot before critical section everywhere. - GlobalDBHandler.clear_locks() no longer blindly over-releases the driver locks (bounded Lock raises, unlike the old Semaphore) and resets the savepoint bookkeeping instead. - Driver tests parametrized over both modes via a new db_scheduling_mode fixture, plus a new concurrency stress test mixing writers, rolling-back savepoint stacks and readers. - Atomicity audit for preemptive threads (findings in the design doc): the shared LRU cache primitives in utils/data_structures.py are now locked, and Inquirer/PriceHistorian set_oracles_order build their oracle lists locally instead of appending into lists concurrent price queries iterate.

  • Lefteris Karapetsas (02 Jul 26)

    chore: cooperative task cancellation Phase 2 of the gevent to asyncio migration (docs/designs/gevent_to_asyncio.md): replace greenlet.kill() with a cooperative CancellationToken raised as TaskCancelledError at defined checkpoints. - New rotkehlchen/concurrency/cancellation.py: CancellationToken, checkpoint(), cancellable_sleep(), run_cancellable(). The current task's token travels in a ContextVar and the seam's spawn functions propagate it to children, so cancelling a task cancels its tree. - Kill paths converted: delete_async_task and the evm address removal path (maybe_cancel_running_tx_query_tasks, which now cancels and waits a short grace period). GreenletKilledError removed. Logout/shutdown keep killall until phase 5. - DB driver: the progress callback aborts statements of cancelled tasks and the cursor translates the interrupt to TaskCancelledError. Transaction/savepoint cleanup catches BaseException so cancellation and GreenletExit roll back properly; savepoint bookkeeping is shielded from aborts. - Retry/backoff sleeps become cancellable_sleep (~27 files) and checkpoint() sits at evm tx pagination, decoding and historical-balance loops. - substrate/manager.py drops gevent entirely: library-level timeouts via ws_options and a default timeout on the http transport session. - New unit tests for the cancellation machinery; kill-semantics tests rewritten to the cooperative model. Also fixes rebase fallout: stale kraken gevent.sleep test patches, undefined greenlet.exception in aggregator.py and bit2me.py importing gevent.lock.

  • Lefteris Karapetsas (12 Jun 26)

    chore: migrate gevent primitives to stdlib Phase 1 of docs/designs/gevent_to_asyncio.md (#10090). Under monkey.patch_all() the stdlib threading/time primitives are already gevent-cooperative, so business logic moves to them directly: - gevent.sleep -> time.sleep (rate limit/backoff paths) - gevent.lock.Semaphore/RLock -> threading equivalents; locks checked with .locked() become threading.Lock - gevent.event.Event -> threading.Event - the spawn/joinall fan-out sites go through the new rotkehlchen.concurrency seam so task-handle access stays behind functions that can switch to thread futures at the flip - ruff TID251 bans gevent imports outside a per-file allowlist that names the migration phase responsible for deleting each entry Semantics are unchanged while gevent runs; after the final flip these become real threading primitives without further call-site changes.

  • Lefteris Karapetsas (12 Jun 26)

    docs: gevent to asyncio migration design Full execution plan for #10090: hybrid model with an asyncio shell and synchronous business logic in worker threads, Flask retained via a WSGI-ASGI bridge, incremental landing with a small final flip. Living document; status markers updated by the PRs doing the work.

  • Konstantinos Paparas (06 Jul 26)

    settings: derive store refs via toSettingsRefs Replace the hand-written useComputedRef(settings, 'key') lines (and their matching return entries) across all four settings stores (frontend, accounting, general, session) with a single toSettingsRefs helper that derives one readonly ref per settings key. The helper uses toRef(getter) rather than computed(), so it allocates no reactive effect per key and stays in sync when the whole settings object is replaced. The general store's two renamed public keys (currency <- mainCurrency, floatingPrecision <- uiFloatingPrecision) are destructured out of the derived refs and re-added, and its derived currencySymbol getter is kept. The exported store surface is unchanged: a new settings-store-shape spec snapshots the public shape of all four stores and stays byte-identical, guarding the ~210 consumers that read these stores via storeToRefs. Consumers that annotated store refs as ComputedRef (use-privacy, use-scramble-settings) are updated to Readonly<Ref>. useComputedRef had no remaining consumers, so it is removed.

  • Konstantinos Paparas (06 Jul 26)

    feat: describe each action in the event picker Add a plain-language one-line description under each verb in the history event Action picker, sourced from the event-types documentation. The strings live in en.json keyed by the serialized event category, resolved through a static map to satisfy the no-dynamic-key lint rule. Verbs without a description fall back to the derived type/subtype subtitle.

  • Lefteris Karapetsas (04 Jul 26)

    feat: avatars for gwei names Route .gwei names in try_download_ens_avatar to the GNS contract, which implements the same ENS resolver text(bytes32,string) interface, and skip the ENS metadata app for their NFT avatars since it only knows ENS names, falling back to opensea directly. The frontend needs no changes: avatars are requested for the displayed name from the merged name mappings, so they follow the address name priority setting automatically.

  • Lefteris Karapetsas (04 Jul 26)

    VCR tests

  • Lefteris Karapetsas (04 Jul 26)

    chore: freeze time in gns calendar reminder test Without it the test would start failing once lefteris.gwei expires in July 2027, since past expiries are skipped by the reminder creator.

  • Lefteris Karapetsas (04 Jul 26)

    feat: calendar reminders for gwei name expiries Include CPT_GNS in the name service expiry reminder creation so that gwei name registrations and renewals get calendar entries with the same week/day-before reminders as ENS and Basenames. The GNS decoder already provides the name/expires extra data the task consumes.

  • Lefteris Karapetsas (04 Jul 26)

    feat: naming systems for address display names Generalize the ens_mappings cache to store one name per naming system (source column, folded into the unreleased v52->v53 upgrade) and add a NamingSystem registry with ENS and GNS (gwei names) implementations. Each system is an entry of the address_name_priority setting: GNS is valid but off by default and only queried on-chain when the user adds it to the priority list, with the relative order deciding which name wins. Forward resolution routes by suffix so .gwei names resolve via GNS reverseResolve/addr which have built-in forward checks. The frontend priority setting offers the new GNS names source.

  • Lefteris Karapetsas (04 Jul 26)

    feat: add gwei names (GNS) decoder Decode the Gwei Name Service (an ownerless ENS fork with burned fees and a single NameNFT contract): commit/reveal registration, subdomain registration, renewal, primary name, setAddr, setText and contenthash events. Reuses EnsCommonDecoder like the Basenames decoder, with the contenthash extraction made overridable since the GNS contract is not in the contracts DB. Names resolve on-chain via getFullName since the token id equals the namehash, cached in the global DB.

  • Lefteris Karapetsas (04 Jul 26)

    chore: bump ruff to 0.15.20 Ignore the new preview rule PLW0717 (too many statements in try clause) which fires 129 times across the codebase and is too strict for our error handling style.

  • Lefteris Karapetsas (04 Jul 26)

    fix: restore progress handler on exception Wrap the critical_section yield in try/finally so the sqlite progress handler is reinstated even when the caller's block raises. Previously an exception inside the context left the connection without a progress handler, disabling gevent context switching for all subsequent queries. Flagged by ruff 0.15.20's RUF075 fallible-context-manager.

  • Lefteris Karapetsas (04 Jul 26)

    fix: remove duplicate typing-only imports Ruff 0.15.20 flags names imported both at runtime and under TYPE_CHECKING as F811 redefined-while-unused. Keep the typing-only import where the name is only used in annotations and drop the TYPE_CHECKING duplicate where the runtime import is needed.

  • Yรกbir Benchakhtir (03 Jul 26)

    Merge pull request #12527 from yabirgb/deps Bump dependencies and address unneded ignores

rotki Security

6.5/10

Repo Security Summary

Updated 29 Jun 26

  • Code-Review 0/10
  • Maintained 10/10
  • Dangerous-Workflow 10/10
  • CII-Best-Practices 0/10
  • Token-Permissions 10/10
  • Security-Policy 0/10
  • License 10/10
  • Binary-Artifacts 10/10
  • Packaging 10/10
  • Branch-Protection 8/10
  • Signed-Releases 0/10
  • Fuzzing 0/10
  • SAST 9/10
  • Pinned-Dependencies 9/10

rotki Website

Website

rotki

rotki is an open source portfolio tracker, accounting and analytics tool that protects your privacy.

Redirects

Does not redirect

Security Checks

All 65 security checks passed

Server Details

  • IP Address 136.244.105.88
  • Hostname 136.244.105.88.vultrusercontent.com
  • Location Amsterdam, Noord-Holland, Netherlands (Kingdom of the), EU
  • ISP Vultr Holdings LLC
  • ASN AS20473

Associated Countries

  • US US
  • NL NL

Safety Score

Website marked as safe

100%

Blacklist Check

rotki.com was found on 0 blacklists

  • AntiSocial Blacklist
  • Artists Against 419
  • Badbitcoin
  • Bambenek Consulting
  • CERT Polska
  • CoinBlockerLists
  • CRDF
  • CryptoScamDB
  • EtherAddressLookup
  • EtherScamDB
  • Fake Website Buster
  • MetaMask EthPhishing
  • NABP Not Recommended Sites
  • OpenPhish
  • PetScams
  • PhishFeed
  • PhishFort
  • Phishing.Database
  • PhishStats
  • PhishTank
  • Phishunt
  • RPiList Not Serious
  • Scam.Directory
  • SecureReload Phishing List
  • Spam404
  • StopGunScams
  • Suspicious Hosting IP
  • ThreatFox
  • ThreatLog
  • TweetFeed
  • URLhaus
  • ViriBack C2 Tracker

Website Preview

Website preview

rotki Reviews

More Secure Budgeting

About the Data: rotki

Change History

API

You can access rotki's data programmatically via our API. Simply make a GET request to:

https://api.awesome-privacy.xyz/v1/services/rotki

The REST API is free, no-auth and CORS-enabled. To learn more, view the API Docs or read the API Usage Guide.

Share rotki

Help your friends compare Secure Budgeting, and pick privacy-respecting software and services.
Share rotki and Awesome Privacy with your network!

View Secure Budgeting (7)