mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
ad93901ae52d0d82269811bf0bf97ae4526dc747
19
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
dfa5165766 |
Integration tests (#3477)
* fix android CI * feat: Add integration test harness and new-ui test keys This change: - Adds a BaseRobot with bounded waits to replace hard sleeps in integration tests - Adds AppLauncher, TestConfig and TestWallets as the core test harness - Adds onboarding flows composing the existing onboarding robots - Adds robots for the new dashboard, home page and lightning username page - Adds a tier0 onboarding create suite targeting the new UI - Applies the declared navbar action keys that were never attached to the InkWell - Fixes AlertWithOneAction dropping its buttonKey since the alert redesign - Adds stable ValueKeys to the home page action row, wallet name, top bar and sync bar - Makes the legacy tap and text entry helpers wait for widgets instead of sleeping * fix: Test secrets drift and integration test runner parameterization This change: - Adds missing test wallet seed and receive address entries to the secrets generator - Fixes trailing space typos in two secret key names - Adds bloc_test to pubspec_base.yaml so pubspec regeneration keeps it - Parameterizes the integration test runner with suite dir, tier, platform and device knobs - Fixes the runner aborting on first failure and misaligning durations after retries - Adds adb data reset so Android suites start from a fresh install * feat: Android emulator integration test workflows for dev PRs This change: - Adds a reusable integration test workflow building the app with prebuilt native deps and running the suites on an Android emulator - Adds the PR gate workflow triggering fund-free tier0 suites on pull requests into dev - Fixes the old workflow discarding the test runner exit code so failures now fail the job - Removes the disabled automated integration test workflow on the stale flutter image - Removes the commit-message gated Linux desktop test steps, unit tests stay - Removes the vestigial emulator config from the reusable build workflow * feat: Revive fund-free integration test suites for the new UI This change: - Adds tier0 suites for restore, seed confirmation, wallet switching, receive address, settings navigation and the show keys auth gate - Adds tier1 suites for sync status, swap quotes and transaction history running non-blocking on PRs - Adds robots for the settings, receive, swap and send sheets - Adds auth, wallet switching and restore flows - Applies widget keys to settings rows through their route names, they were declared but never attached - Adds keys to the swap amount fields, send inputs, confirm swiper and history tiles - Adds an integrationTest wrapper containing background async app errors that are not test failures - Makes taps wait out running route transitions before hitting the screen - Adds a watchdog to the test runner so a wedged driver fails bounded instead of hanging - Removes the old UI suites, robots, helpers and the stale widget_test counter template * chore: Add integration testing docs and suite template This change: - Adds docs/INTEGRATION_TESTS.md covering the architecture, local runs, tier rules and the flakiness playbook - Adds a copy-paste suite template teammates start new feature tests from * feat: Manual funds integration test workflow with funded-chain auto-discovery This change: - Adds a funds suite sending a real self transaction on every funded chain - Adds a funds suite creating a real swap and broadcasting its deposit - Discovers funded chains from the funded wallet seeds map, adding a seed to the secret adds the chain - Adds the manual workflow dispatched from the Actions tab with flow, chain and recording inputs - Gates the run behind the funds-tests environment and posts the result to slack - Keeps funded seeds out of PR builds, the checked in seed map is empty * fix: Harden integration test harness from review findings This change: - Fixes body failures being swallowed by the guarded test zone, failing suites hung until the watchdog instead of failing fast - Fixes multi chain CHAINS dispatch values being split apart by the define forwarding - Fixes the swap limits wait timing out on pairs without a provider minimum - Makes unknown chain names in CHAINS fail with a readable message - Keys favorite token rows by index, duplicate token symbols crashed on duplicate keys - Guards the recording encryption so a keyserver flake cannot eat the test result - Removes the dead security and backup robot and corrects the screenshot claim in the docs * chore: Hold two funded seeds per chain in the funded wallets map This change: - Changes the funded wallet seeds map to a list of seeds per chain - Keeps the first seed as the primary for sends and swaps, the second is reserved for cross wallet scenarios * feat: Try each funded wallet per chain until one has a balance This change: - Adds a funds flow restoring a chain's funded wallets in turn and using the first with a spendable balance - Treats a wallet that finishes syncing while still empty as drained so the next seed gets its turn quickly - Fails a chain with a top up message when every funded wallet of that chain is empty * reformat integration test related files * fix: Run the integration test emulator on the kvm capable runner This change: - Moves the test job to the linux-amd64 runner the old emulator workflow targeted, the android builders have no /dev/kvm - Exposes /dev/kvm through a docker device flag instead of a volume, a bind mount materializes a directory when the host node is missing - Fails the kvm check in seconds with a clear error instead of waiting out the emulator boot timeout * fix: Queue the integration test job on the live android runner pool This change: - Moves the job back to the android runner labels, no runner carries the old linux-amd64 label anymore so the job queued forever - Keeps the kvm device flag and check so a host without kvm fails immediately with a clear error * feat: Run the integration test gate on github hosted runners This change: - Moves the test job to ubuntu-latest which has working kvm, the self-hosted android builders do not expose /dev/kvm yet - Runs directly on the vm since the cake container image does not fit hosted disk, flutter is pinned through the flutter action - Adds rust android targets for the breez cargokit build and java 17 for gradle - Keeps the prebuilt native deps from the ghcr docker cache, they pull fine on hosted docker - Adds pub, gradle and flutter caching plus a disk cleanup step - Keeps every input, the runner script, tier1 soft run, recording and artifacts unchanged * fix: Install flutter from the same source as the ci container image This change: - Clones the flutter 3.41.9 tag directly like the container image instead of using the release archive - The archive ships a stricter dart that rejects the checked in mweb ffi bindings, the tag clone compiles them - Caches the cloned sdk between runs * fix: Regenerate the mweb ffi bindings with the workflow dart This change: - Regenerates cw_mweb/lib/generated_bindings.g.dart after the deps step, the docker deps builder generates it with an older dart whose ffigen output the current compiler rejects * fix: Pin ffigen so fresh resolutions generate compliant mweb bindings This change: - Pins ffigen to ^20.1.1 in cw_mweb, the unconstrained dependency resolved to 7.1.0 on fresh checkouts - Old ffigen emits ffi classes without the base or final modifiers the current dart compiler requires * fix: Restore the pinned cw_mweb pubspec before regenerating bindings This change: - Restores cw_mweb/pubspec.yaml after the deps step, the deps image rsync clobbers it with the stale unpinned copy so the ffigen pin never applied - Wipes the rsynced pubspec.lock and .dart_tool so pub resolves the pinned ffigen fresh - Asserts the regenerated bindings carry class modifiers so a bad generation fails in seconds - Skips the libclang install when it is already present, apt spent fourteen minutes on it * fix: Run the emulator test logic from a file, the action splits script lines This change: - Moves the whole emulator script into a file written before the emulator step, the action executes every script line as a separate sh command so multi line logic broke with a syntax error - Passes the action a single line script invoking that file with bash * fix: Give the ci emulator a real phone profile This change: - Sets the avd to the pixel 5 profile, the default 320x640 skin cannot fit the seed word grid so every wallet creation suite failed its seed display checks - Caps tier0 attempts at ten minutes, two failing suites at the thirty minute worst case blew the job timeout * fix: Wait for pages on the ci emulator instead of asserting immediately This change: - Makes the legacy page assertion poll for the page, the wallet keys page arrives after the ci emulator finishes unlocking the wallet so both auth gated suites failed on a race - Raises the tier0 attempt timeout, the first attempt for each suite compiles that target's kernel and was being killed then passing on retry - Raises the job timeout to fit the per suite compiles * fix: Recover from driver attach failures instead of burning the timeout This change: - Detects attempts where the flutter driver never attached to the app and retries them without spending the real retry, restarting adb first - Lowers the ci attempt timeout back to ten minutes, every passing attempt finishes within four so a longer cap only makes an attach failure cost more * fix: Bound the adb recovery and give up when the driver never attaches This change: - Bounds every adb call in the recovery path, adb wait-for-device blocks forever when the emulator is gone and silently ate a whole ci job - Aborts the suite when adb cannot be brought back instead of retrying against a dead device - Stops the run after a few driver attach failures, an environment that never attaches will not fix itself - Dumps logcat after every failed attempt so a failure to start the app leaves evidence behind * fix: Stop the runner failing when every suite passes This change: - Assigns the result arrays empty instead of only declaring them, set -u treats a declared but never assigned array as unbound so the summary died on the first run where nothing failed * fix: Make the ci build cacheable and keep tier results when the runner dies This change: - Splits the flutter and pub caches into restore and save steps, actions/cache only saves when the whole job succeeds so they were never written and every run paid for a cold build - Lets setup-gradle write its cache, it is read only on branches other than the default one - Caps the gradle heap before the test phase, the six gigabyte daemon plus the emulator left the runner too little to stay alive - Runs tier0 and tier1 as separate emulator steps so each uploads its logs before the next can take the runner down - Logs memory and disk around each tier - Documents how ci runs the suites and the two environment quirks that only bite there * fix: token decimals defaulting to zero and breaking amount parsing in solana (cherry picked from commit c220bbb46df9468faf9a8967cb0ef672af7cc664) * fix: Install the test error handler after the app boots and drain benign errors This change: - Installs the test error handler after main() instead of before it, the app replaces FlutterError.onError while booting so the handler installed earlier never ran - Replaces the app's async error handler during tests, it returns at its first await without recording anything so the framework asserted instead of reporting the real error - Drains background errors the app tolerates at runtime from the wait loops, anything unrecognised still fails the test where it happened - Collects the tolerated categories in one place, layout and semantics assertions from debug builds plus the transient network failures exception_handler.dart already ignores * fix: Import FlutterExceptionHandler from foundation so the test build compiles This change: - Imports package:flutter/foundation.dart in the launcher, material.dart only re-exports Brightness and UniqueKey from foundation so FlutterExceptionHandler was not in scope - Drops the material.dart import, every symbol the launcher uses comes from foundation * chore: Drop the funds-tests environment from the funds workflow and docs This change: - Removes the environment input from the funds workflow, we no longer use a funds-tests GitHub environment so passing it would only auto create an unprotected one on the next dispatch - Says plainly in the docs that nothing gates the dispatch, the doc still claimed the run waits for environment approval - Names write_funds_secrets as the one thing keeping funded seeds out of a PR build, that used to be backed by environment scoping too * chore: Trim the integration test comments down to the ones that carry weight This change: - Drops the doc comment sitting on top of every robot primitive, flow and config getter, they restated the method name and nothing else - Collapses the multi paragraph docstrings on the launcher, the error drain and the funded wallet lookup into the one or two lines that actually explain why the code is shaped that way - Removes the file and class header paragraphs, the funded wallets stub keeps a short note because an empty map needs one - Keeps the notes worth having, the chain quirks in the onboarding flow, the route transition tap, the dust thresholds, the void attempt handling in the runner * chore: Fold the components folder into core and clear out the leftover comments This change: - Moves common_test_cases.dart into core, the components folder was down to that one file after common_test_constants.dart went - Drops six methods from it that nothing calls, dragUntilVisible alone was 95 lines of scroll logic with no caller - Removes the comments narrating the line below them across the older robots, "Drag to the left" above a moveBy and "Confirmation for buttons 1-9" above the loop - Deletes the commented out body of takeScreenshots and says instead that it does nothing, all fifteen callers have been no-ops - Notes in the docs that new robots extend BaseRobot while the older ones still carry a CommonTestCases * chore: Clear out pending items * refactor: Move the onboarding robots onto BaseRobot and drop the sleeps This change: - Deletes common_test_cases.dart, the sixteen robots that held a CommonTestCases now extend BaseRobot like the newer ones do - Moves the primitives those robots needed onto BaseRobot with the same behaviour, isSpecificPage, hasType, hasValueKey, hasText, hasTextAtLeastOnce and swipePage - Renames each robot's page check to isDisplayed so every robot answers the same question the same way - Removes the twenty six defaultSleepTime calls, every one of them sat in front of an action that already waits for what it needs - Waits for the view model to move to the next word in seed verification instead of sleeping a second and hoping, and fails with a message when it does not - Settles after opening a tab on the wallet keys page, the assertions there read the tree straight after the tap - Drops the fifteen takeScreenshots calls, the method's body has been commented out the whole time so none of them did anything - Formats the three files dart format was still rewriting * fix: Wait out the navigation the removed sleeps used to cover This change: - Settles after a page mounts in isSpecificPage, the route being replaced stays in the tree while it animates out and assertions on text were matching both screens, seed_confirmation_test failed on finding two "Verify Seed" widgets - Settles after the last pin digit, the gate sends itself away and returning early landed the caller's next navigation on a locked navigator, wallet_switching_test failed on the !_debugLocked assertion Both are places where waiting for a widget to exist is not the same as waiting for the app to be idle, which is what the sleeps were quietly doing. * fix: Recognise every way a flutter drive attach fails and give tier1 a retry This change: - Matches the other wordings flutter drive uses when the driver never attaches, we had only the connect one and missed the isolate initialise, the service disappeared and the driver extension variants - Gives tier1 the same single retry tier0 has, running it with none meant a driver that never attached was reported as a failing test Two of the three tier1 failures on the last run were attaches that never happened, neither of them reached any test code. * fix: Sync status test watched the wrong status class, history scroll had no guard This change: - Accepts SyncronizingSyncStatus in the sync status suite, cw_core spells it without the h and it is a separate class from SyncingSyncStatus, so chains reporting it sat out the full three minute timeout and then failed saying the wallet never synced while printing Synchronizing - Waits for the home scroll view before handing it to scrollUntilVisible, which calls single on the finder every drag and reported a missing scroll view as a bare Bad state: No element - Skips the scroll entirely when the history tiles are already on screen * fix: Test the transaction history the way the screen actually works The suite looked for history tiles on the home page and scrolled for more when it found none, which could never pass. The home page renders the history under a tab, and for a wallet with tokens the assets tab is the one selected first, so the history section was never built. It also shows a three item preview, itemsShort caps it at shortHistoryLength, and everything else sits behind the All button in a modal. This change: - Opens the history tab before looking for tiles, wallets with no assets tab render the history with no tab bar at all and skip that step - Asserts the preview never renders more than the three the view model hands it, instead of scrolling for tiles that were never coming - Opens the All button and checks the full list in the modal, which nothing covered before - Adds keys for the tab items, the All button, the single tab history bar and the modal root * fix: Stop the runner wiping a real wallet install and document what CI does that the docs did not Following app_config.sh and then running the tests locally clears com.cakewallet.cake_wallet between every suite, which is a developer's own wallets. CI never hits this because the workflow renames the app afterwards, a step that only existed in the workflow. This change: - Refuses to clear a package that is not named for testing, with the rename command in the message - Adds that rename to the local prerequisites, next to the reason it matters - Spells out the assets/images vector pass, compile_graphics.sh does not cover that folder and those vec files are neither committed nor ignored, so a fresh checkout fails on the welcome screen - Notes that a second Gradle daemon from Android Studio deadlocks the build * test: Make the tier0 and tier1 assertions check what they claim to An audit of every robot against the screen it drives turned up assertions that pass without telling you anything, the same shape as the transaction history suite looking for tiles on a tab it never opened. This change: - Reads the receive address off the screen instead of the view model, the view model holding the right address says nothing about what the user is told to send to, and reads both the chunked and the plain render because only handling the first comes back empty - Verifies a restored wallet knows the address its seed derives, restoring the wrong wallet from a correct seed used to look the same as success, and this found two stale addresses in the secrets - Replaces the sync bar check, the key it asserted sits on a widget that renders for every status including none, so it was true whenever the home page was up - Requires the All transaction view to render more than the home preview, it could not tell the two apart before - Fails the keys page check when a wallet type matches none of its branches instead of passing having verified nothing * refactor: Remove outdated items from integration tests * test: Wait for the wallet to actually change instead of pumping a fixed half second Tapping a wallet row does nothing when that wallet is already open, and the load runs behind a progress overlay, so a tap that went nowhere looked the same as a successful switch. This change: - Waits for the app store to report the wallet before returning, and fails with a message when it never does * test: Open wallet groups before looking for a wallet by name Wallets that share a seed are listed as children of their group's tile and are not on screen while it is closed, so switching to one would time out looking for a name that is only rendered once the group is opened. Single seed wallets carry their own name as the tile title, which is why this has not come up yet. This change: - Opens the group tiles when the wallet is not already on screen * chore: Keep the receive address widget diff to the key it needed A formatter pass rewrote the whole file, quotes, body style and trailing commas, for what was a one line addition. Restored the original shape so the diff shows only the key. * chore: Drop trailing whitespace left by the comment cleanup * test: Cover the send screen refusing what it cannot send Nothing covered the send screen, which is the one place in the app where getting it wrong costs the user money. The property worth holding is that the swiper, the point of no return, is only ever offered for a transaction the wallet actually built. This change: - Adds a tier0 suite driving an empty form, an address the chain cannot parse, and a well formed address on a wallet that has never held anything, and requires that none of the three ever produce a swiper - Waits for the swiper and requires it not to arrive rather than checking once, the screen passes through syncing, building and failed states on the way and any of them can be the one you catch - Notes in the docs that a merge from dev leaves the generated chain proxies and the assets/images vec files behind, which has broken the local build three times now * test: Add send and swap dry runs and a flows mode that never spends The funds suites were all or nothing: the only way to find out whether a funded wallet still works was to spend from it. Almost everything worth checking happens before the swipe, the wallet finds its balance, prices the fee and builds a transaction, or the provider returns a live quote, and none of that costs anything. This change: - Adds a send dry run that stops at the swiper, reaching it means the transaction was really built - Adds a swap dry run that stops before the swap button, creating a trade registers an order with the provider even though no funds move, so it goes no further than the quote - Adds a dry-run value to FLOWS that runs both and neither of the spending suites, and makes it the default so a mis-click cannot send anything * test: Cover a wallet name that is already taken being refused Two wallets answering to the same name is how someone ends up sending from the wrong one, and nothing checked that the form stops it. This change: - Adds a tier0 suite that creates a wallet, starts a second one and gives it the same name, and requires the form to refuse it and stay put - Splits the walk to the naming form out of createAdditionalWalletFromWalletList so a suite can stop there instead of going through with the wallet * test: Cover a seed the wallet cannot parse restoring nothing Restoring from a seed the wallet does not understand either fails outright or, worse, derives some other wallet the user has no keys for. The restore button is disabled until the words check out and nothing checked that. This change: - Adds a tier0 suite that types twelve words outside the wordlist, presses restore, and requires the form to stay put - Follows it with the real seed in the same run, so the refusal above cannot pass just because the form restores nothing for any seed at all - Splits the walk to the restore form out of restoreFirstWalletFromSeed so a suite can stop there * refactor: Make spending its own switch instead of a value hidden in flows flows says which flow to run, so folding a dry-run value into it made the dropdown answer two questions at once and buried the one that matters. This change: - Adds a spend input, off by default, and gates the broadcasting suites on it - Puts flows back to all, send or swap - Runs the dry runs whenever their flow is picked, they cost nothing either way * chore: cleanup minor issues * test: Cover one seed backing wallets on two chains Wallet groups had no coverage at all, and the part that would break quietly is the seed: a group member that derived its own seed instead of sharing one looks identical from the wallet list. It is also the only way a wallet ends up inside a collapsed group tile, which is the path switchToWallet handles and nothing exercised. This change: - Adds a tier0 suite that creates a solana wallet, adds an ethereum wallet to its seed, and requires both to report the same seed - Switches back to the first afterwards, which only works by opening the group it now sits in - Adds the flow for it, a child wallet skips the seed and verification screens for a page explaining it shares one, so it cannot reuse the normal creation steps - Adds the one key the group page was missing, its next button * test: Cover changing the pin replacing the one that unlocks the wallet Nothing checked that changing the pin actually takes. Watching the setup screen close proves nothing, the only evidence is the new pin opening something the old one guarded. This change: - Adds a tier0 suite that changes the pin and then unlocks the seed and keys page with the new one - Goes through the pin gate on the way in, so it also covers the current pin being required before the change is allowed * test: Cover changing the language, and make settings rows and pickers addressable Writing this turned up two things in the app rather than the test. Only ListItemRegularRow was given its keyValue as a widget key. Toggles, checkboxes, dropdowns and selectors all took the same keyValue and dropped it, so every settings control that is not a plain navigation row could not be found at all. Picker named three item types and gave everything else an empty string, so a picker over plain values, the language list among them, rendered every row with the same key. This change: - Passes keyValue through as a key for the other four row types - Falls back to the item itself for the picker name, so each row is distinct - Adds a tier0 suite that changes the language and requires the row to show the new one * test: Cover switching the node the wallet talks to Which node a wallet is pointed at decides what it sees, and a switch that quietly does not take leaves someone on a node they believe they left. Nothing covered it. This change: - Adds a tier1 suite that reads the current node, switches to another one, confirms, and requires the wallet to be pointed somewhere new - tier1 rather than tier0 because opening the page speed tests every node against the network * test: Cover the address book keeping the address it was given Pasting an address once and trusting it afterwards is the whole point of an address book, so an entry that comes back changed is a way to lose money quietly. Nothing covered it. This change: - Adds a tier0 suite that saves a contact through the form and requires the stored address to match what was typed - Checks the saved record rather than the rows, the list filters what it shows by the currency in play so a contact can be saved and correctly not on screen - Adds keys for the add button, the name field, the currency picker, the address field and save * fix: Key picker rows on a name that does not change with the language Enumerable items fell through to toString, which is translated for some of them, so the same row carried a different key depending on the language in use. * test: Cover the fiat currency setting, transaction details and sending to a contact This change: - Adds a suite for turning the fiat api off and on, which is what decides whether the currency setting is offered at all, then changes the currency through it - Adds a suite that taps a transaction and checks the details that open belong to it - Adds a suite that saves a contact and picks it from the send screen - Adds a suite for restoring a 25 word monero seed, which takes a seed type and a restore height the polyseed path never asks for - Keys the address book button on the send screen so a test can reach it - Lets SUITE_DIR take a single suite file, for iterating on one test - Lists every suite and what it proves in the integration tests doc * test: Cover renaming and deleting a wallet This change: - Adds a suite for renaming a wallet, including the refusal when the name is already held by another wallet - Adds a suite for deleting a wallet, checking it leaves storage and not only the list - Keys the edit button in the wallet list and the name field, the two buttons and both dialogs on the wallet edit page, none of which could be reached from a test before - Fixes the wallets list robot, which looked for a single Wallets title even though the navbar carries the same word, and looked for wallet names across the whole tree even though every dashboard tab stays mounted in an IndexedStack * ci: Report every integration test run to slack This change: - Posts a report to slack on every run, naming each tier's counts, duration and any suite that failed, with the full list of what passed in a reply on the same message - Adds a SUMMARY_FILE knob to the runner, which writes what it counted in a form the report can be built from rather than parsing the run log - Skips with a notice when SLACK_APP_TOKEN or SLACK_TESTS_CHANNEL is missing, and never fails the gate when slack is unreachable - Leaves the funds workflow alone, the new slack_notify input defaults to off * chore: Removing unneeded items * ci: Say which slack scope was missing when a report is rejected A rejection carries the scope it wanted in a field of its own, and dropping it left the warning saying missing_scope without saying what to go and add. * ci: Report funds runs to the tests channel and say when slack refuses them This change: - Sends the funds report to SLACK_TESTS_CHANNEL, so both test workflows land in one place and the apk channel is left to builds - Checks the slack response, the post was going out unchecked so a rejected report looked like a delivered one - Builds the message from the environment instead of interpolating workflow inputs into the shell, a quote in a dispatch input was enough to break the payload * ci: Give the test reports their own slack app Uploading apks and reporting test results are different jobs with different scopes, and the name on a message is how anyone in the channel tells which one sent it. Both test workflows now post through SLACK_TESTS_TOKEN, which only needs chat:write, and the apk uploads keep SLACK_APP_TOKEN and its file scopes. * fix: Restore the swap amount key and say when a suite never ran The AnyPay redesign moved the deposit amount field into SwapAmountBox and it lost the key the swap suite enters an amount through, so that suite could no longer reach it. Both amount fields are keyed now, deposit and receive. This change also: - Records whether a failed suite failed on its own or because the driver never attached, which is what happened to send_validation_test in run 31364715120 where the suite never actually ran - Rebuilds the slack report out of blocks, so the status is a heading, the tiers sit in their own fields, failures name their reason and the run is a button rather than a bare url * perf: End an attempt early when the driver cannot attach flutter drive says it is struggling to attach within about a minute and then, when it is really wedged, sits there until TEST_TIMEOUT kills it. Run 31364715120 lost 20 minutes to two attempts doing exactly that. This change: - Watches the attempt log and ends the attempt VOID_GRACE seconds after the driver first reports trouble, 120 by default, so the retry starts on a fresh app instead of waiting out the remaining eight minutes - Starts that clock at the first complaint rather than at the attempt, since the attempt spends its first minute building and installing - Moves the marker list into one variable, the watchdog and the after the fact check were going to drift apart otherwise * perf: Drop the driver grace to 60s and take the whole drive down with it Measured against run 31364715120: the 19 attempts that attached did so in under a second, while the two that never attached had the warning out at 5 seconds. 60 leaves a wide margin over a healthy attach on a runner that was already running slow, and saves another minute on each dead attempt. Ending the attempt now walks the process tree rather than signalling the wrapper. On linux the drive runs under timeout, and a KILL to that cannot be forwarded, so the drive would have been left holding the device just as the retry went looking for it. * trigger another test run * Triggering another run, its been green so far * fix: Wait for the wallet to be ready before tapping send in the funds suites The send button is disabled until isReadyForSend, which wants a synced wallet, but the funds flow moves on as soon as a balance appears. Every funded chain was therefore tapping a disabled button and then waiting ninety seconds for a confirm sheet that could never open. The robot now waits on the same value the screen gates on, so a wallet that never syncs says so instead of looking like a missing widget. * test: Make a funds send failure say what went wrong and stop it spreading A send that never reaches the confirm sheet was reported as a missing ConfirmSwiper, which says nothing about the cause. It now reports the state of whichever sheet is up, what the wallet holds, and the build error when the chain rejected the transaction. Recovery between chains was a single pop, so the first chain to fail left the app somewhere the next chain's restore could not start from and every chain after it failed too. It now unwinds until the home page is back and stops the suite if it cannot get there. * test: full testing of all the funded tests and fix issues that came up with them * fix: update sync key to fix sync test * fix: out of space error * fix: integration test review issues * fix: merge conflicts and update test suites * fix: false test passes and add funded wallet passphrases chore: remove unused integration test robot methods feat: save integration test screenshots on Android feat: pick the swap pair for the funds dispatch * fix: Keep the runner output on the step log through a file descriptor * fix: timeout because of runner crash * test: handle new seed ui flow and fix hang for runner * fix: emulator teardown not rendering succesfully feat: use currency picker for selecting swap options for funded run selected swap currency * test: add key for compact dot key for sync and link to robot * minor fixes * test: review fixes for integration tests |
||
|
|
3a04c7ff1e |
feat: restrict history api calls when toggle is off in privacy settings (#3615)
* feat: restrict history api calls when toggle is off in privacy settings * refactor: remove unnecessary transaction timer cancellation |
||
|
|
09fdded7a6 |
docs: update build instructions and fix stale doc links (#3559) (#3561)
Replace the README's placeholder build section and the stale howto-build-android.md reference with links to the current platform-specific guides in docs/builds/. Also fix broken links in docs/ADDING_NEW_WALLET_TYPES.md that pointed to a non-existent adding_new_l2_network_guide.md file and align the README/docs copyright years with LICENSE.md (2018-2025). |
||
|
|
42019d8644 | security: require app-level vulnerability proofs | ||
|
|
41e2cba322 |
security: harden vulnerability disclosure policy (#3419)
* security: harden vulnerability disclosure policy + Slack routing * security: add discretionary reward policy and marketing-site out-of-scope |
||
|
|
d09391bf99 |
Upgrade Flutter version to 3.41.9 (#3213)
* commit generated files and remove codegen metadata * remove hive_generator dependency * update dependencies * update intl * update ios project files * update native code * update android deps * update image name in ci * revert impeller * re-ignore min version error * fix cw_decred dependency * update hive in cw_core * fix docker build cache for decred * fix conflict * prevent frozen coins being reset on coin info refresh * fix for makeSecure --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> |
||
|
|
0844fd8012 | fix: missing steps for linux build [skip ci] (#2985) | ||
|
|
031b90c1d6 | fix: update docs and dockerfile (#2975) | ||
|
|
2a77d359f1 |
CW-1250: Refactor L2 Chains Code (#2677)
* feat(evm): add ChainConfig model and related classes - Add ChainConfig class for immutable EVM chain configuration - Add ChainCapabilities class for chain feature flags - Add FeeType enum and FeeModel class for fee configuration * feat(evm): add EvmChainRegistry for centralized chain management - Add singleton EvmChainRegistry class with chain configuration storage - Initialize with Ethereum, Polygon, Base, and Arbitrum chains - Add mappings for WalletType, tag, and CAIP-2 to chainId lookups - Provide lookup methods for chain configurations by various identifiers - Support for querying available chains and registered chain IDs * feat(evm): add EVMChainClientFactory and consolidate chain clients - Create EVMChainClientFactory for creating chain-specific clients based on chainId - Move all chain clients (EthereumClient, PolygonClient, BaseClient, ArbitrumClient) into cw_evm/lib/clients/ folder - Refactor EVMChainClient from abstract to concrete class with default implementations - Add default implementations for fetchTransactions, fetchInternalTransactions, and prepareSignedTransactionForSending - Update all client classes to use super(chainId: X) constructor pattern - Factory returns EVMChainClient directly for unregistered chains instead of throwing - Remove dependencies on cw_ethereum, cw_polygon, cw_base, cw_arbitrum packages from pubspec.yaml - Consolidate all EVM chain client implementations in one location for easier maintenance * add ui for evm switcher on eth * add inputs for evm network list edit switches * refactor(evm): Make EVMChainWallet concrete and unify transaction classes - Make EVMChainWallet and EVMChainWalletBase concrete classes (removed abstract) - Implement all previously abstract methods in EVMChainWallet - Make EVMChainTransactionInfo and EVMChainTransactionHistory concrete - Reorganize default tokens into tokens/ folder: - Refactor EVMChainDefaultTokens to import and use token classes from tokens/ folder - Move clients to clients/ folder: - Move evm_chain_formatter.dart to utils - Update chain-specific transaction history classes to pass walletType to fromJson - Update old wallet classes (EthereumWallet, PolygonWallet, etc.) to pass walletType - Maintain compatibility with existing per-chain wallet classes This refactoring enables the unified EVMChainWallet architecture while maintaining full backward compatibility with existing per-chain wallet implementations. * feat(evm): Add chain selection capability to EVMChainWallet - Add selectedChainId observable field to track currently selected chain - Add selectChain(chainId) action method to switch between EVM chains - Add selectedChainConfig computed getter for accessing chain configuration - Initialize selectedChainId from client.chainId in constructor - Add _getClientForCurrentChain() helper method for future use This enables chain switching functionality while maintaining full backward compatibility. The selectedChainId is initialized from the existing client, and all existing methods continue to work unchanged. Future increments will update methods to use selectedChainId internally. * refactor(evm): Update client immediately on chain selection - Update selectChain() to immediately create new client for selected chain - Remove _getClientForCurrentChain() helper method - Simplify all methods to use _client directly instead of helper calls - Client is now always in sync with selectedChainId This improves code simplicity and performance by eliminating repeated client checks. The client is updated synchronously when selectChain() is called, ensuring state consistency. * featr(evm): Add unified evm proxy setup and fix chain initialization - Add unified Evm proxy - Add evm.dart to .gitignore for generated proxy - Add generateEVM() to configure.dart for proxy generation - Fix selectedChainId to initialize from registry based on walletInfo.type Ensures selectedChainId always matches wallet type on initialization and adds unified proxy infrastructure. * refactor(evm): Update backward compat helpers to use registry via proxy - Add registry helper methods to unified EVM proxy - Update all lib/ files to use proxy instead of direct cw_evm imports - Enforce proxy pattern: no direct imports from cw_evm in main app - Update configure.dart to include registry methods in generated proxy Maintains backward compatibility while centralizing chain data access through the unified proxy pattern. * feat(evm): Add chain selection UI to dashboard - Add chain selection methods to DashboardViewModel (isEVMWallet, availableChains, currentChain, selectChain) - Add chain dropdown widget to balance page for EVM wallets - Dropdown shows current chain and allows switching between all registered EVM chains - Only visible for EVM-compatible wallets - Add chain selection methods to EVM proxy (getAllChains, getCurrentChain, selectChain) Users can now switch between EVM chains (Ethereum, Polygon, Base, Arbitrum) directly from the dashboard without creating separate wallets. All chain-related code follows the established proxy pattern. * feat(evm): Add persistence for selected chain ID - Add initialChainId parameter to EVMChainWallet constructor - Save selectedChainId to wallet JSON in toJSON() method - Load selectedChainId from JSON in open() method - Handle backward compatibility: wallets without saved chain ID use wallet type's default chain ID - Client is created with saved chain ID when opening wallet Selected chain preference is now persisted across app sessions. Users will see their previously selected chain when reopening wallets. * chore: Minor cleanup * refactor(evm): Remove old per-chain packages, consolidate to unified cw_evm - Delete cw_ethereum, cw_polygon, cw_base, cw_arbitrum packages - Remove old proxy files (lib/ethereum/, lib/polygon/, lib/base/, lib/arbitrum/) - Update all view models to use unified evm! proxy - Move DEuro functionality to cw_evm/lib/deuro/ - Remove dependencies from pubspec.yaml and configure.dart - Remove walletType parameter from EVM proxy methods All EVM chains now use unified cw_evm package through single proxy interface. lib/ only depends on cw_evm, simplifying architecture and enabling easy addition of new L2 chains. BREAKING CHANGE: Old per-chain packages removed * refactor(evm): Update wallet handling to use unified EVM proxy - Replace individual chain imports (Ethereum, Polygon, Base, Arbitrum) with a single EVM proxy import. - Refactor wallet-related methods across various view models to utilize the new evm proxy for various usecases. This change enhances code maintainability and prepares the codebase for the addition of new EVM chains. * refactor(evm): Update model generator and enhance EVM classes - Modify model_generator.sh to remove previous chain folders from the loop. - Change EVMChainTransactionHistoryBase and EVMChainWalletBase classes to be abstract. - Update import paths in configure.dart to reflect the new structure of the cw_evm package. * add evm switcher icons to pubspec_base * change evm switcher to sliding animation * formatting fix * dpi agnostic width * feat(evm): Enhance chain handling and transaction history management - Introduce getCurrentChainId function to EVMChainTransactionHistoryBase for dynamic transaction history file naming based on the current chain ID. - Update EVMChainTransactionInfo to include chainId, ensuring accurate transaction data representation. - Refactor EVMChainWalletBase to support automatic node connection and transaction history loading upon chain selection. - Implement saveBackup and restoreWalletFilesFromBackup methods in EVMChainWalletService to manage wallet backups based on wallet type. - Update various methods across the codebase to accommodate the new chain handling logic, improving overall functionality and user experience. * chore: Cleanups and updates * feat: add WalletType.evm and deprecate individual EVM chain types Add unified WalletType.evm enum value for all EVM-compatible chains. Deprecate old individual EVM types (ethereum, polygon, base, arbitrum) with guidance to use WalletType.evm for new code. Update serialization/deserialization functions to support WalletType.evm: - serializeToInt() returns 18 for WalletType.evm - deserializeFromInt() handles case 18 - walletTypeToString() and walletTypeToDisplayName() return 'EVM' - cryptoCurrencyToWalletType() maps all EVM currencies to WalletType.evm Old EVM types remain functional for backward compatibility with existing wallets. This is the foundation for unifying EVM chain management. * feat: update EVM detection functions to support WalletType.evm Update all EVM detection and chain helper functions to support the unified WalletType.evm while maintaining backward compatibility with old EVM types. All functions require chainId parameter when used with WalletType.evm, ensuring proper chain-specific behavior. Old EVM types continue to work without changes for backward compatibility. * feat: add currency mapping support for unified EVM wallet type - Update currency mapping to support WalletType.evm with chainId-based currency resolution. - Override EVMChainWallet.currency getter to use selectedChainId from registry. - Replace walletTypeToCryptoCurrency calls with wallet.currency in view models. - Add WalletType.evm cases to switch statements and consolidate EVM transaction list item methods. - Add explorer URL method to EVM interface * feat: Update wallet operations with unified WalletType.evm - Update wallet service to always create new wallets and restores with WalletType.evm, defaulting to Ethereum. - Preserve old wallet types when opening existing wallets. Update wallet instance creation and opening logic to handle unified EVM type with chainId-based config lookup. * Add WalletType.evm support across all switch statements - Update all switch statements and conditionals to include WalletType.evm alongside existing EVM types. - Add chainId-based node selection support for unified EVM wallets. - Fix redundant code in exchange view model. - Ensure backward compatibility with old EVM wallet types. * feat(evm): Implement chainId-based node management for WalletType.evm - Add chainId-based node storage for unified EVM wallets, enabling separate node preferences per chain. - Replace unsafe dynamic casts with type-safe getSelectedChainId method. Update all node retrieval and switching logic to support chainId-based selection. * feat: Update UI components to show unified EVM wallet option - Filter out old EVM wallet types (ethereum, polygon, base, arbitrum) from wallet creation and restore UI, showing only unified WalletType.evm option. - Update wallet type generation to include WalletType.evm instead of individual chain types. - Update configure.dart to generate WalletType.evm in availableWalletTypes - Add filtering checks to prevent old EVM types from appearing * fix: Fix WalletType.evm support and complete remaining edge cases Add missing WalletType.evm cases to switch statements to resolve compilation errors. Complete remaining unification items: - Add WalletType.evm support to node, wallet utils, and view models - Update EVMChainDefaultTokens and EVMChainUtils to support chainId - Fix token initialization and priority fee calculations for WalletType.evm - Update payment view model to handle WalletType.evm with chainId All identified edge cases from the unification plan are now complete. * fix: Fix network switching issues for EVM wallets - Fix null check errors when creating and restoring an EVM wallet - Fix null check errors for balance getter when switching networks - Fix fiat amount display for native currencies in transaction history * refactor: Simplify EVM wallet chain selection in CryptoBalanceWidget - Removed the old dropdown for chain selection and replaced it with a more integrated EvmSwitcher dialog. - Updated EvmSwitcher to accept a list of available chains and handle chain selection more efficiently. - Enhanced EvmSwitcherDataItem to include chainId for better identification of chains. - Improved the overall UI flow for EVM wallet interactions. * refactor: Enhance EVM wallet handling and chainId integration - Simplified chainId checks across various components to ensure consistent handling for WalletType.evm. - Updated EVMChainDefaultTokens and EVMChainUtils checks to allow all EVM wallets for chainId operations - Handled backward compatibility for older wallet types in transaction info * refactor: More enhancement and backward compatibility fixes for EVM Wallet - Introduced a new method to retrieve cryptocurrency by chainId, improving the handling of WalletType.evm. - Updated transaction history management to filter transactions based on the current chainId - Refactored EVMChainTransactionInfo to accept chainId directly - Modified utility functions in EVMChainUtils and TokenUtilities for better chainId handling * refactor: Streamline EVM token retrieval and enhance chainId handling * feat: Introduce comprehensive guide for adding new EVM L2 networks - Added a detailed documentation file outlining the steps to integrate new EVM-compatible L2 networks into Cake Wallet. - Included prerequisites, architecture overview, and a step-by-step guide covering chain configuration, native currency addition, default tokens, and node setup. - Emphasized the unified architecture for EVM chains and backward compatibility considerations. - Removed the outdated guide on adding new L2 networks to streamline documentation. * feat: Reposition EVM switcher button on dashboard page * just adding reminders for the refactoring [skip ci] * more todos [skip ci] * feat: enhance wallet functionality with chainId support - Added chainId to `WalletBase` and updated currency method to use it. - Modified EvmChainRegistry to initialize registry on call. - Updated various components to pass chainId where necessary for currency conversions. - Improved handling of chain IDs in wallet-related view models and UI components for better compatibility with EVM networks. * fix: remove default chainId fallback in payment confirmation widget * feat: Adapt anypay to new unified flow WIP * fix: Issues with handling non evm swaps for anypay * fixes to flow structure * Add persistence to hidden chains on switcher * refactor: simplify equality operator in Erc20Token class * feat: add excluded arbutrum tokens for chainflip * chore: Remove print statement [skip ci] * refactor: dispaly QR image display for evm based on selected chain ID * feat: enhance EVM wallet compatibility check for raw address input * chore: Update doc to reflect changes [skip ci] * feat: Integrate Blink for EVM wallets for anti-sandwich attacks. - Add toggle in privacy settings to enable/disable - When enabled, route all evm broadcast rpc calls via Blink * feat: Integrate Blink for EVM wallets for anti-sandwich attacks. - Add toggle in privacy settings to enable/disable - When enabled, route all evm broadcast rpc calls via Blink * feat: Integrate Blink for EVM wallets for anti-sandwich attacks. - Add toggle in privacy settings to enable/disable - When enabled, route all evm broadcast rpc calls via Blink * feat: Handle same chainId scenario for pasted evm addresses * feat: Add Blink Setting to Advanced Settings Page * fix: Handle Blink supported chains * fix: Fixes to evm swap * fix: Add fallback for currency fetch * fix: Add evm to supported list * fix: Better handle balance errors * fix: Update token check to use contract address comparison, resolves the usdt/usdt0 issue * refactor: Simplify checks by cearing all tokens on chain switch, removing unnecessary currency checks * fix: Simplify checks based from feedback on review * fix: Exclude arbitrum from tx priority using chainId * refactor: Handle chains not having transaction priority within refactor * fix: Unified evm wallet fixes - Handle switching networks while on tx history page - Update NFTs when network is switching on listing page - Fix arbitrum tx priority error on swap screen - Switch check to display deuro from wallet type to chainId - Add fallback check for getting token info for evm - Better error message for max fee per gas less than block base fee error * refactor: Update wallet checks from type to chainId for EVM compatibility checks * refactor: Update eth chain utils and remove verified contract criteria in scam detection checks * refactor: Enhance validation for deuro calls * fix : Remove unneeded validation check for savings edit * feat: Hide EVM chain switcher behind feature flag * refactor: Remove EVM wallet type references and update related logic * chore: Clean up wallet type references and remove repeated imports * feat: Add fallback for derivation path and add arbitrum support to app scripts * fix: NFT popup error * fix: Transaction details fee regression * fix: Fixes and improvements - Add nonce parameter to transaction methods in EVM clients - Remove brackets for create swaps - Display contacts for selected wallettype alone - Remove tx priorty switching for arb * feat: Enhance Arbitrum support across swap providers * fix: Add missing secret keys * fix: display sending currency icon in send from external page * refactor: Handle arbitrum edgecase for wallet list display image * fix: Handle more cases of arbitrum image display for contact book * exploring possible issues with tx history * chore: switch to printV * fix: Add an extra check for keys * fix: Filter out eth spam transaction and modify parsing format * fix: Handle contract decimals for sending tokens * fix: Display proper balance error * Update cw_evm/lib/utils/evm_chain_formatter.dart [skip ci] --------- Co-authored-by: Robert Malikowski <malikowskirobert@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> |
||
|
|
3756d46ad4 | chore: don't default to installing system files in scripts (#2592) | ||
|
|
75ca159d2c | ci: use prebuilt monero_c on CI builds (#2569) | ||
|
|
e4726c2242 |
fix: initial 16KB pages support (#2523)
* fix: initial 16KB pages support all .so dependencies other than reown walletkit (`libuniffi_yttrium.so`) which is a prebuild are 16KB now * chore: update flutter to 3.29.3 * build: 16KB align go builds ci: update action to check for 16KB align [skip ci] * chore: bump flutter to 3.32.0 * fix: replace prebuilds with built versions for 16KB alignment fix: walletkit secure storage fix |
||
|
|
2dc95b2bf0 |
feat: use better embedded Tor (#2453)
* feat: use better embedded Tor * update docs [skip ci] * fix: update torch properly add minimum os version for iOS * fix: use of invalid thread api on iOS * fix: disable use of lzma and zstd system libraries * fix: update torch_dart to work on linux |
||
|
|
e4806ad141 |
CW948: feat: bitcoin view-only (#2261)
* feat: bitcoin view-only * fix: add UR to cw_bitcoin, fix payjoin issue * feat: add unsignedPsbt to PendingBitcoinTransaction * wip * feat: xpub restore from bitcoin URI QR * feat(dev): debug UR codes [skip ci] * fix: master fingerprint default in create bitcoin transaction * fix: handle empty private key in bitcoin transaction building * fix: show success dialog after btc ur transaction * fix(ui): remove SP from UI in view only wallet * fix(cw_bitcoin): tx sending on non-airgap * fix(cw_bitcoin): disable all non-segwit addresses * feat(ur): support BBQR for coldcard qr * flutter 3.27.0 -> 3.27.4 * feat: raw xpub qr support for coldcard q * fix: update ledger_bitcoin fix: update switching button * fix: restore button not showing up after scanning * feat: commit raw tx (from coldcard Q) * feat: legacy crypto-psbt support for seed signer * chore: address comments from review * fix(cw_bitcoin): disable sp and payjoin for cupcake * feat: enable DNSSEC BIP353 in PSBT (#2397) * feat: enable DNSSEC BIP353 in PSBT * chore: put bitcoin view only behind a featureflag * fix: update Monero wallet to work with new internal URQR return format * feat: add "Restore from Cupcake App" option with localized strings * feat: enable DNSSEC BIP353 in PSBT using dnssec-prover (#2401) * fix: use dnssec-prover chore: bump ndk to r28c * fix: validate DNSSEC proof before updating the UI * fix: rename button to copy debug info to not hide actions on URQR widget --------- Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> |
||
|
|
8457a45c2a |
update-payjoin (#2281)
* feat: upgrade to flutter_payjoin 0.23.0 * fix: dependency discrepancy freezed_annotation * feat(cw_bitcoin): use latest payjoin deps * ci: update rust toolchain * Update pubspec.yaml * trial: downgrade flutter payjoin ffi * some checks * trial 2 * [skip ci] use correct image for CI * fix: bump flutter sdk to 3.29.0 * partial revert --------- Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> |
||
|
|
eab91de9f6 |
CW-1020 arm64 linux support (#2110)
* arm64 linux support * add missing dependencies to Dockerfile * run everything in docker to not break permissions * remove current linux dir * proper directory name * Clean up build script by removing .flatpak-builder directory flatpak before build process * account for arm64 in docs [skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> |
||
|
|
102ab8dbe6 |
CW-875 BackupServiceV3 (#2064)
* implement v3 backup system - Add new BackupServiceV3 with chunked backup support and checksums - Implement json-based metadata in backup system - Instead of binary file export a .zip file that user can open and see that it is a cake backup (should also prevent 3rd party software from corrupting binary data inside of the file, in case it doesn't checksum will fail, and user will know for sure that backup got corrupted) - Update flutter to 3.27.4 to use archive ^4.x.x (it offers in memory archive operations) * fix wallets not getting restored properly * prevent out of memory errors on files that are way too big during restore * Update lib/view_model/backup_view_model.dart [skip ci] * Update lib/core/backup_service.dart [skip ci] * Update lib/core/backup_service.dart --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> |
||
|
|
a085eff984 | add decred to docs (#2105) | ||
|
|
1cb8651ffc |
Improve build docs and optimize Dockerfile for Android and Linux builds (#2016)
* Update build docs and migrate Android builds to Docker * Update NDK and move steps directly into Dockerfile * Fix NDK installation via script and Dockerfile * Migrate to @MrCyjaneK's existing Dockerfile (with optimizations) * Add .dockerignore and migrate Dockerfile to root of project * Revert .gitignore changes * Update Android and Linux builds and resolve Linux build issue * Fix git repo for Linux and Android build instructions * Set branch to latest release in build docs * Ensure `flutter clean` is run before building * Fix Linux completion example * Don't build Zano for Linux * Apply suggestions from code review Co-authored-by: cyan <cyjan@mrcyjanek.net> * Explicitly add automake package to Dockerfile * Improve logging on Android and Linux builds via Docker * Improve Dockerfile comments and ordering * Fix issues in macOS and iOS builds docs (thanks @MrCyjaneK) * Update docs/builds/IOS.md [skip ci] * Update docs/builds/IOS.md [skip ci] * Update docs/builds/MACOS.md [skip ci] * Update docs/builds/IOS.md [skip ci] * Fix and improve iOS and macOS build docs * Windows build doc improvements Co-authored-by: cyan <cyjan@mrcyjanek.net> * add missing dependency to Dockerfile * Update Windows build docs * More fixes and optimizations to the Windows build docs * Add git config to Windows build doc * Fix shell location/commands in Windows build doc * Fix WSL commands in Windows build doc * Apply suggestions from code review Co-authored-by: cyan <cyjan@mrcyjanek.net> --------- Co-authored-by: cyan <cyjan@mrcyjanek.net> |