{
 "number": 33741,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/33741",
 "title": "rpc: Optionally print feerates in sat/vb",
 "author": "polespinasa",
 "author_association": "MEMBER",
 "created_at": "2025-10-30T00:39:41Z",
 "updated_at": "2026-09-12T08:58:49Z",
 "age_days": 322,
 "draft": false,
 "labels": [
  "RPC/REST/ZMQ"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "2d136c58e7336906f5fdeb00ea606f607c0fa776",
 "head_ref": "2025-10-29-PoC-rpc-satvb",
 "head_repo": "polespinasa/bitcoin",
 "head_history": [
  {
   "t": "2025-10-30T00:53:12Z",
   "sha": "1389b9790440a990de843875ae5f7a7431918f82"
  },
  {
   "t": "2025-10-30T20:14:22Z",
   "sha": "ed354dfc5bd5b6577570b6a597cefee743c00140"
  },
  {
   "t": "2025-10-31T18:52:09Z",
   "sha": "c07dc9be34f9d734a2fc2464d48a512df0398a1a"
  },
  {
   "t": "2025-10-31T21:57:28Z",
   "sha": "f4f932d9076df52e2c2da46ed074a684144e6fe8"
  },
  {
   "t": "2025-10-31T22:03:51Z",
   "sha": "c34f6a351457af8d6029e178eb4fc06012edc338"
  },
  {
   "t": "2025-11-11T00:24:30Z",
   "sha": "6caa12234682a7d3d4289d0c848982b803b623d6"
  },
  {
   "t": "2025-11-11T00:30:42Z",
   "sha": "004d22b9a3546e2c6b3b7cba9f417fc9c14aa03b"
  },
  {
   "t": "2026-05-18T17:17:35Z",
   "sha": "66badd0ef70083f58454c6b3458c2e3161d0d64b"
  },
  {
   "t": "2026-05-18T17:21:49Z",
   "sha": "0a39080368bb5b79fbe2ebb868aaba7bef5f9bbb"
  },
  {
   "t": "2026-05-18T20:08:57Z",
   "sha": "99b041c462465b456dff2bc9d1218818ec8bb04e"
  },
  {
   "t": "2026-05-31T15:49:21Z",
   "sha": "0810ba2e8e9e56abbf7d6b38c94e91f7f177d74b"
  },
  {
   "t": "2026-05-31T16:33:02Z",
   "sha": "26316c53ef65c1289227b1c2b9dcc2d7ad802aec"
  },
  {
   "t": "2026-06-04T15:50:11Z",
   "sha": "b218138562580a5ac81179ac4eef600b0aaafc48"
  },
  {
   "t": "2026-06-20T06:31:14Z",
   "sha": "513555558e6c6739a5a55df8c90fe78e76005713"
  },
  {
   "t": "2026-07-28T11:05:34Z",
   "sha": "7e83031241302aff4b67671396f1739d800bbec2"
  },
  {
   "t": "2026-08-21T11:02:27Z",
   "sha": "25fb86bfa7fa319e202d1ec970c0dc15e00888ae"
  },
  {
   "t": "2026-08-21T11:31:48Z",
   "sha": "a1f60713fdfae0e0751d0427e0508736a3ff955e"
  },
  {
   "t": "2026-08-22T08:25:41Z",
   "sha": "55c26e59a451f8dcf8f4a6ff745d29b73360320a"
  },
  {
   "t": "2026-09-10T07:51:45Z",
   "sha": "ccddd8a60f57a0be2f3649191919b20cf428e55f"
  },
  {
   "t": "2026-09-10T09:25:21Z",
   "sha": "234971ad277ff0dad41304d7d48a62d28d7650fe"
  },
  {
   "t": "2026-09-10T09:40:02Z",
   "sha": "ccddd8a60f57a0be2f3649191919b20cf428e55f"
  },
  {
   "t": "2026-09-10T12:18:45Z",
   "sha": "2d136c58e7336906f5fdeb00ea606f607c0fa776"
  }
 ],
 "additions": 158,
 "deletions": 56,
 "changed_files": 10,
 "commit_count": 7,
 "size_bucket": "M",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "musaHaruna",
      "url": "https://github.com/bitcoin/bitcoin/pull/33741#pullrequestreview-4648804064"
     },
     {
      "login": "jonatack",
      "url": "https://github.com/bitcoin/bitcoin/pull/33741#pullrequestreview-4996905869"
     }
    ],
    "stale_ack": [
     {
      "login": "w0xlt",
      "url": "https://github.com/bitcoin/bitcoin/pull/33741#pullrequestreview-4422855914"
     }
    ]
   },
   "conflicts": [
    {
     "number": 35511,
     "title": "RFC: consensus: Make `CAmount` a class",
     "author": "hodlinator"
    }
   ]
  }
 },
 "acks_parsed": {
  "w0xlt": {
   "kind": "ack",
   "hash": "26316c53ef65c1289227b1c2b9dcc2d7ad802aec",
   "t": "2026-06-03T21:15:20Z",
   "stale": true
  },
  "musaHaruna": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-07-07T20:45:48Z",
   "stale": false
  },
  "jonatack": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-08-21T19:52:35Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 1,
  "concept_ack": 2,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "glozow",
   "jonatack",
   "maflcko",
   "musaHaruna",
   "sedited",
   "w0xlt"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-10T12:18:45Z",
  "last_reviewer_activity": "2026-08-21T19:52:35Z",
  "last_reviewer": "jonatack",
  "author_silent_days": 7,
  "waiting_on_author_days": 0,
  "days_since_update": 5
 },
 "refs": {
  "mentioned": [],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [],
  "conflicts": [
   35511
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/core_io.cpp",
  "src/policy/feerate.h",
  "src/rpc/client.cpp",
  "src/rpc/fees.cpp",
  "src/rpc/net.cpp",
  "src/test/amount_tests.cpp"
 ],
 "body": "Part of https://github.com/bitcoin/bitcoin/issues/32093\n\nReturning feerates in BTC/kvB it can be very burdensome and is not good practice, as the most widely adopted units are sat/vB. This PR aims to show a PoC of how we could migrate to sat/vb in a backwards compatible manner.\n\nThe RPC affectd by this PR are `getmempoolinfo`, `getnetworkinfo`, `getwalletinfo`, `estimatesmartfee` and `estimaterawfee`.\n\nBecause of sub 1sat/vB environment we cannot rely on GetFee() because it internally uses FeeFrac EvaluateFee() function which rounds the values to always return an int. If the `feerate < 1` then it will be rounded to 1 or 0.\n\nFor more context or discuss how to migrate also arguments please refer to the open issue https://github.com/bitcoin/bitcoin/issues/32093.",
 "commits": [
  {
   "sha": "4812e3735c1b27b11f08f351b697b952dfe26e2e",
   "date": "2026-09-10T06:22:24Z",
   "message": "rpc: Add ValueFromFeeRate function to core_write\n\nSimilar to ValueFromAmount this function returns a UniValue object with the feerate given a CFeeRate object."
  },
  {
   "sha": "cf6704c6535beaa6f783b596313877ed90780194",
   "date": "2026-09-10T06:22:31Z",
   "message": "test: add unit tests for ValueFromFeeRate"
  },
  {
   "sha": "5f244f61ca7ced9c72aca3673e57a529b43e3b7d",
   "date": "2026-09-10T07:36:33Z",
   "message": "rpc: let MempoolInfoToJSON to return feerates in sat/vb\n\nThis commit also includes mempool.h, which was missing and was causing function definition issues."
  },
  {
   "sha": "1615dbe8e37b47ea6635aa8f48b52da7517f484c",
   "date": "2026-09-10T07:36:37Z",
   "message": "rpc: allow getmempoolinfo to return feerates in sat/vb"
  },
  {
   "sha": "7d9f800390f9b8e8decf0805376ee630fafdd6aa",
   "date": "2026-09-10T07:36:44Z",
   "message": "rpc: allow getnetworkinfo to return feerates in sat/vb"
  },
  {
   "sha": "831ef481eb546cc75174ea081bf5d7f8cf175404",
   "date": "2026-09-10T12:18:31Z",
   "message": "rpc: allow estimatesmartfee to return feerates in sat/vb"
  },
  {
   "sha": "2d136c58e7336906f5fdeb00ea606f607c0fa776",
   "date": "2026-09-10T12:18:32Z",
   "message": "rpc: allow estimaterawfee to return feerates in sat/vB\n\nCo-authored-by: w0xlt <woltx@protonmail.com>"
  }
 ],
 "timeline": [
  {
   "t": "2025-10-30T00:53:12Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82"
  },
  {
   "t": "2025-10-30T00:59:35Z",
   "kind": "review",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "text": "Concept ACK"
  },
  {
   "t": "2025-10-30T07:10:57Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/rpc/fees.cpp",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "in_reply_to": null,
   "text": "Please don't encode default values in how the code is written. This makes it impossible to change the default values without rewriting the code logic.\n\nAlso, could use `self.(Maybe)Arg<bool>` instead?"
  },
  {
   "t": "2025-10-30T07:29:19Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/rpc/fees.cpp",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "in_reply_to": null,
   "text": "this is wrong, you can't use `double` to denote monetary amounts. `double` is a floating point type, which can not represent decimal values accurately.\n\nFor example, if `GetFeePerK()` returns `CAmount{665714}`, the Univalue will be `665.7140000000001`, which is confusing at best, but also wrong."
  },
  {
   "t": "2025-10-30T07:32:20Z",
   "kind": "review",
   "who": "maflcko",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "text": "I guess it is a bit verbose to have each RPC annotated with a type optional arg, but there probably isn't an easier solution.\n\nThere could be a global option to toggle the default, to improve the UX.\n\nThough, please don't use floating point types. For monetary values, a fixed point type should be used."
  },
  {
   "t": "2025-10-30T20:14:22Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "ed354dfc5bd5b6577570b6a597cefee743c00140"
  },
  {
   "t": "2025-10-30T20:19:36Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/rpc/fees.cpp",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "in_reply_to": 2476689034,
   "text": "You are right, although I think it would not be a problem because we don't print that many decimals.\n\nAnyway, I think the approach in afeeac1596da47b0e431d468a665d344b39e1a87 is cleaner and follows what we already do for btc/kvB. What do you think?"
  },
  {
   "t": "2025-10-30T20:21:28Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/rpc/fees.cpp",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "in_reply_to": 2476651745,
   "text": "I think with the rebase it's easier to change defaults without touching code logic.\n\nWhat would be the benefits of using `self.MaybeArg<bool>`? I'm not really familiar with it tbh."
  },
  {
   "t": "2025-10-30T20:29:21Z",
   "kind": "comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "text": "@maflcko thanks for reviewing :)\n\n[quoted text omitted]\nI don't think there is either, at least not in a backwards compatible way.\n\n[quoted text omitted]\nThat would be great for a `bitcoin-cli` user, but probably fatal for other programs that rely on the Bitcoin Core RPC.\nE.g. if a user with a Lightning Node enables sat/vB, thus making `estimatesmartfee` return `sat/vb` by default, probably the LN node will force close the channels due to lack of fees to pay."
  },
  {
   "t": "2025-10-31T07:46:10Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/rpc/fees.cpp",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "in_reply_to": 2476651745,
   "text": "[quoted text omitted]\n\nThe function is explained in the docstring above the function. It will read the default value by itself, so it doesn't need to be hardcoded several times.\n\n[quoted text omitted]\nI don't think so. I can still see `                    FeeEstimateMode feerate_units = (!request.params[2].isNull() && request.params[2].get_bool()) ? FeeEstimateMode::SAT_VB : FeeEstimateMode::BTC_KVB;`"
  },
  {
   "t": "2025-10-31T07:47:59Z",
   "kind": "review_comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "path": "src/rpc/fees.cpp",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "in_reply_to": 2476689034,
   "text": "[quoted text omitted]\n\nThey are printed. You can try it locally by printing the constructed univalue or by checking it in a test.\n\n[quoted text omitted]\nI don't think it handles negative feerates correctly. Generally, it is best to write unit or fuzz tests for newly written code. Also, I am not sure if `CFeeRate` should be bundled with univalue. The conversion could be a standalone helper, but this is just a nit."
  },
  {
   "t": "2025-10-31T18:51:02Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/rpc/fees.cpp",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "in_reply_to": 2476651745,
   "text": "Thanks for the suggestion, yep it is cleaner. Now should be as easy as changing the default in the arg definition.\n\nbtw: I used `self.Arg<bool>`, I think it makes more sense, `self.MaybeArg<bool>` is defined for optional params with no defaults."
  },
  {
   "t": "2025-10-31T18:52:09Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "c07dc9be34f9d734a2fc2464d48a512df0398a1a"
  },
  {
   "t": "2025-10-31T21:57:28Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "f4f932d9076df52e2c2da46ed074a684144e6fe8"
  },
  {
   "t": "2025-10-31T22:00:40Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/rpc/fees.cpp",
   "commit": "1389b9790440a990de843875ae5f7a7431918f82",
   "in_reply_to": 2476689034,
   "text": "[quoted text omitted]\n\nDone\n\n[quoted text omitted]\nI decided to make it a function of `CFeeRate` because is the only class that should use it. But I'm ok moving it to `core_writte`."
  },
  {
   "t": "2025-10-31T22:03:51Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "c34f6a351457af8d6029e178eb4fc06012edc338"
  },
  {
   "t": "2025-11-06T01:34:36Z",
   "kind": "review_comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "path": "src/policy/feerate.h",
   "commit": "c34f6a351457af8d6029e178eb4fc06012edc338",
   "in_reply_to": null,
   "text": "This drags an RPC\u2011only dependency (UniValue) into a widely included policy header.\nInstead, a helper in `rpc/util.{h,cpp}` (or `rpc/fees.h`) could be added such as:\n\n```cpp\nenum class FeeRateUnit { BTC_KVB, SAT_VB };\nUniValue ValueFromFeeRate(const CFeeRate& fr, FeeRateUnit unit);\n```"
  },
  {
   "t": "2025-11-06T01:38:00Z",
   "kind": "review_comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/amount_tests.cpp",
   "commit": "c34f6a351457af8d6029e178eb4fc06012edc338",
   "in_reply_to": null,
   "text": "Do the last six lines duplicate three assertions ?\nYou probably intended to add negative sat/vB cases."
  },
  {
   "t": "2025-11-06T01:43:06Z",
   "kind": "review_comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "path": "src/rpc/net.cpp",
   "commit": "c34f6a351457af8d6029e178eb4fc06012edc338",
   "in_reply_to": null,
   "text": "This line constructs a full object (with locking) only to reuse two values.\nIt also adds a new include `<rpc/mempool.h>` into `rpc/net.cpp`, creating an RPC\u2011RPC dependency.\nIt may be better to compute these two fields directly."
  },
  {
   "t": "2025-11-10T23:22:21Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/rpc/net.cpp",
   "commit": "c34f6a351457af8d6029e178eb4fc06012edc338",
   "in_reply_to": 2496744390,
   "text": "True!\nI thought that there was the intention to add all values and not only `relayfee` and `incrementalfee` here as the comment `        // Those fields can be deprecated, to be replaced by the getmempoolinfo fields` seems suggest.\n\nBut seeing https://github.com/bitcoin/bitcoin/pull/25648/files#r935563077 I think I just misunderstood and the intention was to maybe remove them in the future and just use `getmempool` rpc to know that info."
  },
  {
   "t": "2025-11-10T23:27:52Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/test/amount_tests.cpp",
   "commit": "c34f6a351457af8d6029e178eb4fc06012edc338",
   "in_reply_to": 2496731465,
   "text": "yup, good catch tanks! :)"
  },
  {
   "t": "2025-11-10T23:28:59Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/policy/feerate.h",
   "commit": "c34f6a351457af8d6029e178eb4fc06012edc338",
   "in_reply_to": 2496724101,
   "text": "Yes maybe it's a better approach.\nMoved the function into `core_write.cpp` and `core_io.h` similar to function `ValueFromAmount`."
  },
  {
   "t": "2025-11-11T00:24:30Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "6caa12234682a7d3d4289d0c848982b803b623d6"
  },
  {
   "t": "2025-11-11T00:25:51Z",
   "kind": "review",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "c34f6a351457af8d6029e178eb4fc06012edc338",
   "text": "Rebased following @w0xlt suggestions :)"
  },
  {
   "t": "2025-11-11T00:30:42Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "004d22b9a3546e2c6b3b7cba9f417fc9c14aa03b"
  },
  {
   "t": "2025-11-11T14:27:21Z",
   "kind": "comment",
   "who": "glozow",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nCan you explain why it's not good practice? I agree it's not as human-readable but I don't software minds either way. Not to be snarky, but I think the most widely adopted usage of this RPC interface must be to handle what it returns, including converting it before printing to users.\n\n[quoted text omitted]\nIt seems extremely dangerous to ever change the default... no matter how much notice is given, it's a massive api break that would cause software to suddenly be several orders of magnitude off.\n\nA different approach could be to add another field to each result, like \"feerate_satvb\". A global config option like `-feeraterepresentation\" could determine which results are returned so that RPC results aren't bloated. I'm not familiar with how we might pass a global config option to the RPCs though."
  },
  {
   "t": "2025-11-11T18:46:45Z",
   "kind": "comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "text": "@glozow thanks for your comments :)\n\n[quoted text omitted]\nWe open the door to other software/users to make easy mistakes by forcing them to do a unit conversion that Core shouldn't have done in the first place. I understand that this has been like this since \u00bfalways?, but giving users the option to have sat/vB (or even more units thanks to FeeFrac) can be useful and reduce the burden on other software.\n\n[quoted text omitted]\nI agree, this PR doesn't change any default behavior. It MUST be done in a backwards compatible way.\n\n[quoted text omitted]\nYes! This is another approach already taken by `fundrawtransaction` for example ([context](https://github.com/bitcoin/bitcoin/issues/32093#issuecomment-2884755244)).\n\n[quoted text omitted]\nI don't dislike at all this approach and I would probably be open to switching to it if there's consensus that it is better.\nBut I am afraid that with that approach users can easily make huge mistakes. They could start their node with `-feeraterepresentation=satvB` and then use some software on top that expects BTC/kvB. That could be fatal. Because of that, I decided to follow the bool option in each RPC call. See https://github.com/bitcoin/bitcoin/pull/33741#issuecomment-3469979496"
  },
  {
   "t": "2025-11-11T19:14:24Z",
   "kind": "comment",
   "who": "glozow",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nI still don't see the argument for why a unit per kvB is so problematic. BTC and satoshis are easily convertible.\n\nI do think it's worthwhile to unify the format for inputting and and outputting feerates. And I agree it should be configurable, given there are multiple preferred formats.\n\nMy concern is the idea that the default format might be changed in the future, causing somebody's reading of a feerate to be off by a factor of 100,000. I think the acceptable way to do this is to create new fields and maybe phase out the old ones.\n\n[quoted text omitted]\nThat's why the results must have different names. If they forget the config option, they should get a key error."
  },
  {
   "t": "2025-11-12T22:09:56Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "Adding a new field in the result called `feerate_unit` (string) to specify the unit used (`sat/vB` or `BTC/kvB`) may address this issue.\nThe current approach looks good to me, though."
  },
  {
   "t": "2025-11-13T01:52:17Z",
   "kind": "comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nYes the idea is to be able to use sat/vB or BTC/kvB in inputting and outputting. This PR is just a PoC on how we could do it for outputting.\n\n[quoted text omitted]\nI don't think the default will ever be changed. I left the door open to it in some comments, but I would not push for it tbh.\n\n[quoted text omitted]\nOh sorry I didn't understand your idea. So you propose to add a startup option to choose the feerate units and then on the outputs change the key so it will return a key error if someone is expecting a different unit? I think that's a good approach, how would you do it for rpc arguments though? Unless you force to use named arguments I think it can still lead to errors."
  },
  {
   "t": "2026-03-30T06:05:28Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "@polespinasa What is the status here?"
  },
  {
   "t": "2026-03-30T12:35:45Z",
   "kind": "comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "text": "@maflcko will rebase soon.\n\nStill would like to know more opinions on the approach to take"
  },
  {
   "t": "2026-05-14T10:43:33Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nReviewers are far less likely to look at PRs that have conflicts, so I would suggest rebasing."
  },
  {
   "t": "2026-05-18T17:17:35Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "66badd0ef70083f58454c6b3458c2e3161d0d64b"
  },
  {
   "t": "2026-05-18T17:21:49Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "0a39080368bb5b79fbe2ebb868aaba7bef5f9bbb"
  },
  {
   "t": "2026-05-18T17:22:36Z",
   "kind": "comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "text": "rebased on top of master"
  },
  {
   "t": "2026-05-18T20:08:57Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "99b041c462465b456dff2bc9d1218818ec8bb04e"
  },
  {
   "t": "2026-05-26T23:20:54Z",
   "kind": "review",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "99b041c462465b456dff2bc9d1218818ec8bb04e",
   "text": "`estimaterawfee` is converting only the top-level feerate when `satvB=true`, while `pass/fail.startrange` and `endrange` remained in the `sat/kvB` units.\n\nThe below patch applies the same unit selection to those bucket range feerates, avoiding mixed-unit RPC responses.\n\ndiff\n\n```diff\ndiff --git a/src/rpc/fees.cpp b/src/rpc/fees.cpp\nindex 140ba9d64f..854f4f203f 100644\n--- a/src/rpc/fees.cpp\n+++ b/src/rpc/fees.cpp\n@@ -4,6 +4,7 @@\n // file COPYING or http://www.opensource.org/licenses/mit-license.php.\n\n #include <common/messages.h>\n+#include <consensus/amount.h>\n #include <core_io.h>\n #include <node/context.h>\n #include <policy/feerate.h>\n@@ -21,6 +22,7 @@\n #include <algorithm>\n #include <array>\n #include <cmath>\n+#include <limits>\n #include <string>\n #include <string_view>\n\n@@ -29,6 +31,19 @@ using common::FeeModesDetail;\n using common::InvalidEstimateModeErrorMessage;\n using node::NodeContext;\n\n+static UniValue ValueFromEstimateBucketFeeRate(double fee_rate_per_kvb, bool sat_vb)\n+{\n+    const double rounded_fee_rate_per_kvb{round(fee_rate_per_kvb)};\n+\n+    // Preserve estimaterawfee's legacy bucket range unit unless sat/vB was requested.\n+    if (!sat_vb || rounded_fee_rate_per_kvb < 0) return rounded_fee_rate_per_kvb;\n+\n+    if (rounded_fee_rate_per_kvb > static_cast<double>(std::numeric_limits<CAmount>::max())) {\n+        return rounded_fee_rate_per_kvb / 1000.0;\n+    }\n+    return ValueFromFeeRate(CFeeRate{static_cast<CAmount>(rounded_fee_rate_per_kvb)}, FeeRateUnit::SAT_VB);\n+}\n+\n static RPCMethod estimatesmartfee()\n {\n     return RPCMethod{\n@@ -168,6 +183,8 @@ static RPCMethod estimaterawfee()\n             }\n\n             UniValue result(UniValue::VOBJ);\n+            const bool sat_vb{self.Arg<bool>(\"satvB\")};\n+            const FeeRateUnit feerate_units{sat_vb ? FeeRateUnit::SAT_VB : FeeRateUnit::BTC_KVB};\n\n             for (const FeeEstimateHorizon horizon : ALL_FEE_ESTIMATE_HORIZONS) {\n                 CFeeRate feeRate;\n@@ -180,15 +197,15 @@ static RPCMethod estimaterawfee()\n                 UniValue horizon_result(UniValue::VOBJ);\n                 UniValue errors(UniValue::VARR);\n                 UniValue passbucket(UniValue::VOBJ);\n-                passbucket.pushKV(\"startrange\", round(buckets.pass.start));\n-                passbucket.pushKV(\"endrange\", round(buckets.pass.end));\n+                passbucket.pushKV(\"startrange\", ValueFromEstimateBucketFeeRate(buckets.pass.start, sat_vb));\n+                passbucket.pushKV(\"endrange\", ValueFromEstimateBucketFeeRate(buckets.pass.end, sat_vb));\n                 passbucket.pushKV(\"withintarget\", round(buckets.pass.withinTarget * 100.0) / 100.0);\n                 passbucket.pushKV(\"totalconfirmed\", round(buckets.pass.totalConfirmed * 100.0) / 100.0);\n                 passbucket.pushKV(\"inmempool\", round(buckets.pass.inMempool * 100.0) / 100.0);\n                 passbucket.pushKV(\"leftmempool\", round(buckets.pass.leftMempool * 100.0) / 100.0);\n                 UniValue failbucket(UniValue::VOBJ);\n-                failbucket.pushKV(\"startrange\", round(buckets.fail.start));\n-                failbucket.pushKV(\"endrange\", round(buckets.fail.end));\n+                failbucket.pushKV(\"startrange\", ValueFromEstimateBucketFeeRate(buckets.fail.start, sat_vb));\n+                failbucket.pushKV(\"endrange\", ValueFromEstimateBucketFeeRate(buckets.fail.end, sat_vb));\n                 failbucket.pushKV(\"withintarget\", round(buckets.fail.withinTarget * 100.0) / 100.0);\n                 failbucket.pushKV(\"totalconfirmed\", round(buckets.fail.totalConfirmed * 100.0) / 100.0);\n                 failbucket.pushKV(\"inmempool\", round(buckets.fail.inMempool * 100.0) / 100.0);\n@@ -196,7 +213,7 @@ static RPCMethod estimaterawfee()\n\n                 // CFeeRate(0) is used to indicate error as a return value from estimateRawFee\n                 if (feeRate != CFeeRate(0)) {\n-                    horizon_result.pushKV(\"feerate\", ValueFromFeeRate(feeRate, self.Arg<bool>(\"satvB\") ? FeeRateUnit::SAT_VB : FeeRateUnit::BTC_KVB ));\n+                    horizon_result.pushKV(\"feerate\", ValueFromFeeRate(feeRate, feerate_units));\n                     horizon_result.pushKV(\"decay\", buckets.decay);\n                     horizon_result.pushKV(\"scale\", buckets.scale);\n                     horizon_result.pushKV(\"pass\", std::move(passbucket));\n```"
  },
  {
   "t": "2026-05-31T15:49:21Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "0810ba2e8e9e56abbf7d6b38c94e91f7f177d74b"
  },
  {
   "t": "2026-05-31T16:32:43Z",
   "kind": "comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "text": "@w0xlt good catch. I picked it with a slightly different approach.\nAdded you as a co-author."
  },
  {
   "t": "2026-05-31T16:33:02Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "26316c53ef65c1289227b1c2b9dcc2d7ad802aec"
  },
  {
   "t": "2026-06-03T21:15:20Z",
   "kind": "review",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "26316c53ef65c1289227b1c2b9dcc2d7ad802aec",
   "text": "ACK 26316c53ef65c1289227b1c2b9dcc2d7ad802aec\n\nnit: RPC help texts can be update\n\n  diff\n\n```diff\ndiff --git a/src/rpc/fees.cpp b/src/rpc/fees.cpp\nindex a1bef9615f..e429c4e473 100644\n--- a/src/rpc/fees.cpp\n+++ b/src/rpc/fees.cpp\n@@ -47,7 +47,7 @@ static RPCMethod estimatesmartfee()\n         RPCResult{\n             RPCResult::Type::OBJ, \"\", \"\",\n             {\n-                {RPCResult::Type::NUM, \"feerate\", /*optional=*/true, \"estimate fee rate in \" + CURRENCY_UNIT + \"/kvB (only present if no errors were encountered)\"},\n+                {RPCResult::Type::NUM, \"feerate\", /*optional=*/true, \"estimate fee rate in \" + CURRENCY_UNIT + \"/kvB, or \" + CURRENCY_ATOM + \"/vB if satvB is true (only present if no errors were encountered)\"},\n                 {RPCResult::Type::ARR, \"errors\", /*optional=*/true, \"Errors encountered during processing (if there are any)\",\n                     {\n                         {RPCResult::Type::STR, \"\", \"error\"},\ndiff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp\nindex ed5d8fb662..e6428518eb 100644\n--- a/src/rpc/mempool.cpp\n+++ b/src/rpc/mempool.cpp\n@@ -1089,9 +1089,9 @@ static RPCMethod getmempoolinfo()\n                     {RPCResult::Type::NUM, \"usage\", \"Total memory usage for the mempool\"},\n                     {RPCResult::Type::STR_AMOUNT, \"total_fee\", \"Total fees for the mempool in \" + CURRENCY_UNIT + \", ignoring modified fees through prioritisetransaction\"},\n                     {RPCResult::Type::NUM, \"maxmempool\", \"Maximum memory usage for the mempool\"},\n-                    {RPCResult::Type::STR_AMOUNT, \"mempoolminfee\", \"Minimum fee rate in \" + CURRENCY_UNIT + \"/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee\"},\n-                    {RPCResult::Type::STR_AMOUNT, \"minrelaytxfee\", \"Current minimum relay fee for transactions\"},\n-                    {RPCResult::Type::NUM, \"incrementalrelayfee\", \"minimum fee rate increment for mempool limiting or replacement in \" + CURRENCY_UNIT + \"/kvB\"},\n+                    {RPCResult::Type::STR_AMOUNT, \"mempoolminfee\", \"Minimum fee rate in \" + CURRENCY_UNIT + \"/kvB, or \" + CURRENCY_ATOM + \"/vB if satvB is true, for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee\"},\n+                    {RPCResult::Type::STR_AMOUNT, \"minrelaytxfee\", \"Current minimum relay fee for transactions in \" + CURRENCY_UNIT + \"/kvB, or \" + CURRENCY_ATOM + \"/vB if satvB is true\"},\n+                    {RPCResult::Type::NUM, \"incrementalrelayfee\", \"minimum fee rate increment for mempool limiting or replacement in \" + CURRENCY_UNIT + \"/kvB, or \" + CURRENCY_ATOM + \"/vB if satvB is true\"},\n                     {RPCResult::Type::NUM, \"unbroadcastcount\", \"Current number of transactions that haven't passed initial broadcast yet\"},\n                     {RPCResult::Type::BOOL, \"permitbaremultisig\", \"True if the mempool accepts transactions with bare multisig outputs\"},\n                     {RPCResult::Type::NUM, \"maxdatacarriersize\", \"Maximum number of bytes that can be used by OP_RETURN outputs in the mempool\"},\ndiff --git a/src/rpc/net.cpp b/src/rpc/net.cpp\nindex 742287a12b..ab49a386e7 100644\n--- a/src/rpc/net.cpp\n+++ b/src/rpc/net.cpp\n@@ -670,8 +670,8 @@ static RPCMethod getnetworkinfo()\n                                 {RPCResult::Type::BOOL, \"proxy_randomize_credentials\", \"Whether randomized credentials are used\"},\n                             }},\n                         }},\n-                        {RPCResult::Type::NUM, \"relayfee\", \"minimum relay fee rate for transactions in \" + CURRENCY_UNIT + \"/kvB\"},\n-                        {RPCResult::Type::NUM, \"incrementalfee\", \"minimum fee rate increment for mempool limiting or replacement in \" + CURRENCY_UNIT + \"/kvB\"},\n+                        {RPCResult::Type::NUM, \"relayfee\", \"minimum relay fee rate for transactions in \" + CURRENCY_UNIT + \"/kvB, or \" + CURRENCY_ATOM + \"/vB if satvB is true\"},\n+                        {RPCResult::Type::NUM, \"incrementalfee\", \"minimum fee rate increment for mempool limiting or replacement in \" + CURRENCY_UNIT + \"/kvB, or \" + CURRENCY_ATOM + \"/vB if satvB is true\"},\n                         {RPCResult::Type::ARR, \"localaddresses\", \"list of local addresses\",\n                         {\n                             {RPCResult::Type::OBJ, \"\", \"\",\n```"
  },
  {
   "t": "2026-06-04T15:50:11Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "b218138562580a5ac81179ac4eef600b0aaafc48"
  },
  {
   "t": "2026-06-20T06:31:14Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "513555558e6c6739a5a55df8c90fe78e76005713"
  },
  {
   "t": "2026-06-20T06:32:23Z",
   "kind": "comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "text": "Rebased on top of master"
  },
  {
   "t": "2026-07-07T20:45:48Z",
   "kind": "review",
   "who": "musaHaruna",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "513555558e6c6739a5a55df8c90fe78e76005713",
   "text": "Concept ACK on migrating to sat/vb in a backwards compatible manner.\n\nCode Review [740b6e8](https://github.com/bitcoin/bitcoin/pull/33741/changes/740b6e803cede9c9f7a70055bed3e0e0baba3b44)\n\nI noticed that this section handles negative values by mutating the signed variables after division:\n```cpp\n    int64_t quotient  = feerate_per_kvb / units;\n    int64_t remainder = feerate_per_kvb % units;\n\n    if (feerate_per_kvb < 0) {\n        quotient = -quotient;\n        remainder = -remainder;\n    }\n```\nWould it make sense, or is there a reason not to simplify this by stripping the negative sign right at the start using static_cast<uint64_t>? i.e:\n\n```cpp\nconst uint64_t abs_feerate = (feerate_per_kvb < 0) ? -static_cast<uint64_t (feerate_per_kvb) : feerate_per_kvb;\n    const uint64_t quotient = abs_feerate / units;\n    const uint64_t remainder = abs_feerate % units;\n```\nHandling the magnitude on pure unsigned types feels like it would eliminate the need for the conditional if block, making the function completely linear.\n\nI might be likely missing some subtleties that I'm not aware of though."
  },
  {
   "t": "2026-07-07T20:49:35Z",
   "kind": "review_comment",
   "who": "musaHaruna",
   "assoc": "CONTRIBUTOR",
   "path": "src/core_io.cpp",
   "commit": "513555558e6c6739a5a55df8c90fe78e76005713",
   "in_reply_to": null,
   "text": "Nit: Extra whitespaces can be removed\n```suggestion\n    const int64_t units = is_sat_vb ? 1000 : COIN;\n    const int8_t decimals = is_sat_vb ? 3 : 8;\n```"
  },
  {
   "t": "2026-07-07T20:52:46Z",
   "kind": "review_comment",
   "who": "musaHaruna",
   "assoc": "CONTRIBUTOR",
   "path": "src/core_io.cpp",
   "commit": "2d136c58e7336906f5fdeb00ea606f607c0fa776",
   "in_reply_to": null,
   "text": "I think this can be returned on a single line?\n```suggestion\nreturn UniValue(UniValue::VNUM, strprintf(\"%s%d.%0*d\", feerate_per_kvb < 0 ? \"-\" : \"\", quotient, decimals, remainder));\n```"
  },
  {
   "t": "2026-07-28T11:02:56Z",
   "kind": "comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI rather keep as is for now, unless someone finds a better reason for it. I think is more readable and you are not really eliminating the conditional block, just writing it in a different way (`(feerate_per_kvb < 0) ? .... : ....`)."
  },
  {
   "t": "2026-07-28T11:04:30Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/core_io.cpp",
   "commit": "513555558e6c6739a5a55df8c90fe78e76005713",
   "in_reply_to": 3539580497,
   "text": "done"
  },
  {
   "t": "2026-07-28T11:05:04Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/core_io.cpp",
   "commit": "2d136c58e7336906f5fdeb00ea606f607c0fa776",
   "in_reply_to": 3539600748,
   "text": "I like it as is, imho it's more readable."
  },
  {
   "t": "2026-07-28T11:05:34Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "7e83031241302aff4b67671396f1739d800bbec2"
  },
  {
   "t": "2026-08-21T11:02:27Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "25fb86bfa7fa319e202d1ec970c0dc15e00888ae"
  },
  {
   "t": "2026-08-21T11:31:48Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "a1f60713fdfae0e0751d0427e0508736a3ff955e"
  },
  {
   "t": "2026-08-21T19:52:27Z",
   "kind": "review_comment",
   "who": "jonatack",
   "assoc": "MEMBER",
   "path": "src/rpc/client.cpp",
   "commit": "a1f60713fdfae0e0751d0427e0508736a3ff955e",
   "in_reply_to": null,
   "text": "Would it be better to use `sat_vb` or `satvb` for these? (Sorry if this has already been discussed above.) Per the developer notes:\n\n```md\nArgument and field naming: please consider whether there is already a naming\n  style or spelling convention in the API for the type of object in question\n  (`blockhash`, for example), and if so, try to use that. If not, use snake case\n  `fee_delta` (and not, e.g. `feedelta` or camel case `feeDelta`).\n```"
  },
  {
   "t": "2026-08-21T19:52:35Z",
   "kind": "review",
   "who": "jonatack",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "a1f60713fdfae0e0751d0427e0508736a3ff955e",
   "text": "Concept ACK"
  },
  {
   "t": "2026-08-22T08:25:41Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "55c26e59a451f8dcf8f4a6ff745d29b73360320a"
  },
  {
   "t": "2026-08-22T08:26:07Z",
   "kind": "review_comment",
   "who": "polespinasa",
   "assoc": "MEMBER",
   "path": "src/rpc/client.cpp",
   "commit": "a1f60713fdfae0e0751d0427e0508736a3ff955e",
   "in_reply_to": 3833241606,
   "text": "Fair enough, changed to `sat_vb`"
  },
  {
   "t": "2026-09-10T07:51:45Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "ccddd8a60f57a0be2f3649191919b20cf428e55f"
  },
  {
   "t": "2026-09-10T09:25:21Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "234971ad277ff0dad41304d7d48a62d28d7650fe"
  },
  {
   "t": "2026-09-10T09:40:02Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "ccddd8a60f57a0be2f3649191919b20cf428e55f"
  },
  {
   "t": "2026-09-10T12:18:45Z",
   "kind": "force_push",
   "who": "polespinasa",
   "commit": "2d136c58e7336906f5fdeb00ea606f607c0fa776"
  }
 ],
 "labels_log": [
  {
   "t": "2025-10-30T00:39:45Z",
   "action": "labeled",
   "label": "RPC/REST/ZMQ",
   "who": "DrahtBot"
  },
  {
   "t": "2025-12-27T17:45:00Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-18T17:23:06Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-18T18:10:26Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-18T21:13:47Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-29T11:39:40Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-31T16:33:37Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-31T17:31:24Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-31T17:39:43Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-20T01:24:22Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-20T07:46:15Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-21T08:42:24Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-21T11:32:43Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-21T12:54:10Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-21T13:18:27Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-09T16:35:47Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-10T08:55:31Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-10T09:49:35Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-10T13:20:36Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2025-10-30T00:43:34Z",
   "kind": "renamed",
   "who": "polespinasa",
   "from": "rpc: [PoC] Optionally print feerates in sat/vb",
   "to": "rpc: Optionally print feerates in sat/vb"
  }
 ],
 "text_chars": 20579,
 "text_tokens_estimate": 5144,
 "changed_paths": [
  "src/core_io.cpp",
  "src/core_io.h",
  "src/rpc/client.cpp",
  "src/rpc/fees.cpp",
  "src/rpc/mempool.cpp",
  "src/rpc/mempool.h",
  "src/rpc/net.cpp",
  "src/test/rpc_tests.cpp",
  "test/functional/feature_fee_estimation.py",
  "test/functional/rpc_estimatefee.py"
 ],
 "files": [
  {
   "path": "src/core_io.cpp",
   "add": 26,
   "del": 0
  },
  {
   "path": "src/core_io.h",
   "add": 10,
   "del": 0
  },
  {
   "path": "src/rpc/client.cpp",
   "add": 5,
   "del": 1
  },
  {
   "path": "src/rpc/fees.cpp",
   "add": 25,
   "del": 10
  },
  {
   "path": "src/rpc/mempool.cpp",
   "add": 12,
   "del": 10
  },
  {
   "path": "src/rpc/mempool.h",
   "add": 3,
   "del": 1
  },
  {
   "path": "src/rpc/net.cpp",
   "add": 8,
   "del": 3
  },
  {
   "path": "src/test/rpc_tests.cpp",
   "add": 36,
   "del": 0
  },
  {
   "path": "test/functional/feature_fee_estimation.py",
   "add": 23,
   "del": 21
  },
  {
   "path": "test/functional/rpc_estimatefee.py",
   "add": 10,
   "del": 10
  }
 ],
 "test_lines": 100,
 "git": {
  "head": "2d136c58e7336906f5fdeb00ea606f607c0fa776",
  "head_matches_backup": true,
  "base": "fc4f35fdce4d65696be6bf34003dbcac1f2f64bf",
  "commits": [
   {
    "sha": "4812e3735c",
    "subject": "rpc: Add ValueFromFeeRate function to core_write",
    "files": 2,
    "add": 36,
    "del": 0
   },
   {
    "sha": "cf6704c653",
    "subject": "test: add unit tests for ValueFromFeeRate",
    "files": 1,
    "add": 36,
    "del": 0
   },
   {
    "sha": "5f244f61ca",
    "subject": "rpc: let MempoolInfoToJSON to return feerates in sat/vb",
    "files": 2,
    "add": 7,
    "del": 6
   },
   {
    "sha": "1615dbe8e3",
    "subject": "rpc: allow getmempoolinfo to return feerates in sat/vb",
    "files": 2,
    "add": 9,
    "del": 5
   },
   {
    "sha": "7d9f800390",
    "subject": "rpc: allow getnetworkinfo to return feerates in sat/vb",
    "files": 2,
    "add": 9,
    "del": 3
   },
   {
    "sha": "831ef481eb",
    "subject": "rpc: allow estimatesmartfee to return feerates in sat/vb",
    "files": 4,
    "add": 41,
    "del": 36
   },
   {
    "sha": "2d136c58e7",
    "subject": "rpc: allow estimaterawfee to return feerates in sat/vB",
    "files": 2,
    "add": 20,
    "del": 6
   }
  ],
  "patch_truncated": true
 },
 "input_hash": "2ac2ae4d205bf180",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}