{
 "number": 35511,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35511",
 "title": "RFC: consensus: Make `CAmount` a class",
 "author": "hodlinator",
 "author_association": "MEMBER",
 "created_at": "2026-06-11T10:06:17Z",
 "updated_at": "2026-09-16T23:35:19Z",
 "age_days": 98,
 "draft": true,
 "labels": [],
 "milestone": null,
 "base": "master",
 "head_sha": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
 "head_ref": "2026/06/safer_camount.take2",
 "head_repo": "hodlinator/bitcoin",
 "head_history": [
  {
   "t": "2026-06-11T18:11:27Z",
   "sha": "6b8a616b60541c77120525f74ebb11e422373a44"
  },
  {
   "t": "2026-06-17T13:40:07Z",
   "sha": "2488d44b8dd3bcc8c848b3a61d257ead805c37b7"
  },
  {
   "t": "2026-06-18T11:46:18Z",
   "sha": "de528ab5c400855a85b62904441ffbb850953107"
  },
  {
   "t": "2026-06-18T13:28:25Z",
   "sha": "bebcceca04bffc9be078d68ebd4b987cdd06d10a"
  },
  {
   "t": "2026-06-18T20:35:01Z",
   "sha": "cc90782e0cfa602d2b312f0d669d47a7d3d4dd37"
  },
  {
   "t": "2026-07-01T12:18:34Z",
   "sha": "9c11263d4148f0f8369875ae1acb00b82ff33538"
  },
  {
   "t": "2026-07-12T08:58:47Z",
   "sha": "4bcb9af26bd6d125f82a0d0c63008c540a18b644"
  },
  {
   "t": "2026-07-12T09:15:17Z",
   "sha": "df4b4fc56e021e7e8abc6bc6b2bfb8b0c22b0c4d"
  },
  {
   "t": "2026-08-07T12:47:31Z",
   "sha": "00704f26ba2949432697312431dd6d8b7c41d0af"
  },
  {
   "t": "2026-08-07T13:27:01Z",
   "sha": "68b9fd26fcae7dd4cb87b272ce330c10c7d6e34c"
  },
  {
   "t": "2026-08-25T06:44:43Z",
   "sha": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f"
  }
 ],
 "additions": 1486,
 "deletions": 1258,
 "changed_files": 184,
 "commit_count": 12,
 "size_bucket": "XL",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "sedited",
      "url": "https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4679584491"
     },
     {
      "login": "ryanofsky",
      "url": "https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4712614640"
     }
    ]
   },
   "conflicts": [
    {
     "number": 36255,
     "title": "Refactor CoinGrinder tests",
     "author": "murchandamus"
    },
    {
     "number": 36188,
     "title": "crypto: plug hardware optimized SHA256 into libsecp256k1's context",
     "author": "furszy"
    },
    {
     "number": 36182,
     "title": "fees: return `block_policy` fee rate estimate when `mempool_policy` is not ready",
     "author": "ismaelsadeeq"
    },
    {
     "number": 36097,
     "title": "mining: replace interrupt methods with cancellation arguments",
     "author": "xyzconstant"
    },
    {
     "number": 36091,
     "title": "test: Add debug output to common tested types",
     "author": "rustaceanrob"
    },
    {
     "number": 36068,
     "title": "fuzz: reuse one fuzzed wallet across inputs",
     "author": "brunoerg"
    },
    {
     "number": 36066,
     "title": "validation: Separate check-only version of ConnectBlock",
     "author": "optout21"
    },
    {
     "number": 35997,
     "title": "refactor: bench: make_hard_case to find a solution",
     "author": "yancyribbens"
    },
    {
     "number": 35919,
     "title": "p2p: avoid orphanage abort at high peer counts",
     "author": "l0rinc"
    },
    {
     "number": 35793,
     "title": "Implement BIP 54 (Consensus Cleanup) without mainnet activation",
     "author": "darosior"
    },
    {
     "number": 35751,
     "title": "validation: use parallel input prevout fetching in TestBlockValidity",
     "author": "andrewtoth"
    },
    {
     "number": 35716,
     "title": "wallet: Replace mapWallet and wtxOrdered with a boost::multi_index",
     "author": "achow101"
    },
    {
     "number": 35713,
     "title": "Remove boost as a unit test runner",
     "author": "rustaceanrob"
    },
    {
     "number": 35675,
     "title": "mining: add block template manager",
     "author": "ismaelsadeeq"
    },
    {
     "number": 35662,
     "title": "script: prevent stale sighash caches across transactions",
     "author": "l0rinc"
    },
    {
     "number": 35641,
     "title": "kernel: Add script evaluation tracer",
     "author": "sedited"
    },
    {
     "number": 35581,
     "title": "node: add block template manager and track waitNext fee inflow",
     "author": "ismaelsadeeq"
    },
    {
     "number": 35569,
     "title": "Encapsulation for CTransaction",
     "author": "purpleKarrot"
    },
    {
     "number": 35557,
     "title": "kernel, validation: Add btck_chainstate_manager_set_clock_time",
     "author": "ryanofsky"
    },
    {
     "number": 34844,
     "title": "util: Add util::NotNull<SmartPtrType>",
     "author": "maflcko"
    },
    {
     "number": 34824,
     "title": "net: encapsulate TxRelay state and replace recursive mutexes",
     "author": "w0xlt"
    },
    {
     "number": 34698,
     "title": "wallet: handle MiniMiner bump fee calculation failures",
     "author": "shuv-amp"
    },
    {
     "number": 33922,
     "title": "mining: add getMemoryLoad() and track template non-mempool memory footprint",
     "author": "Sjors"
    },
    {
     "number": 33741,
     "title": "rpc: Optionally print feerates in sat/vb",
     "author": "polespinasa"
    },
    {
     "number": 32387,
     "title": "ipc: add windows support",
     "author": "ryanofsky"
    },
    {
     "number": 29700,
     "title": "kernel, refactor: return error status on all fatal errors",
     "author": "ryanofsky"
    },
    {
     "number": 29491,
     "title": "[EXPERIMENTAL] Schnorr batch verification for blocks",
     "author": "fjahr"
    },
    {
     "number": 29409,
     "title": "multiprocess: Add capnp wrapper for Chain interface",
     "author": "ryanofsky"
    },
    {
     "number": 29278,
     "title": "Wallet:  Add `maxfeerate` wallet startup option",
     "author": "ismaelsadeeq"
    },
    {
     "number": 27865,
     "title": "wallet: Track no-longer-spendable TXOs separately",
     "author": "achow101"
    },
    {
     "number": 19461,
     "title": "multiprocess: Add bitcoin-gui -ipcconnect option",
     "author": "ryanofsky"
    },
    {
     "number": 19460,
     "title": "multiprocess: Add bitcoin-wallet -ipcconnect option",
     "author": "ryanofsky"
    },
    {
     "number": 10102,
     "title": "Multiprocess bitcoin",
     "author": "ryanofsky"
    }
   ]
  }
 },
 "acks_parsed": {
  "sedited": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-06-11T10:26:58Z",
   "stale": false
  },
  "ryanofsky": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-06-15T21:30:44Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 2,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 1,
  "distinct_reviewers": [
   "Sjors",
   "janb84",
   "l0rinc",
   "maflcko",
   "optout21",
   "purpleKarrot",
   "ryanofsky",
   "sedited"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-08-25T07:40:18Z",
  "last_reviewer_activity": "2026-06-29T19:29:17Z",
  "last_reviewer": "l0rinc",
  "author_silent_days": 23,
  "waiting_on_author_days": 0,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [
   4067,
   5430,
   35372,
   35456
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 35456,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-06-08",
    "title": "test: Perform full reset of CoinsResult in order to avoid passing 21M BTC"
   },
   {
    "number": 35372,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-05-27",
    "title": "refactor: Enhance type safety in overflow operations"
   },
   {
    "number": 5430,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "CAmount: Small improvements related to CAmount"
   },
   {
    "number": 4067,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "Encapsulate coin balances within a new CMoney type."
   }
  ],
  "conflicts": [
   36255,
   36188,
   36182,
   36097,
   36091,
   36068,
   36066,
   35997,
   35919,
   35793,
   35751,
   35716,
   35713,
   35675,
   35662,
   35641,
   35581,
   35569,
   35557,
   34844,
   34824,
   34698,
   33922,
   33741,
   32387,
   29700,
   29491,
   29409,
   29278,
   27865,
   19461,
   19460,
   10102
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/consensus/amount.h",
  "src/ipc/capnp/common-types.h",
  "src/node/mempool_persist.cpp",
  "src/serialize.h",
  "src/txmempool.cpp",
  "src/util/moneystr.cpp",
  "src/wallet/test/coinselector_tests.cpp",
  "src/wallet/test/fuzz/coinselection.cpp",
  "src/wallet/wallet.cpp"
 ],
 "body": "We should treat people's money with care across node and wallet.\n\nHaving our `CAmount` be a `typedef` for raw `int64_t` as on master has the following drawbacks:\n* Allows assigning to from `bool`s, leading to the issues like the one previously fixed by 0026b330c4abbbbdb96e4f0c4d380d70d8e592ab.\n* Allows direct comparison with integral types of differing size, leading to things like the issue previously fixed by 890a09b1e49925315a5636f29cc5af36928fe092 (reverting it on top of this PR leads to compile errors).\n* Allows forgetting to initialize amounts (`CAmount fee;`).\n* Allows direct operations with imprecise decimal types as in `CAmount target = 49.5L * COIN` in src/wallet/test/coinselector_tests.cpp.\n* Allows *multiplying* two `CAmount`s with each other, which does not make sense.\n\nBeyond fixing the above, this PR also makes amounts stick out more in code - either by the `CAmount` type itself or by using the `sats` unit specifier.\n\nThis work is also a stepping stone for possible future work introducing an even safer amount type which only allows values in the range `[0, MAX_MONEY]`.\n\nAlready merged PRs spawned out of this work: #35456, #35372\n\nPrior attempts in similar directions: #5430, #4067",
 "commits": [
  {
   "sha": "84ce94fbfe9fc0fb53eb08e721217b379e96e0be",
   "date": "2026-08-24T15:00:48Z",
   "message": "fuzz: Add ConsumeMoney() overloads with support for specifying minimum"
  },
  {
   "sha": "9a0627d77c2e3293b0b6589531fd78ea46798c7a",
   "date": "2026-08-24T15:00:48Z",
   "message": "ipc: Add coverage for passing CAmount\n\nCo-authored-by: janb84 <githubjanb.drainer976@passmail.net>"
  },
  {
   "sha": "ee9163b96d14853603935e45f65d35cc332d0e81",
   "date": "2026-08-24T15:00:48Z",
   "message": "test: Add BTC unit to coinselector_tests.cpp\n\nThis allows for later banning of multiplying amount with floating point types.\n\nUses a \"unit type\" to multiply with along the lines of the mp_units library http://wg21.link/p3045, instead of a _BTC user-defined literal."
  },
  {
   "sha": "e8819053726640787b35bbbc5f0d93ed94889eef",
   "date": "2026-08-24T15:00:48Z",
   "message": "consensus: Make CAmount a class\n\nNo longer allows implicit conversions from non-integer types such as floats or bools.\n\nWould prevent issues such as the one fixed by 0026b330c4abbbbdb96e4f0c4d380d70d8e592ab."
  },
  {
   "sha": "8594553ac6ce01096e15563d96d89549060e610e",
   "date": "2026-08-24T15:00:48Z",
   "message": "scripted-diff: Explicitly initialize CAmounts before explicit initialization becomes required\n\n-BEGIN VERIFY SCRIPT-\nsed -i -E \"s/(^\\s+CAmount )(\\w+);/\\1\\2{0};/g\" $( git grep -lE \"^\\s+CAmount \\w+;\" -- src/ ':!src/primitives/transaction.h' )\n-END VERIFY SCRIPT-"
  },
  {
   "sha": "da36dff4887c3f7255def88eb7737d7afdc34d45",
   "date": "2026-08-24T15:00:48Z",
   "message": "refactor(mempool): Return nullopt instead of default-initialized TxMempoolInfo\n\nPreparation for CAmount not having default ctor."
  },
  {
   "sha": "c59e914994857fb08e2b11b29fb9ced76b34e303",
   "date": "2026-08-24T15:00:48Z",
   "message": "consensus: Require initialization of CAmount"
  },
  {
   "sha": "b42e9484d0a9ab0b69c026b6ed357cee16072bfa",
   "date": "2026-08-24T17:01:13Z",
   "message": "scripted-diff: Prepare for CAmount ctor becoming explicit\n\n-BEGIN VERIFY SCRIPT-\nsed -i -E \"s/(\\s+CAmount )(\\w+) = (-?[0-9]+);/\\1\\2{\\3};/g\" $( git grep -lE \"CAmount \\w+ = -?[0-9]+;\" src/ )\nsed -i -E \"s/CFeeRate([\\(\\{])((-?[0-9\\'])+)([\\)\\}])/CFeeRate\\1CAmount{\\2}\\4/g\" $( git grep -lE \"CFeeRate[\\(\\{]-?[0-9\\']+[\\)\\}]\" src/ )\nsed -i -E \"s/\\.nValue = ([a-zA-Z_]+ - )?([0-9]+[0-9L]*([ \\-]*[0-9]+(LL)?)*)([^\\*0-9])/.nValue = \\1CAmount{\\2}\\5/g\" src/test/miner_tests.cpp src/test/transaction_tests.cpp src/test/script_p2sh_tests.cpp\nsed -i -E \"s/([\\(\\{])(-?(0x)?[0-9a-f]+), (-?\\w+)([\\)\\}])/\\1CAmount{\\2}, \\4\\5/g\" src/test/feefrac_tests.cpp src/test/rbf_tests.cpp\n-END VERIFY SCRIPT-"
  },
  {
   "sha": "523a8fcb681a3ecdf942676874e85913e7a4dd70",
   "date": "2026-08-24T17:22:32Z",
   "message": "refactor: Manual explicit initializations of CAmount"
  },
  {
   "sha": "ecfa1bd93d8bd3a9c5105ae32f9dbeaacf87d93c",
   "date": "2026-08-24T17:23:48Z",
   "message": "consensus: Make CAmount(T) constructor explicit"
  },
  {
   "sha": "16e7ba8f3ce14e34b95e4af1ac3e7ead69acfc79",
   "date": "2026-08-24T19:34:46Z",
   "message": "consensus: Add sats unit"
  },
  {
   "sha": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
   "date": "2026-08-24T19:34:46Z",
   "message": "scripted-diff: Apply sats unit\n\n-BEGIN VERIFY SCRIPT-\nsed -i -E \"s/CAmount\\{(-?[0-9\\']+)\\}/\\1*sats/g\" $( git grep -lE \"CAmount\\{-?[0-9\\']+\\}\" src/ )\n-END VERIFY SCRIPT-"
  }
 ],
 "timeline": [
  {
   "t": "2026-06-11T10:26:58Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "Concept ACK"
  },
  {
   "t": "2026-06-11T10:32:16Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/consensus/amount.h",
   "commit": "6b8a616b60541c77120525f74ebb11e422373a44",
   "in_reply_to": null,
   "text": "[quoted text omitted]\n\n... guess this will finally put an end to the sats-vs-bits debate ..."
  },
  {
   "t": "2026-06-11T10:35:17Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/consensus/amount.h",
   "commit": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
   "in_reply_to": null,
   "text": "Do we need to keep the Hungarian notation `C` prefix?"
  },
  {
   "t": "2026-06-11T10:35:35Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/consensus/amount.h",
   "commit": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
   "in_reply_to": null,
   "text": "What are the expected use cases for dividing one `CAmount` by another? What is the expected outcome of 10_sats / 3_sats? Since this returns a scalar ratio rather than another amount, would it be clearer to avoid exposing this as a generic operator and keep the few ratio-style uses explicit?"
  },
  {
   "t": "2026-06-11T13:17:39Z",
   "kind": "review_comment",
   "who": "janb84",
   "assoc": "MEMBER",
   "path": "src/ipc/capnp/common-types.h",
   "commit": "c14d67a309c975360467dbf63d84556fe832d602",
   "in_reply_to": null,
   "text": "```suggestion\n    output.set(value.Int());\n    // OR output.set(static_cast<CAmount::inner_type>(value.Int()));\n```\n\nCurrent code gets a local copy, overrides the local copy and local copy gets destroyed at the end of the function. Nothing is written back to the Cap'n Proto message.\n\nRegression test:\n\n```diff\ndiff --git a/src/ipc/test/ipc_test.capnp b/src/ipc/test/ipc_test.capnp\n@@ -15,6 +15,7 @@ using Mining = import \"../capnp/mining.capnp\";\n\n interface FooInterface $Proxy.wrap(\"FooImplementation\") {\n     add @0 (a :Int32, b :Int32) -> (result :Int32);\n+    passAmounts @6 (arg :List(Int64)) -> (result :List(Int64));\n     passOutPoint @1 (arg :Data) -> (result :Data);\n     passUniValue @2 (arg :Text) -> (result :Text);\n     passTransaction @3 (arg :Data) -> (result :Data);\n\ndiff --git a/src/ipc/test/ipc_test.h b/src/ipc/test/ipc_test.h\n@@ -15,6 +15,7 @@ class FooImplementation\n {\n public:\n     int add(int a, int b) { return a + b; }\n+    std::vector<CAmount> passAmounts(std::vector<CAmount> v) { return v; }\n     COutPoint passOutPoint(COutPoint o) { return o; }\n     UniValue passUniValue(UniValue v) { return v; }\n     CTransactionRef passTransaction(CTransactionRef t) { return t; }\n\ndiff --git a/src/ipc/test/ipc_test.cpp b/src/ipc/test/ipc_test.cpp\n@@ -84,6 +84,15 @@ void IpcPipeTest()\n     // Test: make sure arguments were sent and return value is received\n     BOOST_CHECK_EQUAL(foo->add(1, 2), 3);\n\n+    // Test for CustomBuildField / CustomReadField overloads for\n+    //  CAmount in ipc/capnp/common-types.h. Each value is serialized\n+    //  once as the argument and once as the return value, so\n+    // a build field that fails to write its value shows up as a zeroed result.\n+    const std::vector<CAmount> amounts1{0_sats, 1_sats, COIN, MAX_MONEY, CAmount{-1}};\n+    const std::vector<CAmount> amounts2{foo->passAmounts(amounts1)};\n+    BOOST_CHECK_EQUAL_COLLECTIONS(amounts1.begin(), amounts1.end(), amounts2.begin(), amounts2.end());\n+\n     COutPoint txout1{Txid::FromUint256(uint256{100}), 200};\n     COutPoint txout2{foo->passOutPoint(txout1)};\n     BOOST_CHECK(txout1 == txout2);\n```"
  },
  {
   "t": "2026-06-11T15:57:51Z",
   "kind": "review_comment",
   "who": "janb84",
   "assoc": "MEMBER",
   "path": "src/node/mempool_persist.cpp",
   "commit": "c14d67a309c975360467dbf63d84556fe832d602",
   "in_reply_to": null,
   "text": "```suggestion\n\n```\n\nNIT: Change suggestion isn't helpful but now that we have new Serialization/deserialization, deserializes straight into a `CAmount amountdelta`. Dropping the need for the int64_t nFeeDelta temporary and the separate `CAmount amountdelta{nFeeDelta}` wrap.\n\n```cpp\n\nCAmount amountdelta{0};\nfile >> TX_WITH_WITNESS(tx);\nfile >> nTime;\nfile >> amountdelta;\n\nif (opts.use_current_time) {\nnTime = TicksSinceEpoch<std::chrono::seconds>(now);\n}\n\n```"
  },
  {
   "t": "2026-06-11T18:11:27Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "6b8a616b60541c77120525f74ebb11e422373a44"
  },
  {
   "t": "2026-06-11T18:16:49Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/ipc/capnp/common-types.h",
   "commit": "c14d67a309c975360467dbf63d84556fe832d602",
   "in_reply_to": 3396163565,
   "text": "Thanks for catching this! I'm surprised it was not covered by tests earlier. I guess transaction serialization has the `CAmount`s outputs serialized as part of a byte-stream before reaching our Cap'n Proto integration layer. Added your test in a separate commit (44e5ba33d8ded7c1d72ce29321bcf839aacfb7e4) in the latest push (6b8a616b60541c77120525f74ebb11e422373a44), omitting the comment (let me know if you disagree, I think it was good context for your comment here in the PR but a bit verbose in the context of the test)."
  },
  {
   "t": "2026-06-11T18:19:10Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/node/mempool_persist.cpp",
   "commit": "c14d67a309c975360467dbf63d84556fe832d602",
   "in_reply_to": 3397350072,
   "text": "Agreed - switched in latest push (6b8a616b60541c77120525f74ebb11e422373a44) to serializing and checking `CAmount fee_delta{0};`. I think `nFeeDelta` was a more descriptive name, but altered it to match current naming style."
  },
  {
   "t": "2026-06-12T08:31:28Z",
   "kind": "review_comment",
   "who": "janb84",
   "assoc": "MEMBER",
   "path": "src/ipc/capnp/common-types.h",
   "commit": "c14d67a309c975360467dbf63d84556fe832d602",
   "in_reply_to": 3396163565,
   "text": "yeah test was for proving the bug not the intention to add it so i'm glad you altered it somewhat !"
  },
  {
   "t": "2026-06-15T21:30:44Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "Concept ACK. New [CAmount](https://github.com/hodlinator/bitcoin/blob/6b8a616b60541c77120525f74ebb11e422373a44/src/consensus/amount.h#L23-L109) implementation seems simple and clean and adds safety benefits.\n\nI haven't looked through the commits yet, but the PR seems very big. Could it make sense to split up somehow, maybe by introducing a new amount class and beginning to use that in places where it's most important, and gradually replacing CAmount, instead of replacing it everywhere? Or maybe there are other ways to split it up? Or maybe it is trivial to review and splitting it up would not be helpful? Curious what you think. This is just larger than I might have expected."
  },
  {
   "t": "2026-06-16T06:27:00Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/consensus/amount.h",
   "commit": "6b8a616b60541c77120525f74ebb11e422373a44",
   "in_reply_to": null,
   "text": "q: Shouldn't this check against MAX_MONEY?"
  },
  {
   "t": "2026-06-16T06:27:35Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/consensus/amount.h",
   "commit": "6b8a616b60541c77120525f74ebb11e422373a44",
   "in_reply_to": null,
   "text": "Either mark with LIFETIMEBOUND, or just return a copy, which is usually not slower anyway?"
  },
  {
   "t": "2026-06-16T06:33:52Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI think the main reason why this looks large is commit c8fa9d5fbef02ccb2150448cb84ed6e9bba383ef and the two following ones. It should be trivial to split the them up and then reduce the size of this pull by two thirds. After this, it seems small enough and easy enough to do in one go."
  },
  {
   "t": "2026-06-17T08:21:27Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nIn addition to making b7af8d94d6708e50ebede9a513d006ac0031606c a followup or earlier PR (to introduce the _sats literal), it could be a scripted diff? The script is already in the commit description, but then CI can check it too."
  },
  {
   "t": "2026-06-17T12:30:20Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nNice. I'd be ok with reviewing this all together, but IMO it would be good to introduce the `_sats` suffix as a standalone readability improvement so this PR is smaller and just focused on safety issues, which seem more subtle."
  },
  {
   "t": "2026-06-17T13:40:07Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "2488d44b8dd3bcc8c848b3a61d257ead805c37b7"
  },
  {
   "t": "2026-06-17T13:42:25Z",
   "kind": "comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "text": "Thanks for the encouraging feedback & ideas to facilitate review!\nMigrating to a new class gradually is worth at least keeping in the back-pocket (https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4712614640).\nAgree with https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4715661184 that peeling off the beginning half of this PR up until before `CAmount` starts becoming an actual class could make sense (everything before \"ipc: Add coverage for passing CAmount\"?).\n\nRegarding https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4727632236, making a scripted diff commit out of the largest one (\"refactor: Apply _sats literals (trivial)\"):\nIn that commit I'm preemptively manually inserting `_sats` in places which will otherwise no longer compile due to later commits. Outside of some kind of heavily C++ AST-aware Clang-integrated tooling, I don't see a way of doing that in a scripted fashion. Maybe there is some creative way of running the sed-script I have in reverse... I just cannot come up with it now, but I'm open to suggestions."
  },
  {
   "t": "2026-06-17T13:48:01Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "Ah, it is an inverse scripted diff. In theory (after https://github.com/bitcoin/bitcoin/pull/35547) you could do an empty scripted diff in the next commit with:\n\n* Your sed command on ./src\n* git --no-pager diff --exit-code --quiet HEAD~\n* git checkout -- ./src\n\nNot sure if that is too spicy."
  },
  {
   "t": "2026-06-18T09:16:47Z",
   "kind": "comment",
   "who": "optout21",
   "assoc": "CONTRIBUTOR",
   "text": "A strong like for the concept of `CAmount` as a class rather than a simple typedef.\n\nThese properties make a lot of sense:\n- constructible from a numerical value\n- convertible to to a numerical value, but only explicitly\n- automatic extra sanity checks (e.g. max)\n\nHowever, to be constructible only from `_sats` literals, and to be comparable only against other `CAmount` seems a bit of an overkill to me, and it hinders introduction (large change). An amount is a special kind of number, but it is a number, and I don't see the benefit of abstracting away that fact that it is a number.\n\nI would be happier to see a more gradual introduction:\n- constructible from a number (with internal max check)\n- using conversion to number for comparisons, computations (i.e., `amount.Int() < 1000` instead of `amount < 1000_sat`\n- for an even more gradual introduction, as an intermediary step, the conversion to number can be automatic\n- forcing to exclusive comparison and arithmetics (as proposed currently) could be done as a later step.\n\nAlso, I'm a bit uneasy with the allowed negative values. Maybe such a change would be a good time to separate absolute amounts and amount differences, with the former being strictly non-negative (e.g. `CAmount` andd `CAmountDelta`; an analogue would be time points and time differences). I suspect for most of the usages, non-negative absolute amount would fit."
  },
  {
   "t": "2026-06-18T11:45:46Z",
   "kind": "comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "text": "Thanks for your feedback @optout21!\n\n[quoted text omitted]\nOne of my first experiments in this area was having distinct `Amount` (signed) and `UAmount` (unsigned) types (older branch: https://github.com/hodlinator/bitcoin/tree/2026/03/safer_camount).\n\nAfter presenting this experiment at https://btctranscripts.com/bitcoin-core-dev-tech/2026-05/camount I got feedback about the pitfalls of unsigned integers (mixing signed with unsigned in the same expression makes the result unsigned, which might result in unintentional conversion from negative values to positive).\n\nThis made me change my approach to only keep signed amounts, but to experiment with having an amount type which only allows [0,21M BTC] in addition to a type which allows any `int64_t` value. A first attempt in this direction is made here: https://github.com/hodlinator/bitcoin/tree/2026/06/safer_camount.take2.unchecked. But will probably go with a more careful approach as described in the final commit message.\n\n[quoted text omitted]\nIf we were going to introduce the requirement to use `.Int()` everywhere we might as well instead introduce the literal directly?\n\n[quoted text omitted]\nI briefly experimented with having an `operator int64_t()` method and allowing comparisons against integers. What I ran into a lot was that the compiler couldn't resolve the resulting ambiguities. Maybe there's a way to resolve those ambiguities, but my impression from earlier feedback is that the current approach without that intermediate step may be acceptable."
  },
  {
   "t": "2026-06-18T11:46:18Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "de528ab5c400855a85b62904441ffbb850953107"
  },
  {
   "t": "2026-06-18T13:28:25Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "bebcceca04bffc9be078d68ebd4b987cdd06d10a"
  },
  {
   "t": "2026-06-18T20:35:01Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "cc90782e0cfa602d2b312f0d669d47a7d3d4dd37"
  },
  {
   "t": "2026-06-22T06:30:40Z",
   "kind": "comment",
   "who": "purpleKarrot",
   "assoc": "MEMBER",
   "text": "It might be worth having a look into the crystal ball. If [P3045](http://wg21.link/p3045) gets accepted into the C++ standard in one form or another, then our preferred way of defining amount might become something like this:\n\n```cpp\nusing Amount = std::quantity<units::satoshi, std::int64_t>;\n```\n\nI don't want to suggest using a polyfill for `std::quantity`, but I strongly recommend having a look into the proposal linked above and see whether some of its design decisions could influence the design of a custom `Amount` class.\nFor example, there is a design decision [against user defined literals](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p3045r8.html#why-dont-we-use-udls-to-create-quantities) that contains:\n\n[quoted text omitted]\nWhich is absolutely true and convinces me to vote **against** a `_sat` UDL.\n\nI also strongly believe that whether an amount is inside a particular value range should be a **predicate** rather than an **invarant**."
  },
  {
   "t": "2026-06-22T20:35:27Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4765530904\n\n[quoted text omitted]\nThis seems to already be the case if you look at ed38d9ffa1da4cfafb17fc7977edc579e5147207. The `CAmount` class doesn't enforce any range requirements. It could be useful to have other types that enforce runtime invariants like `not_null` or boost's `safe_signed_range` type do, but this would be a separate concern. There is also a `MoneyRange` predicate already.\n\n[quoted text omitted]\nThe std::quantity proposal is interesting and seems to write units with multiplication syntax (`quantity dist1 = 80 * km;`) instead of literal suffixes. This does seem more general and could be a good idea to apply here."
  },
  {
   "t": "2026-06-23T06:26:43Z",
   "kind": "comment",
   "who": "purpleKarrot",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nTrue. Such a utility may be handy in *some* areas of the codebase, but we should keep in mind (and probably document somewhere) that there are areas where such a utility **must not be used**. A function like `validate(Block) -> bool` has **three** different outcomes:\n\n1. `true` when the block was found **valid**\n2. `false` when the block was found **invalid**\n3. An exception being thrown in case of a runtime failure, in which case the function is **unable** to determine whether the block is valid or invalid.\n\nIt is especially crucial that we are always able to distinguish between 2 and 3. That means:\n\n* A block must be allowed to represent invalid data. None of its parts should have an invariant that disallows checking it for validity.\n* Checking an invalid block must not result in runtime failure. There is no `throw` in case of consensus violations.\n* Runtime errors must not be interpreted as the block being invalid. There should be no `catch` in validation code.\n\nBut outside of consensus, yes, such a utility may be useful."
  },
  {
   "t": "2026-06-26T13:00:57Z",
   "kind": "comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "text": "Thanks for your comments @purpleKarrot & @ryanofsky! (https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4765530904, https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4772817451, https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4776362626).\n\n### User-defined literals\n\nThe future quantities & units library makes sense for use-cases mixing different quantities (radius vs distance), units (metre vs foot) and kinds (energy vs length). In our domain we are typically interested in sats, sat/vbyte and similar cost/weight unit ratios. At least one test had me define a `_BTC` literal (fe04bd335bc407c2754f332c697c1f7475f1dd17).\n\nWriting `0_sats` rather than `CAmount{0}` was nice for readability and is okay for code that is not exposed by the kernel IMO. However, my next plan is to experiment with a version of this PR which does not introduce any UDLs and instead tries to survive on explicit `CAmount`-ctors and possibly increased usage of `.Int()`.\n\n### Constraining valid ranges - `[0,21M] BTC`\n\nMy experimentation so far confirms that it would be safer to introduce constraints on amount types outside of the consensus code."
  },
  {
   "t": "2026-06-29T18:28:48Z",
   "kind": "comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "text": "re: https://github.com/bitcoin/bitcoin/pull/35511#issuecomment-4809818861\n\n[quoted text omitted]\nI think you should consider using the `* sats` multiplication syntax like `std::quantity`. It seems pretty nice, and better than literal suffixes because it can work with all integer expressions, not just integer literals. It should also be more readable and self-documenting than passing numbers without units to the CAmount constructor."
  },
  {
   "t": "2026-06-29T19:05:45Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/txmempool.cpp",
   "commit": "68b9fd26fcae7dd4cb87b272ce330c10c7d6e34c",
   "in_reply_to": null,
   "text": "alternatively we could add a `bool` operator to it (we have many other cases when we're checking for `value > 0`)"
  },
  {
   "t": "2026-06-29T19:06:59Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/serialize.h",
   "commit": "cc90782e0cfa602d2b312f0d669d47a7d3d4dd37",
   "in_reply_to": null,
   "text": "Could we rather encapsulate the serialization inside the new type?"
  },
  {
   "t": "2026-06-29T19:07:40Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "68b9fd26fcae7dd4cb87b272ce330c10c7d6e34c",
   "in_reply_to": null,
   "text": "Wouldn't it make more sense to make this optional instead of using a magic value?"
  },
  {
   "t": "2026-06-29T19:11:14Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/wallet/test/coinselector_tests.cpp",
   "commit": "68b9fd26fcae7dd4cb87b272ce330c10c7d6e34c",
   "in_reply_to": null,
   "text": "Won't this introduce unnecessary headaches around floating-point money, even if this is only test code? This was already doing something similar before, but adding a `_BTC` literal seems to make that pattern more attractive rather than less. See [5aadd4b](https://github.com/bitcoin/bitcoin/commit/5aadd4be1883386a04bef6a04e9a1142601ef7a7) and [fa2ac58](https://github.com/bitcoin/bitcoin/commit/fa2ac5881edf8d0d3f15c43f089f1831348dfae2)."
  },
  {
   "t": "2026-06-29T19:13:34Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/wallet/test/coinselector_tests.cpp",
   "commit": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
   "in_reply_to": null,
   "text": "note that `0.5 * COIN` may not be the same as `COIN / 2`"
  },
  {
   "t": "2026-06-29T19:14:41Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/wallet/test/fuzz/coinselection.cpp",
   "commit": "cc90782e0cfa602d2b312f0d669d47a7d3d4dd37",
   "in_reply_to": null,
   "text": "why do we have `CAmount{1}` here instead of `1_sats`"
  },
  {
   "t": "2026-06-29T19:15:34Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/util/moneystr.cpp",
   "commit": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
   "in_reply_to": null,
   "text": "seems contrived to do math with the new types, wouldn't it make a lot more sense to extract the value, do the math and reparse at the end?"
  },
  {
   "t": "2026-06-29T19:29:17Z",
   "kind": "review",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "state": "CHANGES_REQUESTED",
   "commit": "cc90782e0cfa602d2b312f0d669d47a7d3d4dd37",
   "text": "As mentioned by others, I am also not comfortable with taking such a large change in one step. I would prefer introducing a new amount type first, with a few focused examples, and then converting call sites gradually. I also think `_sats`, `* sats`, and explicit `CAmount{}` construction should be debated independently.\n\nI share the concern around value-range semantics. If the goal is that an amount object is always valid once constructed, then the boundary needs to be explicit: parsing should probably produce an unchecked/raw amount first, and validation should then finalize it into a bounded monetary amount, for example through a factory returning `std::optional<MoneyAmount>` or `util::Result<MoneyAmount>`. If negative values are needed, they may deserve a separate `MoneyDelta` type instead of being folded into the same bounded money type.\n\nOne area I would like clarified is amount compression and serialization. After this change, `AmountCompression` still appears to deserialize compressed amounts into `CAmount` without enforcing `MoneyRange`. Is that intentional and byte-for-byte compatible with the current behavior? Several fuzz targets in `src/test/fuzz/deserialize.cpp` can already construct invalid monetary values, and coins/undo data previously seemed to treat a successful parse as sufficient, as far as I can tell."
  },
  {
   "t": "2026-06-30T14:27:25Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/wallet/test/coinselector_tests.cpp",
   "commit": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
   "in_reply_to": 3494129197,
   "text": "`0.5 * COIN` on master is `double * int64_t` which somehow ends up being sent into a `int64_t` parameter. Making `COIN` a typed amount and disallowing implicit `double` operations is a step in the right direction I think.\n(We also know that 100'000'000 is divisible by 2 without any remainder)."
  },
  {
   "t": "2026-07-01T07:43:52Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/wallet/test/fuzz/coinselection.cpp",
   "commit": "cc90782e0cfa602d2b312f0d669d47a7d3d4dd37",
   "in_reply_to": 3494135689,
   "text": "Good point, it's inconsistent with the similar case around L177."
  },
  {
   "t": "2026-07-01T07:48:19Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/util/moneystr.cpp",
   "commit": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
   "in_reply_to": 3494141474,
   "text": "The idea is for `CAmount` to not implicitly convert back & forth with raw integers. Not sure I follow, are you objecting to there being two calls to `COIN.Int()`?"
  },
  {
   "t": "2026-07-01T07:57:35Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/wallet/test/coinselector_tests.cpp",
   "commit": "68b9fd26fcae7dd4cb87b272ce330c10c7d6e34c",
   "in_reply_to": 3494118100,
   "text": "The literal is only declared in this one .CPP file and has a comment that it should only be used for tests due to imprecision.\n\nhttps://github.com/bitcoin/bitcoin/blob/cc90782e0cfa602d2b312f0d669d47a7d3d4dd37/src/wallet/test/coinselector_tests.cpp#L34-L39\n\n(The standard non-long `double` type has more than enough bits[^1] to represent satoshi-amounts, which is part of why we have 21M BTC as I got confirmed when I presented this).\n\n[^1]: https://cppreference.com/cpp/language/types#Standard_floating-point_types"
  },
  {
   "t": "2026-07-01T07:58:10Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/wallet/wallet.cpp",
   "commit": "68b9fd26fcae7dd4cb87b272ce330c10c7d6e34c",
   "in_reply_to": 3494100383,
   "text": "Agreed, that would be cleaner. However we do have:\nhttps://github.com/bitcoin/bitcoin/blob/341360964a6fef725825639edc507c275f8ad0b2/src/wallet/spend.cpp#L1485\nSo I'd prefer if that change happened in another PR."
  },
  {
   "t": "2026-07-01T07:59:22Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/txmempool.cpp",
   "commit": "68b9fd26fcae7dd4cb87b272ce330c10c7d6e34c",
   "in_reply_to": 3494090900,
   "text": "What I'm going for is to make amounts stand out more in code and have less implicit conversions with raw types."
  },
  {
   "t": "2026-07-01T08:02:40Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/consensus/amount.h",
   "commit": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
   "in_reply_to": 3395163488,
   "text": "Agree it would be nice to rename to `Amount` as a before/after marker that the codebase is more typesafe, just want to keep the file count down a bit for now."
  },
  {
   "t": "2026-07-01T11:36:04Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/consensus/amount.h",
   "commit": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
   "in_reply_to": 3395165127,
   "text": "When would it be harmful to allow calculating the scalar ratio? Exploring this helped me find a `CeilDiv`.\n\nDiff from disabling `amount / amount` and `amount % amount` while still being able to divide & mod by scalars.\n\n```diff\ndiff --git a/src/consensus/amount.h b/src/consensus/amount.h\nindex 143d76b7b7..e42d55dedf 100644\n--- a/src/consensus/amount.h\n+++ b/src/consensus/amount.h\n@@ -52,7 +52,7 @@ public:\n     {\n         return CAmount{m_sats - other.m_sats};\n     }\n-\n+/*\n     constexpr inner_type operator/(const CAmount other) const noexcept\n     {\n         return m_sats / other.m_sats;\n@@ -62,7 +62,7 @@ public:\n     {\n         return m_sats % other.m_sats;\n     }\n-\n+*/\n     constexpr CAmount& operator+=(const CAmount other) noexcept\n     {\n         *this = CAmount{m_sats + other.m_sats};\ndiff --git a/src/core_io.cpp b/src/core_io.cpp\nindex a5ad9f618f..38988f980c 100644\n--- a/src/core_io.cpp\n+++ b/src/core_io.cpp\n@@ -283,8 +283,8 @@ util::Result<int> SighashFromStr(const std::string& sighash)\n UniValue ValueFromAmount(const CAmount amount)\n {\n     static_assert(COIN > 1_sats);\n-    int64_t quotient = amount / COIN;\n-    int64_t remainder = amount % COIN;\n+    int64_t quotient = amount.Int() / COIN.Int();\n+    int64_t remainder = amount.Int() % COIN.Int();\n     if (amount < 0_sats) {\n         quotient = -quotient;\n         remainder = -remainder;\ndiff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp\nindex 5cacd5c4f2..ad926dde14 100644\n--- a/src/policy/feerate.cpp\n+++ b/src/policy/feerate.cpp\n@@ -30,7 +30,7 @@ std::string CFeeRate::ToString(FeeRateFormat fee_rate_format) const\n {\n     const CAmount feerate_per_kvb{GetFeePerK()};\n     switch (fee_rate_format) {\n-    case FeeRateFormat::BTC_KVB: return strprintf(\"%d.%08d %s/kvB\", feerate_per_kvb / COIN, feerate_per_kvb % COIN, CURRENCY_UNIT);\n+    case FeeRateFormat::BTC_KVB: return strprintf(\"%d.%08d %s/kvB\", feerate_per_kvb / COIN.Int(), feerate_per_kvb % COIN.Int(), CURRENCY_UNIT);\n     case FeeRateFormat::SAT_VB: return strprintf(\"%d.%03d %s/vB\", feerate_per_kvb / 1000, feerate_per_kvb % 1000, CURRENCY_ATOM);\n     } // no default case, so the compiler can warn about missing cases\n     assert(false);\ndiff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp\nindex 286150a834..1241e0c664 100644\n--- a/src/primitives/transaction.cpp\n+++ b/src/primitives/transaction.cpp\n@@ -59,7 +59,7 @@ CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)\n\n std::string CTxOut::ToString() const\n {\n-    return strprintf(\"CTxOut(nValue=%d.%08d, scriptPubKey=%s)\", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));\n+    return strprintf(\"CTxOut(nValue=%d.%08d, scriptPubKey=%s)\", nValue.Int() / COIN.Int(), nValue.Int() % COIN.Int(), HexStr(scriptPubKey).substr(0, 30));\n }\n\n CMutableTransaction::CMutableTransaction() : version{CTransaction::CURRENT_VERSION}, nLockTime{0} {}\ndiff --git a/src/util/moneystr.cpp b/src/util/moneystr.cpp\nindex fe488bca92..a4b9aa603e 100644\n--- a/src/util/moneystr.cpp\n+++ b/src/util/moneystr.cpp\n@@ -22,8 +22,8 @@ std::string FormatMoney(const CAmount n)\n     // Note: not using straight sprintf here because we do NOT want\n     // localized number formatting.\n     static_assert(COIN > 1_sats);\n-    int64_t quotient = n / COIN;\n-    int64_t remainder = n % COIN;\n+    int64_t quotient = n.Int() / COIN.Int();\n+    int64_t remainder = n.Int() % COIN.Int();\n     if (n < 0_sats) {\n         quotient = -quotient;\n         remainder = -remainder;\ndiff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp\nindex 457f6ae290..750ae51647 100644\n--- a/src/wallet/coinselection.cpp\n+++ b/src/wallet/coinselection.cpp\n@@ -12,6 +12,7 @@\n #include <util/check.h>\n #include <util/log.h>\n #include <util/moneystr.h>\n+#include <util/overflow.h>\n\n #include <numeric>\n #include <optional>\n@@ -528,7 +529,7 @@ util::Result<SelectionResult> CoinGrinder(std::vector<OutputGroup>& utxo_pool, c\n                 best_selection_weight = curr_weight;\n                 best_selection_amount = curr_amount;\n             }\n-        } else if (!best_selection.empty() && curr_weight + int64_t{min_tail_weight[curr_tail]} * ((total_target - curr_amount + utxo_pool[curr_tail].GetSelectionAmount() - 1_sats) / utxo_pool[curr_tail].GetSelectionAmount()) > best_selection_weight) {\n+        } else if (!best_selection.empty() && curr_weight + int64_t{min_tail_weight[curr_tail]} * int64_t(CeilDiv(uint64_t((total_target - curr_amount).Int()), uint64_t(utxo_pool[curr_tail].GetSelectionAmount().Int()))) > best_selection_weight) {\n             // Compare minimal tail weight and last selected amount with the amount missing to gauge whether a better weight is still possible.\n             if (utxo_pool[curr_tail].m_weight <= min_tail_weight[curr_tail]) {\n                 should_cut = true;\n```"
  },
  {
   "t": "2026-07-01T11:43:18Z",
   "kind": "review_comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "path": "src/serialize.h",
   "commit": "cc90782e0cfa602d2b312f0d669d47a7d3d4dd37",
   "in_reply_to": 3494097004,
   "text": "Agreed, thanks for pushing back on this.\n\n(Edit: Fixed in later push)."
  },
  {
   "t": "2026-07-01T12:10:38Z",
   "kind": "review",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "cc90782e0cfa602d2b312f0d669d47a7d3d4dd37",
   "text": "Thanks for checking this out @l0rinc!\n\n[quoted text omitted]\nYes, explicit construction could be split off as a later PR.\n\n[quoted text omitted]\nYup, introducing an additional bounded type should come after this work. Something like `MoneyDelta`/`AmountDelta` sounds good.\n\n[quoted text omitted]\nAs far as I can see there is no change to the on the wire format. This comment makes me uneasy but the number of out of bounds values serialized should be the same as on master.\n\nhttps://github.com/bitcoin/bitcoin/blob/341360964a6fef725825639edc507c275f8ad0b2/src/compressor.h#L30-L39"
  },
  {
   "t": "2026-07-01T12:18:34Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "9c11263d4148f0f8369875ae1acb00b82ff33538"
  },
  {
   "t": "2026-07-12T08:58:47Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "4bcb9af26bd6d125f82a0d0c63008c540a18b644"
  },
  {
   "t": "2026-07-12T09:15:17Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "df4b4fc56e021e7e8abc6bc6b2bfb8b0c22b0c4d"
  },
  {
   "t": "2026-08-07T12:47:31Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "00704f26ba2949432697312431dd6d8b7c41d0af"
  },
  {
   "t": "2026-08-07T13:27:01Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "68b9fd26fcae7dd4cb87b272ce330c10c7d6e34c"
  },
  {
   "t": "2026-08-25T06:44:43Z",
   "kind": "force_push",
   "who": "hodlinator",
   "commit": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f"
  },
  {
   "t": "2026-08-25T07:40:18Z",
   "kind": "comment",
   "who": "hodlinator",
   "assoc": "MEMBER",
   "text": "Latest push removes the `_sats` UDL. Instead the commits get by on `CAmount{<literal>}` until the end, when a `sats` unit is defined and applied as `<literal>*sats`. (It can also be applied to non-literal expressions). The push also uses scripted diffs to a larger extent than before."
  }
 ],
 "labels_log": [
  {
   "t": "2026-06-11T11:40:09Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-11T19:47:42Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-17T15:26:19Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-18T22:08:30Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-01T14:00:16Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-07T21:37:39Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-12T09:14:17Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-12T10:50:17Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-14T18:49:11Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-07T13:28:12Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-07T13:59:14Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-07T15:11:12Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-11T16:21:36Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-25T07:01:30Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-08-10T10:00:01Z",
   "kind": "convert_to_draft",
   "who": "hodlinator"
  }
 ],
 "text_chars": 27023,
 "text_tokens_estimate": 6755,
 "changed_paths": [
  "src/bench/block_assemble.cpp",
  "src/bench/cluster_linearize.cpp",
  "src/bench/coin_selection.cpp",
  "src/bench/disconnected_transactions.cpp",
  "src/bench/duplicate_inputs.cpp",
  "src/bench/mempool_ephemeral_spends.cpp",
  "src/bench/mempool_eviction.cpp",
  "src/bench/mempool_stress.cpp",
  "src/bench/rpc_mempool.cpp",
  "src/bench/sign_transaction.cpp",
  "src/bench/txgraph.cpp",
  "src/bench/verify_script.cpp",
  "src/bench/wallet_balance.cpp",
  "src/bench/wallet_create_tx.cpp",
  "src/bitcoin-tx.cpp",
  "src/coins.cpp",
  "src/compressor.h",
  "src/consensus/amount.h",
  "src/consensus/tx_check.cpp",
  "src/consensus/tx_verify.cpp",
  "src/consensus/tx_verify.h",
  "src/core_io.cpp",
  "src/core_io.h",
  "src/index/coinstatsindex.cpp",
  "src/interfaces/wallet.h",
  "src/ipc/capnp/common-types.h",
  "src/ipc/test/ipc_test.capnp",
  "src/ipc/test/ipc_test.h",
  "src/ipc/test/ipc_tests.cpp",
  "src/kernel/bitcoinkernel.cpp",
  "src/kernel/coinstats.cpp",
  "src/kernel/mempool_entry.h",
  "src/net_processing.cpp",
  "src/net_processing.h",
  "src/node/interfaces.cpp",
  "src/node/mempool_persist.cpp",
  "src/node/miner.cpp",
  "src/node/miner.h",
  "src/node/mini_miner.cpp",
  "src/node/mini_miner.h",
  "src/node/mining_types.h",
  "src/node/psbt.cpp",
  "src/node/transaction.cpp",
  "src/node/txorphanage.cpp",
  "src/policy/ephemeral_policy.cpp",
  "src/policy/feerate.cpp",
  "src/policy/feerate.h",
  "src/policy/fees/block_policy_estimator.cpp",
  "src/policy/policy.cpp",
  "src/policy/policy.h",
  "src/primitives/transaction.cpp",
  "src/primitives/transaction.h",
  "src/psbt.h",
  "src/qt/bitcoin.cpp",
  "src/qt/bitcoinamountfield.cpp",
  "src/qt/bitcoinamountfield.h",
  "src/qt/bitcoingui.cpp",
  "src/qt/bitcoinunits.cpp",
  "src/qt/coincontroldialog.cpp",
  "src/qt/guiutil.cpp",
  "src/qt/overviewpage.cpp",
  "src/qt/psbtoperationsdialog.cpp",
  "src/qt/receivecoinsdialog.cpp",
  "src/qt/receiverequestdialog.cpp",
  "src/qt/recentrequeststablemodel.cpp",
  "src/qt/sendcoinsdialog.cpp",
  "src/qt/sendcoinsentry.cpp",
  "src/qt/sendcoinsrecipient.h",
  "src/qt/test/uritests.cpp",
  "src/qt/test/wallettests.cpp",
  "src/qt/transactiondesc.cpp",
  "src/qt/transactionfilterproxy.cpp",
  "src/qt/transactionrecord.cpp",
  "src/qt/transactionrecord.h",
  "src/qt/transactiontablemodel.cpp",
  "src/qt/transactionview.cpp",
  "src/qt/walletmodel.cpp",
  "src/qt/walletmodeltransaction.cpp",
  "src/qt/walletview.cpp",
  "src/rpc/blockchain.cpp",
  "src/rpc/fees.cpp",
  "src/rpc/mempool.cpp",
  "src/rpc/mining.cpp",
  "src/rpc/rawtransaction.cpp",
  "src/rpc/util.cpp",
  "src/script/interpreter.cpp",
  "src/script/sigcache.h",
  "src/script/sign.h",
  "src/signet.cpp",
  "src/test/amount_tests.cpp",
  "src/test/blockencodings_tests.cpp",
  "src/test/blockfilter_tests.cpp",
  "src/test/blockpolicyestimator_tests.cpp",
  "src/test/cluster_linearize_tests.cpp",
  "src/test/coins_tests.cpp",
  "src/test/coinsviewoverlay_tests.cpp",
  "src/test/compress_tests.cpp",
  "src/test/descriptor_tests.cpp",
  "src/test/feefrac_tests.cpp",
  "src/test/feerounder_tests.cpp",
  "src/test/fuzz/cluster_linearize.cpp",
  "src/test/fuzz/cmpctblock.cpp",
  "src/test/fuzz/coins_view.cpp",
  "src/test/fuzz/coinscache_sim.cpp",
  "src/test/fuzz/deserialize.cpp",
  "src/test/fuzz/fee_rate.cpp",
  "src/test/fuzz/feefrac.cpp",
  "src/test/fuzz/feeratediagram.cpp",
  "src/test/fuzz/integer.cpp",
  "src/test/fuzz/mini_miner.cpp",
  "src/test/fuzz/p2p_headers_presync.cpp",
  "src/test/fuzz/package_eval.cpp",
  "src/test/fuzz/rbf.cpp",
  "src/test/fuzz/script_flags.cpp",
  "src/test/fuzz/tx_pool.cpp",
  "src/test/fuzz/txgraph.cpp",
  "src/test/fuzz/txorphan.cpp",
  "src/test/fuzz/util.cpp",
  "src/test/fuzz/util.h",
  "src/test/fuzz/util/mempool.cpp",
  "src/test/mempool_fee_estimator_tests.cpp",
  "src/test/mempool_tests.cpp",
  "src/test/miner_tests.cpp",
  "src/test/miniminer_tests.cpp",
  "src/test/multisig_tests.cpp",
  "src/test/psbt_tests.cpp",
  "src/test/rbf_tests.cpp",
  "src/test/rpc_tests.cpp",
  "src/test/script_p2sh_tests.cpp",
  "src/test/script_tests.cpp",
  "src/test/sighash_tests.cpp",
  "src/test/sigopcount_tests.cpp",
  "src/test/testnet4_miner_tests.cpp",
  "src/test/transaction_tests.cpp",
  "src/test/txdownload_tests.cpp",
  "src/test/txgraph_tests.cpp",
  "src/test/txospenderindex_tests.cpp",
  "src/test/txpackage_tests.cpp",
  "src/test/txvalidation_tests.cpp",
  "src/test/txvalidationcache_tests.cpp",
  "src/test/util/cluster_linearize.h",
  "src/test/util/random.h",
  "src/test/util/setup_common.cpp",
  "src/test/util/transaction_utils.cpp",
  "src/test/util/transaction_utils.h",
  "src/test/util/txmempool.cpp",
  "src/test/util_tests.cpp",
  "src/test/validation_block_tests.cpp",
  "src/test/validation_chainstatemanager_tests.cpp",
  "src/test/validation_tests.cpp",
  "src/txgraph.cpp",
  "src/txgraph.h",
  "src/txmempool.cpp",
  "src/txmempool.h",
  "src/util/feefrac.cpp",
  "src/util/feefrac.h",
  "src/util/fees.h",
  "src/util/moneystr.cpp",
  "src/validation.cpp",
  "src/wallet/coinselection.cpp",
  "src/wallet/coinselection.h",
  "src/wallet/feebumper.cpp",
  "src/wallet/fees.cpp",
  "src/wallet/interfaces.cpp",
  "src/wallet/receive.cpp",
  "src/wallet/receive.h",
  "src/wallet/rpc/addresses.cpp",
  "src/wallet/rpc/coins.cpp",
  "src/wallet/rpc/spend.cpp",
  "src/wallet/rpc/transactions.cpp",
  "src/wallet/rpc/wallet.cpp",
  "src/wallet/spend.cpp",
  "src/wallet/test/coinselection_tests.cpp",
  "src/wallet/test/coinselector_tests.cpp",
  "src/wallet/test/fuzz/coinselection.cpp",
  "src/wallet/test/fuzz/fees.cpp",
  "src/wallet/test/fuzz/spend.cpp",
  "src/wallet/test/group_outputs_tests.cpp",
  "src/wallet/test/spend_tests.cpp",
  "src/wallet/test/wallet_tests.cpp",
  "src/wallet/transaction.h",
  "src/wallet/types.h",
  "src/wallet/wallet.cpp",
  "src/wallet/wallet.h"
 ],
 "files": [
  {
   "path": "src/bench/block_assemble.cpp",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/bench/cluster_linearize.cpp",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/bench/coin_selection.cpp",
   "add": 18,
   "del": 17
  },
  {
   "path": "src/bench/disconnected_transactions.cpp",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/bench/duplicate_inputs.cpp",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/bench/mempool_ephemeral_spends.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/bench/mempool_eviction.cpp",
   "add": 7,
   "del": 7
  },
  {
   "path": "src/bench/mempool_stress.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/bench/rpc_mempool.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/bench/sign_transaction.cpp",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/bench/txgraph.cpp",
   "add": 3,
   "del": 2
  },
  {
   "path": "src/bench/verify_script.cpp",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/bench/wallet_balance.cpp",
   "add": 3,
   "del": 1
  },
  {
   "path": "src/bench/wallet_create_tx.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/bitcoin-tx.cpp",
   "add": 4,
   "del": 3
  },
  {
   "path": "src/coins.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/compressor.h",
   "add": 7,
   "del": 4
  },
  {
   "path": "src/consensus/amount.h",
   "add": 128,
   "del": 2
  },
  {
   "path": "src/consensus/tx_check.cpp",
   "add": 3,
   "del": 2
  },
  {
   "path": "src/consensus/tx_verify.cpp",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/consensus/tx_verify.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/core_io.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/core_io.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/index/coinstatsindex.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/interfaces/wallet.h",
   "add": 8,
   "del": 8
  },
  {
   "path": "src/ipc/capnp/common-types.h",
   "add": 16,
   "del": 0
  },
  {
   "path": "src/ipc/test/ipc_test.capnp",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/ipc/test/ipc_test.h",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/ipc/test/ipc_tests.cpp",
   "add": 5,
   "del": 1
  },
  {
   "path": "src/kernel/bitcoinkernel.cpp",
   "add": 4,
   "del": 3
  },
  {
   "path": "src/kernel/coinstats.cpp",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/kernel/mempool_entry.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/net_processing.cpp",
   "add": 6,
   "del": 5
  },
  {
   "path": "src/net_processing.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/node/interfaces.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/node/mempool_persist.cpp",
   "add": 8,
   "del": 11
  },
  {
   "path": "src/node/miner.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/node/miner.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/node/mini_miner.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/node/mini_miner.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/node/mining_types.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/node/psbt.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/node/transaction.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/node/txorphanage.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/policy/ephemeral_policy.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/policy/feerate.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/policy/feerate.h",
   "add": 1,
   "del": 2
  },
  {
   "path": "src/policy/fees/block_policy_estimator.cpp",
   "add": 19,
   "del": 20
  },
  {
   "path": "src/policy/policy.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/policy/policy.h",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/primitives/transaction.cpp",
   "add": 2,
   "del": 3
  },
  {
   "path": "src/primitives/transaction.h",
   "add": 4,
   "del": 7
  },
  {
   "path": "src/psbt.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/qt/bitcoin.cpp",
   "add": 1,
   "del": 2
  },
  {
   "path": "src/qt/bitcoinamountfield.cpp",
   "add": 9,
   "del": 9
  },
  {
   "path": "src/qt/bitcoinamountfield.h",
   "add": 10,
   "del": 1
  },
  {
   "path": "src/qt/bitcoingui.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/qt/bitcoinunits.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/qt/coincontroldialog.cpp",
   "add": 20,
   "del": 20
  },
  {
   "path": "src/qt/guiutil.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/qt/overviewpage.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/qt/psbtoperationsdialog.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/qt/receivecoinsdialog.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/qt/receiverequestdialog.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/qt/recentrequeststablemodel.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/qt/sendcoinsdialog.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/qt/sendcoinsentry.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/qt/sendcoinsrecipient.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/qt/test/uritests.cpp",
   "add": 8,
   "del": 8
  },
  {
   "path": "src/qt/test/wallettests.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/qt/transactiondesc.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/qt/transactionfilterproxy.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/qt/transactionrecord.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/qt/transactionrecord.h",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/qt/transactiontablemodel.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/qt/transactionview.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/qt/walletmodel.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/qt/walletmodeltransaction.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/qt/walletview.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/rpc/blockchain.cpp",
   "add": 22,
   "del": 22
  },
  {
   "path": "src/rpc/fees.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/rpc/mempool.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/rpc/mining.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/rpc/rawtransaction.cpp",
   "add": 5,
   "del": 4
  },
  {
   "path": "src/rpc/util.cpp",
   "add": 3,
   "del": 2
  },
  {
   "path": "src/script/interpreter.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/script/sigcache.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/script/sign.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/signet.cpp",
   "add": 3,
   "del": 2
  },
  {
   "path": "src/test/amount_tests.cpp",
   "add": 42,
   "del": 42
  },
  {
   "path": "src/test/blockencodings_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/blockfilter_tests.cpp",
   "add": 10,
   "del": 10
  },
  {
   "path": "src/test/blockpolicyestimator_tests.cpp",
   "add": 7,
   "del": 7
  },
  {
   "path": "src/test/cluster_linearize_tests.cpp",
   "add": 7,
   "del": 7
  },
  {
   "path": "src/test/coins_tests.cpp",
   "add": 11,
   "del": 11
  },
  {
   "path": "src/test/coinsviewoverlay_tests.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/compress_tests.cpp",
   "add": 11,
   "del": 8
  },
  {
   "path": "src/test/descriptor_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/feefrac_tests.cpp",
   "add": 79,
   "del": 78
  },
  {
   "path": "src/test/feerounder_tests.cpp",
   "add": 7,
   "del": 7
  },
  {
   "path": "src/test/fuzz/cluster_linearize.cpp",
   "add": 4,
   "del": 2
  },
  {
   "path": "src/test/fuzz/cmpctblock.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/fuzz/coins_view.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/fuzz/coinscache_sim.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/fuzz/deserialize.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/fuzz/fee_rate.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/fuzz/feefrac.cpp",
   "add": 11,
   "del": 11
  },
  {
   "path": "src/test/fuzz/feeratediagram.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/test/fuzz/integer.cpp",
   "add": 7,
   "del": 7
  },
  {
   "path": "src/test/fuzz/mini_miner.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/test/fuzz/p2p_headers_presync.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/fuzz/package_eval.cpp",
   "add": 15,
   "del": 15
  },
  {
   "path": "src/test/fuzz/rbf.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/fuzz/script_flags.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/fuzz/tx_pool.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/test/fuzz/txgraph.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/test/fuzz/txorphan.cpp",
   "add": 7,
   "del": 7
  },
  {
   "path": "src/test/fuzz/util.cpp",
   "add": 13,
   "del": 3
  },
  {
   "path": "src/test/fuzz/util.h",
   "add": 3,
   "del": 1
  },
  {
   "path": "src/test/fuzz/util/mempool.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/mempool_fee_estimator_tests.cpp",
   "add": 6,
   "del": 6
  },
  {
   "path": "src/test/mempool_tests.cpp",
   "add": 32,
   "del": 32
  },
  {
   "path": "src/test/miner_tests.cpp",
   "add": 39,
   "del": 39
  },
  {
   "path": "src/test/miniminer_tests.cpp",
   "add": 19,
   "del": 19
  },
  {
   "path": "src/test/multisig_tests.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/test/psbt_tests.cpp",
   "add": 6,
   "del": 6
  },
  {
   "path": "src/test/rbf_tests.cpp",
   "add": 34,
   "del": 34
  },
  {
   "path": "src/test/rpc_tests.cpp",
   "add": 51,
   "del": 51
  },
  {
   "path": "src/test/script_p2sh_tests.cpp",
   "add": 18,
   "del": 18
  },
  {
   "path": "src/test/script_tests.cpp",
   "add": 50,
   "del": 50
  },
  {
   "path": "src/test/sighash_tests.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/test/sigopcount_tests.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/testnet4_miner_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/transaction_tests.cpp",
   "add": 51,
   "del": 51
  },
  {
   "path": "src/test/txdownload_tests.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/test/txgraph_tests.cpp",
   "add": 13,
   "del": 13
  },
  {
   "path": "src/test/txospenderindex_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/txpackage_tests.cpp",
   "add": 17,
   "del": 17
  },
  {
   "path": "src/test/txvalidation_tests.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/test/txvalidationcache_tests.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/test/util/cluster_linearize.h",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/util/random.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/util/setup_common.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/test/util/transaction_utils.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/util/transaction_utils.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/util/txmempool.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/util_tests.cpp",
   "add": 15,
   "del": 15
  },
  {
   "path": "src/test/validation_block_tests.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/test/validation_chainstatemanager_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/test/validation_tests.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/txgraph.cpp",
   "add": 7,
   "del": 7
  },
  {
   "path": "src/txgraph.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/txmempool.cpp",
   "add": 22,
   "del": 16
  },
  {
   "path": "src/txmempool.h",
   "add": 6,
   "del": 6
  },
  {
   "path": "src/util/feefrac.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/util/feefrac.h",
   "add": 15,
   "del": 14
  },
  {
   "path": "src/util/fees.h",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/util/moneystr.cpp",
   "add": 7,
   "del": 6
  },
  {
   "path": "src/validation.cpp",
   "add": 9,
   "del": 9
  },
  {
   "path": "src/wallet/coinselection.cpp",
   "add": 25,
   "del": 25
  },
  {
   "path": "src/wallet/coinselection.h",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/wallet/feebumper.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/wallet/fees.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/wallet/interfaces.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/receive.cpp",
   "add": 16,
   "del": 12
  },
  {
   "path": "src/wallet/receive.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/rpc/addresses.cpp",
   "add": 4,
   "del": 2
  },
  {
   "path": "src/wallet/rpc/coins.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/wallet/rpc/spend.cpp",
   "add": 6,
   "del": 6
  },
  {
   "path": "src/wallet/rpc/transactions.cpp",
   "add": 3,
   "del": 3
  },
  {
   "path": "src/wallet/rpc/wallet.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/spend.cpp",
   "add": 22,
   "del": 22
  },
  {
   "path": "src/wallet/test/coinselection_tests.cpp",
   "add": 22,
   "del": 21
  },
  {
   "path": "src/wallet/test/coinselector_tests.cpp",
   "add": 134,
   "del": 109
  },
  {
   "path": "src/wallet/test/fuzz/coinselection.cpp",
   "add": 24,
   "del": 24
  },
  {
   "path": "src/wallet/test/fuzz/fees.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/wallet/test/fuzz/spend.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/test/group_outputs_tests.cpp",
   "add": 8,
   "del": 8
  },
  {
   "path": "src/wallet/test/spend_tests.cpp",
   "add": 5,
   "del": 5
  },
  {
   "path": "src/wallet/test/wallet_tests.cpp",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/wallet/transaction.h",
   "add": 2,
   "del": 2
  },
  {
   "path": "src/wallet/types.h",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/wallet.cpp",
   "add": 4,
   "del": 4
  },
  {
   "path": "src/wallet/wallet.h",
   "add": 5,
   "del": 5
  }
 ],
 "test_lines": 1770,
 "git": {
  "head": "417e0659cfc6e4d8fbdfe0aee1f325d52447db3f",
  "head_matches_backup": true,
  "base": "07ca9ba9e84ab6d517f875cf06ab62e72ba8921d",
  "commits": [
   {
    "sha": "84ce94fbfe",
    "subject": "fuzz: Add ConsumeMoney() overloads with support for specifying minimum",
    "files": 7,
    "add": 35,
    "del": 23
   },
   {
    "sha": "9a0627d77c",
    "subject": "ipc: Add coverage for passing CAmount",
    "files": 3,
    "add": 6,
    "del": 0
   },
   {
    "sha": "ee9163b96d",
    "subject": "test: Add BTC unit to coinselector_tests.cpp",
    "files": 1,
    "add": 32,
    "del": 17
   },
   {
    "sha": "e881905372",
    "subject": "consensus: Make CAmount a class",
    "files": 63,
    "add": 314,
    "del": 145
   },
   {
    "sha": "8594553ac6",
    "subject": "scripted-diff: Explicitly initialize CAmounts before explicit initialization becomes required",
    "files": 27,
    "add": 35,
    "del": 35
   },
   {
    "sha": "da36dff488",
    "subject": "refactor(mempool): Return nullopt instead of default-initialized TxMempoolInfo",
    "files": 4,
    "add": 11,
    "del": 9
   },
   {
    "sha": "c59e914994",
    "subject": "consensus: Require initialization of CAmount",
    "files": 15,
    "add": 39,
    "del": 32
   },
   {
    "sha": "b42e9484d0",
    "subject": "scripted-diff: Prepare for CAmount ctor becoming explicit",
    "files": 55,
    "add": 313,
    "del": 313
   },
   {
    "sha": "523a8fcb68",
    "subject": "refactor: Manual explicit initializations of CAmount",
    "files": 126,
    "add": 704,
    "del": 696
   },
   {
    "sha": "ecfa1bd93d",
    "subject": "consensus: Make CAmount(T) constructor explicit",
    "files": 13,
    "add": 44,
    "del": 44
   },
   {
    "sha": "16e7ba8f3c",
    "subject": "consensus: Add sats unit",
    "files": 1,
    "add": 9,
    "del": 0
   },
   {
    "sha": "417e0659cf",
    "subject": "scripted-diff: Apply sats unit",
    "files": 121,
    "add": 810,
    "del": 810
   }
  ],
  "patch_truncated": true
 },
 "input_hash": "d5041fea107d4f7c",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}