<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Susan Githaiga</title>
    <description>The latest articles on DEV Community by Susan Githaiga (@susangithaigan).</description>
    <link>https://dev.to/susangithaigan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1095882%2F2dac7a31-72e5-4268-aa94-92af21e4ac3f.jpg</url>
      <title>DEV Community: Susan Githaiga</title>
      <link>https://dev.to/susangithaigan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/susangithaigan"/>
    <language>en</language>
    <item>
      <title>Using the Lightning Anchor Fee Bumping Service: A Frontend Walkthrough</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Tue, 31 Mar 2026 06:40:09 +0000</pubDate>
      <link>https://dev.to/susangithaigan/using-the-lightning-anchor-fee-bumping-service-a-frontend-walkthrough-ge1</link>
      <guid>https://dev.to/susangithaigan/using-the-lightning-anchor-fee-bumping-service-a-frontend-walkthrough-ge1</guid>
      <description>&lt;p&gt;&lt;em&gt;Welcome to Part 6 of my Lightning Anchor Fee Outputs Series&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;In the previous articles, we built the entire backend for a Lightning powered CPFP fee bumping service. From Bitcoin RPC integration, to LND invoice creation, to broadcasting the child transaction. &lt;/p&gt;

&lt;p&gt;In this final part, I will walk you through the frontend and show you exactly how to use the service end to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Service Does
&lt;/h2&gt;

&lt;p&gt;When a Bitcoin transaction gets stuck in the mempool due to low fees, you can use Child-Pays-For-Parent (CPFP) to accelerate/unstuck it. Our service uses the &lt;strong&gt;anchor output&lt;/strong&gt; as the input for a child transaction that pays a higher fee. You pay for this service via a Lightning invoice, and the service broadcasts the fee-bumping transaction on your behalf.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before using the frontend, make sure the following are running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker containers: &lt;code&gt;bitcoin-regtest&lt;/code&gt;, &lt;code&gt;lnd-alice&lt;/code&gt;, &lt;code&gt;postgres-db&lt;/code&gt;, &lt;code&gt;redis-cache&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Backend server on port 3000 (&lt;code&gt;npm run dev&lt;/code&gt; in &lt;code&gt;/backend&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Frontend on port 5173 (&lt;code&gt;npm run dev&lt;/code&gt; in &lt;code&gt;/frontend&lt;/code&gt;)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start all Docker services&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;backend
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Terminal 1: Start backend&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;backend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev

&lt;span class="c"&gt;# Terminal 2: Start frontend&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;frontend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1: The Status Dashboard
&lt;/h2&gt;

&lt;p&gt;When you open the app at &lt;a href="http://localhost:5173" rel="noopener noreferrer"&gt;&lt;code&gt;http://localhost:5173&lt;/code&gt;&lt;/a&gt; and click on &lt;a href="http://localhost:5173/app" rel="noopener noreferrer"&gt;&lt;code&gt;Get Started&lt;/code&gt;&lt;/a&gt;, the first thing you'll notice on the right side is the &lt;strong&gt;Status Dashboard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfym2ajj0v9c03qagdty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfym2ajj0v9c03qagdty.png" alt="App overview showing the Transaction Input form and Status Dashboard" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the backend syncs to the regtest blockchain, the dashboard shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Block Height&lt;/strong&gt;: The current regtest block height, confirming Bitcoin Core is synced&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network&lt;/strong&gt;: &lt;code&gt;regtest&lt;/code&gt; for local development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mempool Monitor&lt;/strong&gt;: Shows &lt;code&gt;Inactive&lt;/code&gt; until a transaction is submitted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Block Height shows a number (861 in our case), your backend is successfully connected to the regtest blockchain network.&lt;br&gt;
If it shows an error, check that your backend is running. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywcsw30p4qgshejaie0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywcsw30p4qgshejaie0i.png" alt="App overview showing the Transaction Input form and Status Dashboard" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 2: Creating a Test Transaction
&lt;/h2&gt;

&lt;p&gt;Before using the fee bumper, you need a stuck transaction to work with. In regtest, we create one manually:&lt;/p&gt;

&lt;p&gt;First, get the service wallet address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SERVICE_ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:3000/api/v1/bitcoin/wallet-address | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.data.address'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Service wallet address: &lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ADDR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then send 330 sats to it and capture the TXID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TXID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcwallet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;testwallet sendtoaddress &lt;span class="nv"&gt;$SERVICE_ADDR&lt;/span&gt; 0.00000330&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"TXID: &lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Expected output
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe24tsnnqdpvvgcizb8ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe24tsnnqdpvvgcizb8ef.png" alt="Expected output" width="800" height="138"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the TXID(Transaction Id) as you'll need it in the next step. My TXID in this case is &lt;code&gt;59224b8385c5dd882f0a1f48b11e05fbe0e9fdab414344f8fd5f725b6571661a&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Estimating the Fee
&lt;/h2&gt;

&lt;p&gt;Paste your TXID into the &lt;strong&gt;Transaction ID&lt;/strong&gt; field. Set the &lt;strong&gt;Anchor Output Index&lt;/strong&gt; (usually &lt;code&gt;0&lt;/code&gt;) and choose a &lt;strong&gt;Target Fee Rate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Three fee rate presets are available:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Preset&lt;/th&gt;
&lt;th&gt;Rate&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;1 sat/vB&lt;/td&gt;
&lt;td&gt;Non-urgent, patient&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;10 sat/vB&lt;/td&gt;
&lt;td&gt;Standard confirmation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;50 sat/vB&lt;/td&gt;
&lt;td&gt;Urgent, next block&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Click &lt;strong&gt;Estimate Cost&lt;/strong&gt;. The &lt;strong&gt;Fee Estimation&lt;/strong&gt; panel below will populate with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Child Fee Needed&lt;/strong&gt;: The fee the child transaction must pay&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Fee Needed&lt;/strong&gt;: Child fee plus the service fee&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parent Fee Rate&lt;/strong&gt;: What the stuck transaction is currently paying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feasibility indicator&lt;/strong&gt;: confirms the anchor output can cover the bump&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l1uwel35lkxph1o3w82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l1uwel35lkxph1o3w82.png" alt="Fee Estimation panel showing 2369 sats child fee, 2510 sats total, and a green Feasible badge" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Feasible vs Non-Feasible Transactions
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;330-sat anchor feasible&lt;/strong&gt; badge tells you whether the service can actually bump your transaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feasible&lt;/strong&gt; (green badge) means the anchor output in your transaction is a valid 330-satoshi output that the service can spend as the CPFP child input. The child transaction can pay enough to bring the combined package fee rate up to your target. You can proceed to generate an invoice and broadcast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not feasible&lt;/strong&gt; (red badge) means one of the following is true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The output at the specified index is not a 330-satoshi anchor output&lt;/li&gt;
&lt;li&gt;The parent transaction's fee rate is already at or above your target. There is nothing to bump&lt;/li&gt;
&lt;li&gt;The TXID does not exist in the mempool or the local blockchain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you see a non-feasible result, double-check that you are using the correct &lt;strong&gt;Anchor Output Index&lt;/strong&gt;, try &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, or &lt;code&gt;2&lt;/code&gt; depending on which output in the transaction is the anchor. Also confirm the transaction is genuinely stuck by checking its current fee rate against your target..&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample non-feasible output&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13wfhqlhtd77a8xpbkyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13wfhqlhtd77a8xpbkyf.png" alt="Not feasible" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 4: Generating the Lightning Invoice
&lt;/h2&gt;

&lt;p&gt;Once the estimate looks good, scroll down and click &lt;strong&gt;Generate Invoice&lt;/strong&gt;. The backend calls LND via gRPC to create a Lightning invoice for the exact fee amount.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Lightning Invoice&lt;/strong&gt; panel appears with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;QR code&lt;/strong&gt; you can scan with any Lightning wallet&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;amount&lt;/strong&gt; in satoshis&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;payment hash&lt;/strong&gt; for tracking&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Copy Invoice&lt;/strong&gt; button to copy the raw &lt;code&gt;lnbc...&lt;/code&gt; invoice string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5ux3hrax2aqjdprfgdk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5ux3hrax2aqjdprfgdk.png" alt="Lightning Invoice section showing QR code, amount, payment hash, and Copy Invoice button" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In production, your user scans this QR code with their Lightning wallet and pays. In regtest, you can pay it programmatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Pay the invoice from a second LND node (if you have one set up)&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;lnd lncli &lt;span class="nt"&gt;--network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;regtest payinvoice &amp;lt;INVOICE_STRING&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojdngpilc27vrvg93xfo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojdngpilc27vrvg93xfo.png" alt="self-payments not allowed" width="800" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error &lt;code&gt;self-payments not allowed&lt;/code&gt; means you're trying to pay an invoice from the same LND node that created it. You can't pay yourself on Lightning. &lt;/p&gt;

&lt;h2&gt;
  
  
  Coming Up Next
&lt;/h2&gt;

&lt;p&gt;In the next article, we step out of regtest and into the real Bitcoin network, mainnet.&lt;/p&gt;

&lt;p&gt;To continue from here on mainnet, we'll need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spin up a Lightning node connected to the live Bitcoin network&lt;/li&gt;
&lt;li&gt;Migrate the project configuration from regtest to mainnet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By doing this, we'll be able to handle real invoices paid by real wallets&lt;/p&gt;

&lt;p&gt;Part 7: Going Live and Migrating to Mainnet →&lt;br&gt;
(Link will be updated when the article is published)&lt;/p&gt;




&lt;h2&gt;
  
  
  What We Built Across This Series
&lt;/h2&gt;

&lt;p&gt;Over six parts, we've gone from understanding what anchor outputs are to building and using a fully functional fee bumping service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parts 1–2&lt;/strong&gt;: What anchor outputs are and why they exist in Lightning commitment transactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt;: Setting up the backend, Bitcoin RPC integration, and Docker environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt;: Integrating LND for Lightning invoice creation and payment verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt;: Building the CPFP transaction constructor and broadcast endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 6&lt;/strong&gt; &lt;em&gt;(this article)&lt;/em&gt;: Walking through the React frontend and completing the end-to-end flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full source code is on GitHub. If you found this series useful, feel free to connect on &lt;a href="https://www.linkedin.com/in/susangithaiga" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or follow along on &lt;a href="https://dev.to/susangithaigan"&gt;Dev.to&lt;/a&gt;.&lt;/p&gt;




</description>
      <category>bitcoin</category>
      <category>tutorial</category>
      <category>frontend</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Building an Anchor Output Fee Bumping Service — Part 5: CPFP Transactions and Broadcasting</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Thu, 12 Mar 2026 10:33:48 +0000</pubDate>
      <link>https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-5-cpfp-transactions-and-broadcasting-2mpb</link>
      <guid>https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-5-cpfp-transactions-and-broadcasting-2mpb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-4-lightning-payment-integration-5615"&gt;Part 4&lt;/a&gt;, we got Lightning payments working. The service can now generate invoices, track payment status, and verify that users have paid before taking action. That's the business logic sorted.&lt;/p&gt;

&lt;p&gt;But we stopped right before the interesting part: actually building and broadcasting the CPFP transaction that does the fee bumping.&lt;/p&gt;

&lt;p&gt;That's what Part 5 covers. By the end of this article, you'll have a service that can construct a valid Child-Pays-For-Parent transaction using &lt;code&gt;bitcoinjs-lib&lt;/code&gt;, understand why the signing fails (and what that teaches us about Lightning), and see the complete flow from stuck transaction to broadcast-ready CPFP.&lt;/p&gt;

&lt;p&gt;We're still on regtest, still safe, still learning.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📂 Source code: &lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs" rel="noopener noreferrer"&gt;lightning-anchor-fee-outputs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Where We Left Off
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-4-lightning-payment-integration-5615"&gt;Part 4&lt;/a&gt; ended with three working Lightning endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create invoice ✓&lt;/li&gt;
&lt;li&gt;Check payment status ✓
&lt;/li&gt;
&lt;li&gt;Decode invoice ✓&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We verified the payment flow works, even though we can't actually pay invoices on single-node regtest. The verification logic is solid.&lt;/p&gt;

&lt;p&gt;What we haven't done yet is build the actual CPFP transaction. That's the piece that makes this more than just an invoice generator.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the CPFP Transaction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs/blob/143c541d14c4e63d0e344d0e2922a890e5ba19b6/src/services/feebump/cpfp.ts" rel="noopener noreferrer"&gt;&lt;code&gt;src/services/feebump/cpfp.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where theory meets practice. We're going to take a parent transaction with an anchor output and construct a child transaction that spends it, paying enough fee to bump both transactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Use PSBT?
&lt;/h3&gt;

&lt;p&gt;Before jumping into code, let's talk about PSBTs (Partially Signed Bitcoin Transactions). You might wonder why we need this abstraction when we could just build a transaction directly.&lt;/p&gt;

&lt;p&gt;The reason is simple: most Bitcoin transactions these days involve multiple parties or require careful coordination. PSBTs are Bitcoin's standard format for "I've built this transaction, but it's not ready to broadcast yet."&lt;/p&gt;

&lt;p&gt;For our CPFP service:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build the transaction structure&lt;/li&gt;
&lt;li&gt;Add the anchor output as an input
&lt;/li&gt;
&lt;li&gt;Calculate the change output&lt;/li&gt;
&lt;li&gt;Sign it (this is where things get interesting)&lt;/li&gt;
&lt;li&gt;Extract the final transaction&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Complete createCPFPTransaction Method
&lt;/h3&gt;

&lt;p&gt;The method follows nine distinct steps. Let's walk through each one.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Fetch the Parent Transaction
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentTx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bitcoinService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentTxid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;parentTx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Parent transaction not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start by fetching the full parent transaction from Bitcoin Core. We need this to access the anchor output's details.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Verify the Anchor Output
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchorOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parentTx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vout&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anchorVout&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;anchorOutput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Anchor output &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anchorVout&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; not found`&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchorValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchorOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchorValue&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;330&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Output is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;anchorValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; sats, not 330 (not an anchor output)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This catches mistakes early. If someone passes the wrong output index, or if they're trying to spend something that isn't an anchor, we bail out before doing any work.&lt;/p&gt;

&lt;p&gt;Bitcoin Core returns values in BTC (e.g., 0.00000330), so we multiply by 100,000,000 to convert to satoshis.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Calculate Parent Transaction Fees
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parentTx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vsize&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;parentTx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentFee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateActualFee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentTxid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentFeeRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parentFee&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;parentFee&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;parentSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to know what the parent is already paying. The &lt;code&gt;calculateActualFee()&lt;/code&gt; helper fetches all the parent's inputs, sums them up, subtracts the outputs, and returns the difference. That's the fee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Calculating the actual fee requires fetching each input's previous transaction to determine input values. This is why we have a separate helper method for it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4: Calculate Required Child Fees
&lt;/h4&gt;

&lt;p&gt;Remember the package fee rate concept from Part 2? Miners care about the combined fee rate of parent and child transactions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1 input + 1 output ≈ 110 vbytes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parentSize&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;childSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalFeeNeeded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;targetFeeRate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childFeeNeeded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;totalFeeNeeded&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;parentFee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The child transaction size estimate (110 vbytes) assumes one input (the anchor) and one output (change back to our wallet). In reality, it might be slightly different, but this is conservative.&lt;/p&gt;

&lt;p&gt;Here's the math:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parent is 200 vbytes paying 200 sats (1 sat/vbyte)&lt;/li&gt;
&lt;li&gt;We want 10 sat/vbyte for the package&lt;/li&gt;
&lt;li&gt;Total package: (200 + 110) × 10 = 3,100 sats needed&lt;/li&gt;
&lt;li&gt;Child must pay: 3,100 - 200 = 2,900 sats&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 5: Check the 330-Sat Limitation
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;childFeeNeeded&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;anchorValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Child fee needed (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;childFeeNeeded&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; sats) exceeds anchor value (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;anchorValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; sats). Need additional inputs.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the constraint we kept running into during testing. At 1 sat/vbyte, the anchor usually covers it. At 10 sat/vbyte or higher, you need additional wallet inputs.&lt;/p&gt;

&lt;p&gt;We also check an optional &lt;code&gt;maxFee&lt;/code&gt; parameter to prevent accidentally burning too much in fees.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 6: Build the PSBT
&lt;/h4&gt;

&lt;p&gt;Now we construct the actual transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;psbt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;bitcoin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Psbt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Add anchor as input&lt;/span&gt;
&lt;span class="nx"&gt;psbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addInput&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentTxid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;anchorVout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;witnessUtxo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchorOutput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scriptPubKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchorValue&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;witnessUtxo&lt;/code&gt; field is important. For SegWit inputs (which anchor outputs are), we only need the output being spent, not the entire previous transaction. This makes PSBTs more efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why BigInt?&lt;/strong&gt; Bitcoin.js-lib v6+ uses BigInt for satoshi amounts to avoid floating-point precision issues.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 7: Add Change Output
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;changeAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anchorValue&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;childFeeNeeded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changeAmount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;changeAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bitcoin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;p2wpkh&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;walletKeyPair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;psbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOutput&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;changeAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changeAmount&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there's any satoshis left after paying the fee (there usually is at low fee rates), we send it back to our wallet as change. If the fee consumes the entire 330 sats, we skip adding an output.&lt;/p&gt;

&lt;p&gt;The wallet address is generated from our keypair using Pay-to-Witness-Public-Key-Hash (P2WPKH), which is standard SegWit.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 8: The Signing Challenge
&lt;/h4&gt;

&lt;p&gt;Here's where things get educational:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;psbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;walletKeyPair&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;psbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validateSignaturesOfInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;psbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finalizeAllInputs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Transaction signing failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. This is expected - the anchor output belongs to the Lightning channel, not our test wallet.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We generate a random keypair in the constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;walletKeyPair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ECPair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeRandom&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This signing will &lt;strong&gt;always fail&lt;/strong&gt;. And that's actually useful for learning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does it fail?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The anchor output doesn't belong to this random key. It belongs to the Lightning channel participants. In a real Lightning channel, only the channel partners have the private keys needed to spend anchor outputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does this teach us?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In production, this service would need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request the signature from the user's LND node via &lt;code&gt;lncli signrawtransaction&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Have the user provide a pre-signed CPFP transaction template&lt;/li&gt;
&lt;li&gt;Use a collaborative signing setup where the service has partial authority&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For learning purposes, the transaction &lt;em&gt;structure&lt;/em&gt; is perfect. The fee calculations are correct. The inputs and outputs are valid. Only the signature is invalid because we don't have the real keys.&lt;/p&gt;

&lt;p&gt;This is exactly how it works in real Lightning: the service can build the transaction, but only the channel participants can sign it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 9: Extract the Transaction
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childTx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;psbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extractTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childTxHex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;childTx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHex&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childTxid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;childTx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;childTxid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;childTxHex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;feePaid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;childFeeNeeded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If signing succeeded (which it won't in our test setup), we'd extract the raw transaction as a hex string. This is what gets broadcast to the Bitcoin network.&lt;/p&gt;

&lt;p&gt;The transaction ID is deterministic—it's the hash of the transaction data.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Helper Method: calculateActualFee
&lt;/h3&gt;

&lt;p&gt;This method deserves special attention because it's not obvious:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;calculateActualFee&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bitcoinService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;inputValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;outputValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Sum all outputs (easy - they're in the transaction)&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vout&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;outputValue&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;vout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Sum all inputs (harder - need to fetch previous transactions)&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vin&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prevTx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bitcoinService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prevOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prevTx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vout&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vout&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nx"&gt;inputValue&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;prevOut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputValue&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;outputValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bitcoin transactions don't store input amounts directly. To find out how much an input is worth, you have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Look at which previous transaction it's spending&lt;/li&gt;
&lt;li&gt;Fetch that previous transaction&lt;/li&gt;
&lt;li&gt;Look at the specific output being spent&lt;/li&gt;
&lt;li&gt;That output's value is your input value&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is why calculating actual fees requires multiple RPC calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing the CPFP Builder
&lt;/h3&gt;

&lt;p&gt;Let's create a test transaction and try to build a CPFP for it. First, we'll need to load the wallet we created on this series &lt;code&gt;testwallet&lt;/code&gt; by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Load testwallet&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="se"&gt;\&lt;/span&gt;
  loadwallet &lt;span class="s2"&gt;"testwallet"&lt;/span&gt;

&lt;span class="c"&gt;# Expected output:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"testwallet"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;#Verify wallet is loaded&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="se"&gt;\&lt;/span&gt;
  listwallets

&lt;span class="c"&gt;# Expected output:&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"testwallet"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a test transaction with an anchor-like output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcwallet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;testwallet getnewaddress&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;TXID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcwallet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;testwallet sendtoaddress &lt;span class="nv"&gt;$ADDR&lt;/span&gt; 0.00000330&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Test TXID: &lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Expected output:&lt;/span&gt;
Test TXID: 20bdc7dd508d7fe8fc2569c..........
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Try to create CPFP with a low fee rate (feasible):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/feebump/create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;txid&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;anchorVout&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 0,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;targetFeeRate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 1
  }"&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Transaction signing failed: ... This is expected - the anchor output belongs to the Lightning channel, not our test wallet."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bnyfrcl6wvzvtk8wn5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bnyfrcl6wvzvtk8wn5k.png" alt="Transaction signing" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That error message is honest about what's happening. The transaction was built correctly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✓ Anchor output verified&lt;/li&gt;
&lt;li&gt;✓ Fees calculated properly
&lt;/li&gt;
&lt;li&gt;✓ Transaction structure valid&lt;/li&gt;
&lt;li&gt;✓ PSBT constructed&lt;/li&gt;
&lt;li&gt;✗ Signing failed (expected—we don't have the keys)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Try with a high fee rate (unfeasible):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/feebump/create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;txid&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;anchorVout&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 0,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;targetFeeRate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 50
  }"&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Child fee needed (14020 sats) exceeds anchor value (330 sats). Need additional inputs."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxvbfze36ysr7jflk0qml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxvbfze36ysr7jflk0qml.png" alt="Child fee needed" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This confirms the math from Part 2. At 50 sat/vbyte, you'd need roughly 13,000 sats in fees. The anchor only has 330. The service catches this in Step 5 before even attempting to build the transaction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Payment-Gated Broadcasting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs/blob/143c541d14c4e63d0e344d0e2922a890e5ba19b6/src/api/v1/broadcast.ts" rel="noopener noreferrer"&gt;&lt;code&gt;src/api/v1/broadcast.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The broadcast endpoint ties everything together. It's the final piece that turns this from a dev tool into a viable service.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Three-Step Flow
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Verify Payment&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lightningPaymentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkPaymentStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;paymentStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;402&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Payment not received&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paymentRequired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;paymentHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTTP 402 literally means "Payment Required." It's rarely used, but this is exactly what it's for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create CPFP&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cpfpResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cpfpService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCPFPTransaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;parentTxid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;anchorVout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchorVout&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;targetFeeRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetFeeRate&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If payment was confirmed but the CPFP fails to build (maybe the fee is too high, or the anchor is already spent), we return an error but note that payment was received. This matters for customer support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Broadcast&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;broadcastResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bitcoinService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendRawTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cpfpResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;childTxHex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CPFP transaction broadcast successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;parentTxid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;childTxid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cpfpResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;childTxid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;feePaid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cpfpResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;feePaid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;paymentHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we get here, everything worked. The transaction is in the mempool. Both parent and child will confirm together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing the Broadcast Endpoint
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create test transaction&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcwallet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;testwallet getnewaddress&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;TXID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcwallet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;testwallet sendtoaddress &lt;span class="nv"&gt;$ADDR&lt;/span&gt; 0.00000330&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Test Transaction ID: &lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test Transaction ID: 25cebabb25670aa0ab2bc25190dec91....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Create a real Lightning invoice (but don't pay it)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;INVOICE_RESPONSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/lightning/create-invoice &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"amountSats": 250, "memo": "CPFP test"}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$INVOICE_RESPONSE&lt;/span&gt; | jq

&lt;span class="k"&gt;**&lt;/span&gt;Expected response&lt;span class="k"&gt;**&lt;/span&gt;

&lt;span class="o"&gt;![&lt;/span&gt;Create a real Lightning invoice]&lt;span class="o"&gt;(&lt;/span&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jy8xvegfimxoizn3hr2k.png&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Extract payment hash&lt;/span&gt;
&lt;span class="nv"&gt;PAYMENT_HASH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$INVOICE_RESPONSE&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.data.paymentHash'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Payment Hash: &lt;/span&gt;&lt;span class="nv"&gt;$PAYMENT_HASH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment Hash: 5d04662cb0b736f7574d85c1371039..............
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Verify invoice is unpaid&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:3000/api/v1/lightning/payment/&lt;span class="nv"&gt;$PAYMENT_HASH&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"paymentHash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5d04662cb0b736f7574d85c1371039.............."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"paid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6n7ohe573p7mfpvhdsz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6n7ohe573p7mfpvhdsz.png" alt="Verify invoice is unpaid" width="800" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Try to broadcast without payment&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/feebump/broadcast &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;txid&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;anchorVout&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 0,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;targetFeeRate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 1,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;paymentHash&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$PAYMENT_HASH&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
  }"&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Payment not received"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"paymentRequired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"paymentHash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5d04662cb0b736f7574d85c1371039.............."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4rct8fs32ubercqguzj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4rct8fs32ubercqguzj.png" alt="Payment not received" width="800" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! The service correctly blocks unpaid requests with HTTP 402 (Payment Required).&lt;/p&gt;




&lt;h2&gt;
  
  
  Enhanced Bitcoin Service
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs/blob/143c541d14c4e63d0e344d0e2922a890e5ba19b6/src/services/bitcoin/node.ts" rel="noopener noreferrer"&gt;&lt;code&gt;src/services/bitcoin/node.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Bitcoin service needed one crucial method: &lt;code&gt;sendRawTransaction()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;sendRawTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hexString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Broadcasting transaction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hexString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;txid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sendrawtransaction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hexString&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction broadcast successful&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;txid&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction broadcast failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Broadcast failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wraps Bitcoin Core's RPC call, adds logging, and handles errors. Simple but essential.&lt;/p&gt;

&lt;p&gt;We also added &lt;code&gt;testMempoolAccept()&lt;/code&gt; for validating transactions without broadcasting them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;testMempoolAccept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hexString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testmempoolaccept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;hexString&lt;/span&gt;&lt;span class="p"&gt;]]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is great for debugging. You can check if a transaction would be accepted before actually sending it to the network.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We've Built
&lt;/h2&gt;

&lt;p&gt;Let's step back and look at the complete system:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;User's Lightning channel force-closes&lt;/li&gt;
&lt;li&gt;Commitment transaction gets stuck in mempool (low fee)&lt;/li&gt;
&lt;li&gt;User requests fee bump estimate&lt;/li&gt;
&lt;li&gt;Service calculates cost: "2,500 sats to bump to 10 sat/vbyte"&lt;/li&gt;
&lt;li&gt;Service generates Lightning invoice for 2,500 sats&lt;/li&gt;
&lt;li&gt;User pays invoice from their Lightning wallet&lt;/li&gt;
&lt;li&gt;Service verifies payment received&lt;/li&gt;
&lt;li&gt;Service builds CPFP transaction&lt;/li&gt;
&lt;li&gt;Service broadcasts CPFP&lt;/li&gt;
&lt;li&gt;Both transactions confirm together&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Real-time mempool monitoring&lt;/li&gt;
&lt;li&gt;✅ Accurate fee calculation&lt;/li&gt;
&lt;li&gt;✅ Lightning invoice generation&lt;/li&gt;
&lt;li&gt;✅ Payment verification&lt;/li&gt;
&lt;li&gt;✅ CPFP transaction building&lt;/li&gt;
&lt;li&gt;✅ Payment-gated broadcasting&lt;/li&gt;
&lt;li&gt;✅ Complete error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What Doesn't Work (Yet)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⚠️ Transaction signing (needs Lightning keys)&lt;/li&gt;
&lt;li&gt;⚠️ Actual payment testing (need multiple nodes)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The 330-Sat Limit is Real
&lt;/h3&gt;

&lt;p&gt;We confirmed through testing what the math told us: 330 sats can bump fees to roughly 1-2 sat/vbyte. For higher rates, you need more inputs. This isn't a bug; it's a fundamental constraint.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Payment Verification Matters
&lt;/h3&gt;

&lt;p&gt;Without the payment check in the broadcast endpoint, anyone could use the service for free. That one HTTP 402 check is what makes this sustainable.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. PSBTs are the Standard
&lt;/h3&gt;

&lt;p&gt;Every modern Bitcoin tool uses Partially Signed Bitcoin Transactions. Learning this pattern once helps with Lightning wallets, hardware wallets, multi-sig setups, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Signing is the Hard Part
&lt;/h3&gt;

&lt;p&gt;Building the transaction is straightforward. Getting the right signature is the challenge. This is true for Lightning, multi-sig, hardware wallets, and pretty much every interesting Bitcoin use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Error Handling Takes Time
&lt;/h3&gt;

&lt;p&gt;Look at the codebase. Half of it is error handling, validation, and logging. That's not overhead; it's what makes the service maintainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next: Part 6
&lt;/h2&gt;

&lt;p&gt;In the final article of this series, we'll tackle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database Integration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save all CPFP transactions&lt;/li&gt;
&lt;li&gt;Track payment history&lt;/li&gt;
&lt;li&gt;Build an audit trail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Frontend Dashboard&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React UI for the complete flow&lt;/li&gt;
&lt;li&gt;QR codes for Lightning invoices&lt;/li&gt;
&lt;li&gt;Real-time status updates&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Clone the repo: &lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs" rel="noopener noreferrer"&gt;lightning-anchor-fee-outputs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the README setup, then experiment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try different fee rates&lt;/li&gt;
&lt;li&gt;Watch the 330-sat limit in action&lt;/li&gt;
&lt;li&gt;Test the payment verification&lt;/li&gt;
&lt;li&gt;Break things and learn from the errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building this taught me more than reading specs ever could. The best way to understand how Lightning fee bumping works is to implement it yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We started Part 1 understanding the problem: Lightning transactions can get stuck.&lt;/p&gt;

&lt;p&gt;We're ending Part 5 with a working solution: a payment-gated service that can bump those transactions using CPFP.&lt;/p&gt;

&lt;p&gt;The core logic is done. Part 6 will add the polish: persistence, monitoring, and a UI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Previous Articles in This Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en"&gt;Part 1: Lightning Network Anchor Outputs Explained: The Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j"&gt;Part 2: Understanding Lightning Network Anchor Outputs - The Technical Deep Dive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef"&gt;Part 3: Building an Anchor Output Fee Bumping Service - Setup &amp;amp; Implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-4-lightning-payment-integration-31hf"&gt;Part 4: Lightning Payment Integration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitcoinops.org/en/topics/anchor-outputs/" rel="noopener noreferrer"&gt;Bitcoin Optech: Anchor Outputs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bitcoinjs/bitcoinjs-lib" rel="noopener noreferrer"&gt;bitcoinjs-lib Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://api.lightning.community/" rel="noopener noreferrer"&gt;LND REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki" rel="noopener noreferrer"&gt;BIP 174: PSBT Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitcoinops.org/en/topics/cpfp/" rel="noopener noreferrer"&gt;Child-Pays-For-Parent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Questions? Found a bug? Have improvements?&lt;/strong&gt; Drop them in the comments.**&lt;/p&gt;

&lt;p&gt;See you in Part 6 &lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>blockchain</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building an Anchor Output Fee Bumping Service — Part 4: Lightning Payment Integration</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Tue, 10 Mar 2026 10:18:24 +0000</pubDate>
      <link>https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-4-lightning-payment-integration-5615</link>
      <guid>https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-4-lightning-payment-integration-5615</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef"&gt;Part 3&lt;/a&gt;, we built a service that could watch the Bitcoin mempool, spot anchor outputs, and estimate whether a fee bump was feasible. It was a solid foundation, but it stopped short of actually doing anything. No transaction was built. No fee was bumped. And crucially, no one was paying for the service.&lt;/p&gt;

&lt;p&gt;Part 4 fixes all of that.&lt;/p&gt;

&lt;p&gt;By the end of this article, the service will generate Lightning invoices, verify payments, construct real CPFP transactions using &lt;a href="https://github.com/bitcoinjs/bitcoinjs-lib" rel="noopener noreferrer"&gt;&lt;code&gt;bitcoinjs-lib&lt;/code&gt;&lt;/a&gt;, and only broadcast those transactions once payment is confirmed. In other words: a working, payment-gated fee bumping service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We are using Bitcoin &lt;code&gt;regtest&lt;/code&gt;&lt;/strong&gt;, so there's no risk to real funds. The setup is similar to how transactions would work on the mainnet network.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📂 All source code for this project is available here: &lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs" rel="noopener noreferrer"&gt;Anchor Outputs fee-bumping service&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;File paths referenced throughout this article correspond directly to this repository.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Recap: Where Part 3 Left Us
&lt;/h2&gt;

&lt;p&gt;Before diving in, here's a quick summary of what was already working:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bitcoin Core RPC integration&lt;/strong&gt;: the service could connect to a regtest node, query the mempool, and fetch transaction details&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anchor output detection&lt;/strong&gt;: any 330-satoshi output in the mempool was flagged as a potential anchor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fee estimation&lt;/strong&gt;: given a target fee rate, the service calculated whether the 330-sat anchor alone was enough, or whether additional wallet inputs would be needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic REST API&lt;/strong&gt;: endpoints to start/stop monitoring and request fee estimates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What was &lt;em&gt;missing&lt;/em&gt; was everything that comes after the estimate: building the child transaction, collecting payment, and broadcasting. That's what we're adding now.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We're Adding in Part 4
&lt;/h2&gt;

&lt;p&gt;Here's what's new:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lightning invoice generation&lt;/strong&gt;: charge users for the fee bump service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment verification&lt;/strong&gt;: confirm the invoice is settled before taking action&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPFP transaction builder&lt;/strong&gt;: construct the actual child transaction using &lt;a href="https://github.com/bitcoinjs/bitcoinjs-lib" rel="noopener noreferrer"&gt;&lt;code&gt;bitcoinjs-lib&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment-gated broadcast&lt;/strong&gt;: only broadcast the CPFP after payment is confirmed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The end result is a complete payment flow: a user's stuck transaction gets bumped, and they pay for it instantly over Lightning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Payment Flow
&lt;/h2&gt;

&lt;p&gt;Before walking through the code, it helps to see how all these pieces connect. Here is the complete flow a user goes through:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frds2eq81oefddyvmf9gh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frds2eq81oefddyvmf9gh.png" alt="Bitcoin CPFP tutorial" width="800" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key design decision here is that &lt;strong&gt;nothing irreversible happens until payment is confirmed&lt;/strong&gt;. The service checks the payment hash before building or broadcasting anything. This is what makes it viable as a service rather than just a dev tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  Starting the Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Installing New Dependencies
&lt;/h3&gt;

&lt;p&gt;The CPFP transaction builder needs a few new packages for constructing and signing Bitcoin transactions.&lt;/p&gt;

&lt;p&gt;Run the following in your project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;bitcoinjs-lib tiny-secp256k1 ecpair
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What each package does:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/bitcoinjs/bitcoinjs-lib" rel="noopener noreferrer"&gt;&lt;code&gt;bitcoinjs-lib&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Builds and encodes Bitcoin transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/bitcoinjs/tiny-secp256k1" rel="noopener noreferrer"&gt;&lt;code&gt;tiny-secp256k1&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Low-level elliptic curve cryptography (required by bitcoinjs-lib v6+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/bitcoinjs/ecpair" rel="noopener noreferrer"&gt;&lt;code&gt;ecpair&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Key pair management for signing transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Before Testing: Ensuring LND is Synced
&lt;/h2&gt;

&lt;p&gt;Before testing invoice creation, you need to ensure both Bitcoin Core and LND are properly synced. This is a common issue when working with regtest that can cause invoice creation to hang indefinitely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Load Bitcoin Wallet
&lt;/h3&gt;

&lt;p&gt;After restarting Docker containers, Bitcoin Core's wallet needs to be loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Load the wallet&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="se"&gt;\&lt;/span&gt;
  loadwallet &lt;span class="s2"&gt;"testwallet"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"testwallet"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Generate Blocks to Complete Sync
&lt;/h3&gt;

&lt;p&gt;Bitcoin Core may still be in Initial Block Download (IBD) mode, which prevents LND from syncing. Generate additional blocks to complete the sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate 100 blocks&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-generate&lt;/span&gt; 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Verify LND Sync Status
&lt;/h3&gt;

&lt;p&gt;Wait approximately 10 seconds after generating blocks, then check if LND has synced to the chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check LND sync status&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;lnd lncli &lt;span class="nt"&gt;--network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;regtest getinfo | &lt;span class="se"&gt;\&lt;/span&gt;
  jq &lt;span class="s1"&gt;'{synced_to_chain, block_height}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sample response:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fum18t60rzap1xwlnt2pi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fum18t60rzap1xwlnt2pi.png" alt="Check LND sync status" width="800" height="87"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The critical field is &lt;code&gt;"synced_to_chain": true&lt;/code&gt;. If this shows &lt;code&gt;false&lt;/code&gt;, LND is still waiting for Bitcoin Core to sync, and invoice creation will hang.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If sync is still false:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wait another 10-20 seconds&lt;/li&gt;
&lt;li&gt;Generate more blocks:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="nt"&gt;-generate&lt;/span&gt; 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Check LND logs:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs lnd &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once &lt;code&gt;synced_to_chain&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, you're ready to test invoice creation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: The Lightning Payment Service
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs/blob/143c541d14c4e63d0e344d0e2922a890e5ba19b6/src/services/lightning/payment.ts" rel="noopener noreferrer"&gt;&lt;code&gt;src/services/lightning/payment.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This service handles all communication with LND's REST API. It's intentionally kept separate from the existing &lt;code&gt;lnd.ts&lt;/code&gt;, which only handles node info; one file for node status, one for payment operations.&lt;/p&gt;

&lt;p&gt;The service does three things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invoice creation&lt;/strong&gt;: given an amount in sats, a memo, and an expiry time, it calls LND's &lt;code&gt;/v1/invoices&lt;/code&gt; endpoint and returns a &lt;code&gt;payment_request&lt;/code&gt; string the user can pay from any Lightning wallet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payment status checks&lt;/strong&gt;: given a payment hash, it queries LND to see whether that invoice has been settled. One important detail here: LND's REST API expects the payment hash encoded as base64, but Bitcoin tools generally work in hex. The service handles that conversion internally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payment polling&lt;/strong&gt;: rather than setting up a webhook infrastructure, the service simply checks payment status every 2 seconds until it's confirmed or a timeout is reached. This is simpler for regtest and works reliably, though a production system would benefit from webhooks for efficiency.&lt;/p&gt;

&lt;p&gt;A note on authentication: LND uses &lt;strong&gt;macaroons&lt;/strong&gt; , similar to API tokens, to authorize requests. The service reads the macaroon file from disk on startup and attaches it to every request as a header. In regtest, LND also uses a self-signed TLS certificate, so the HTTPS agent is configured to allow that. This should be handled properly with real certificates in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Exposing Lightning Endpoints
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; &lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs/blob/143c541d14c4e63d0e344d0e2922a890e5ba19b6/src/services/lightning/payment.ts" rel="noopener noreferrer"&gt;&lt;code&gt;src/api/v1/lightning-payment.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the service built, three REST endpoints expose its functionality:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/v1/lightning/create-invoice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generates a Lightning invoice for a given amount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/v1/lightning/payment/:hash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks whether a specific invoice has been paid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/v1/lightning/decode-invoice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns the decoded details of a payment request string&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are registered in &lt;code&gt;src/index.ts&lt;/code&gt; under the &lt;code&gt;/api/v1/lightning&lt;/code&gt; path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Invoice Creation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Create an invoice:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/lightning/create-invoice &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"amountSats": 2500, "memo": "Test"}'&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response includes a &lt;code&gt;payment_request&lt;/code&gt; (invoice) field starting with &lt;code&gt;lnbcrt&lt;/code&gt; (the regtest prefix) and a &lt;code&gt;paymentHash&lt;/code&gt; which you'll use to check status later:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokhxt8dykemlurc25dg7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokhxt8dykemlurc25dg7.png" alt="Terminal showing the curl command and the response with payment_request and paymentHash fields visible" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Payment Status Check
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Check payment status:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Replace with your actual payment hash&lt;/span&gt;
curl http://localhost:3000/api/v1/lightning/payment/702085df5684eab92460a1dbd9da797015d3fffcc179290f22c12b32facebbea | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before payment, this returns &lt;code&gt;"paid": false&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F56uj7gj7wr5hhff8gtun.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F56uj7gj7wr5hhff8gtun.png" alt="Payment status showing paid: false" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Limitation on Regtest
&lt;/h3&gt;

&lt;p&gt;On regtest with a single LND node and no open channels, you cannot actually pay invoices. In production, users would pay from their Lightning wallets, but for testing purposes, we can verify the broadcast logic works correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The payment verification logic works&lt;/strong&gt;. The service correctly checks payment status before broadcasting.&lt;/p&gt;

&lt;p&gt;To fully test the payment flow in a real environment, you would need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A second Lightning node (or use a testnet/mainnet Lightning wallet)&lt;/li&gt;
&lt;li&gt;An open channel between the nodes&lt;/li&gt;
&lt;li&gt;Sufficient balance to pay the invoice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the purposes of this tutorial, we've verified that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invoice creation works ✓&lt;/li&gt;
&lt;li&gt;Payment status checking works ✓&lt;/li&gt;
&lt;li&gt;Broadcast is gated by payment verification ✓&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Next: &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-5-cpfp-transactions-and-broadcasting-2mpb"&gt;Part 5&lt;/a&gt; continues with the CPFP transaction builder and the payment-gated broadcast endpoint.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Previous Articles in This Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j"&gt;Part 1: Lightning Network Anchor Outputs Explained: The Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en"&gt;Part 2: Understanding Lightning Network Anchor Outputs - The Technical Deep Dive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef"&gt;Part 3: Building an Anchor Output Fee Bumping Service - Setup &amp;amp; Implementation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>api</category>
      <category>backend</category>
      <category>bitcoin</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Question for those who've deployed Lightning services:

How do you manage reserve funds for fee bumping? Do you keep a fixed amount or calculate it dynamically based on channel count?

Trying to figure out the best approach for Part 4.</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Fri, 27 Feb 2026 14:58:05 +0000</pubDate>
      <link>https://dev.to/susangithaigan/question-for-those-whove-deployed-lightning-services-how-do-you-manage-reserve-funds-for-fee-4127</link>
      <guid>https://dev.to/susangithaigan/question-for-those-whove-deployed-lightning-services-how-do-you-manage-reserve-funds-for-fee-4127</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef" class="crayons-story__hidden-navigation-link"&gt;Building an Anchor Output Fee Bumping Service - Part 3: Setup &amp;amp; Implementation&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/susangithaigan" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1095882%2F2dac7a31-72e5-4268-aa94-92af21e4ac3f.jpg" alt="susangithaigan profile" class="crayons-avatar__image" width="720" height="868"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/susangithaigan" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Susan Githaiga
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Susan Githaiga
                
              
              &lt;div id="story-author-preview-content-3288383" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/susangithaigan" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1095882%2F2dac7a31-72e5-4268-aa94-92af21e4ac3f.jpg" class="crayons-avatar__image" alt="" width="720" height="868"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Susan Githaiga&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Feb 27&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef" id="article-link-3288383"&gt;
          Building an Anchor Output Fee Bumping Service - Part 3: Setup &amp;amp; Implementation
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/api"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;api&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/backend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;backend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/bitcoin"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;bitcoin&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>api</category>
      <category>backend</category>
      <category>bitcoin</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building an Anchor Output Fee Bumping Service - Part 3: Setup &amp; Implementation</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Fri, 27 Feb 2026 04:49:12 +0000</pubDate>
      <link>https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef</link>
      <guid>https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 3 of my anchor outputs learning series - getting the service running and seeing it in action&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Recap
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 1:&lt;/strong&gt; The problem - commitment transactions with outdated fees get stuck&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Part 2:&lt;/strong&gt; The solution - anchor outputs let you boost fees later using CPFP&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Part 3 (this one):&lt;/strong&gt; Actually building a service that does this&lt;/p&gt;


&lt;h3&gt;
  
  
  What We're Building
&lt;/h3&gt;

&lt;p&gt;A service that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Monitors the Bitcoin mempool for commitment transactions&lt;/li&gt;
&lt;li&gt;Detects anchor outputs (330 sat outputs)&lt;/li&gt;
&lt;li&gt;Calculates CPFP fee requirements&lt;/li&gt;
&lt;li&gt;Provides a REST API to estimate and request fee bumps&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; We're using Bitcoin regtest network for this. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs" rel="noopener noreferrer"&gt;lightning-anchor-fee-outputs&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  System Requirements
&lt;/h3&gt;

&lt;p&gt;Before you start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt; 18 or higher&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Desktop&lt;/strong&gt; (for Bitcoin Core and LND)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Operating Systems:&lt;/strong&gt; Works on macOS, Linux, and Windows (with WSL2)&lt;/p&gt;


&lt;h3&gt;
  
  
  Setup Instructions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Clone and Install&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs.git
&lt;span class="nb"&gt;cd &lt;/span&gt;lightning-anchor-fee-service
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Start Docker Services&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This starts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bitcoin Core (regtest network)&lt;/li&gt;
&lt;li&gt;LND (Lightning node)&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlymkx42anqtdafkw9ig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlymkx42anqtdafkw9ig.png" alt="Lightning Network Anchor Outputs" width="800" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Setup Bitcoin Regtest&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;i) Create Bitcoin wallet&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme createwallet &lt;span class="s2"&gt;"mywallet"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5ub5fubxt0amx1yhdfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5ub5fubxt0amx1yhdfe.png" alt="Create lightning wallet" width="800" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ii) Mine 101 blocks&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme &lt;span class="nt"&gt;-generate&lt;/span&gt; 101
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzyircs1ts4yq1q3ym2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzyircs1ts4yq1q3ym2y.png" alt="Mine 101 blocks" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you already created the LND wallet before (you'll get this error), just verify it's working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;lnd lncli &lt;span class="nt"&gt;--network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;regtest getinfo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Setup LND Wallet&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;lnd lncli &lt;span class="nt"&gt;--network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;regtest create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enter a password&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;n&lt;/code&gt; to create a new seed&lt;/li&gt;
&lt;li&gt;Write down the 24 words (optional for regtest)&lt;/li&gt;
&lt;li&gt;Press Enter for cipher passphrase&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Start the Service&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You should see:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Server running on port 3000
Connected to Bitcoin Core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Testing the Service
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Test 1: Health Check&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected:&lt;/strong&gt; &lt;code&gt;{"status":"ok"}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 2: Bitcoin Connection&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000/api/v1/bitcoin/info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You should see:&lt;/strong&gt; Chain info showing regtest network with 101 blocks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 3: LND Connection&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000/api/v1/lightning/info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You should see:&lt;/strong&gt; Node info with anchor outputs feature enabled (check for &lt;code&gt;"anchors-zero-fee-htlc-tx"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zye5tth1yx2pc5vp80r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3zye5tth1yx2pc5vp80r.png" alt="Node info with anchor outputs" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 4: Start Monitoring&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/monitor/start &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"intervalMs": 3000}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected:&lt;/strong&gt; &lt;code&gt;{"success":true,"message":"Monitoring started","intervalMs":3000}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgy4qadjo7npatxgbivp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgy4qadjo7npatxgbivp.png" alt="Monitoring" width="800" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 5: Create Test Transaction&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get a new address&lt;/span&gt;
&lt;span class="nv"&gt;ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme getnewaddress&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Send 330 sats (creates anchor-like output)&lt;/span&gt;
&lt;span class="nv"&gt;TXID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;bitcoin bitcoin-cli &lt;span class="nt"&gt;-regtest&lt;/span&gt; &lt;span class="nt"&gt;-rpcuser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bitcoinrpc &lt;span class="nt"&gt;-rpcpassword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;changeme sendtoaddress &lt;span class="nv"&gt;$ADDR&lt;/span&gt; 0.00000330&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Transaction ID: &lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv81cgixn6uahbacsykra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv81cgixn6uahbacsykra.png" alt="Create Test Transaction" width="800" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 6: Check Monitor Status&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Wait 5 seconds, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000/api/v1/monitor/status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You should see:&lt;/strong&gt; Your transaction being tracked with detected anchor output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"isMonitoring"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"trackedCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"transactions"&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="nl"&gt;"txid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"b3f593509ba7efa9eabeedd159bcc158980917f4804f6a1be8163708c5542c5b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"hex"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"02000000000101c1de4dab5c406faad339b4e7ed3998a2c9286ee8d7e08eecc4cdd56cb1f98d980000000000fdffffff0243f8029500000000160014ba6345572da5d28ea9830dfc0476826c4bb10c8b4a010000000000001600142935034b935612bc85089f89fca385ba9a78791e0247304402205abef31db372537d0f3ab2f4b5240dbdc6dd7367fe64384e1e58669f4a1a686e022049e7e8d418d18b8328070dad0251b6aefc6b0023b6ed914f34797a40111a9df5012103bb47542f3fe2dd313e221de0620dd733f0766e00d52eb90e222c06aa090aec9e30010000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"fee"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;141&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"feeRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"anchorOutputs"&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="nl"&gt;"vout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;330&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"scriptPubKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"00142935034b935612bc85089f89fca385ba9a78791e"&lt;/span&gt;&lt;span class="p"&gt;}]}]}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test 7: Estimate Fee Bump&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/feebump/estimate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;txid&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;targetFeeRate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 10}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You should see:&lt;/strong&gt; Fee calculations showing that 10 sats/vbyte is NOT feasible with just the 330 sat anchor alone. This proves the limitation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; "feasible": false&lt;/p&gt;

&lt;p&gt;This means you'd need to add ~2,180 sats from your own wallet to bump to 10 sats/vbyte.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example result&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;susan@susan-githaiga:~/development/projects/lightning-anchor-fee-outputs&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/feebump/estimate &lt;span class="se"&gt;\u&lt;/span&gt;ts&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/feebump/estimate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;txid&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;targetFeeRate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 10}"&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;:true,&lt;span class="s2"&gt;"data"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"txid"&lt;/span&gt;:&lt;span class="s2"&gt;"b3f593509ba7efa9eabeedd159bcc158980917f4804f6a1be8163708c5542c5b"&lt;/span&gt;,&lt;span class="s2"&gt;"parentSize"&lt;/span&gt;:141,&lt;span class="s2"&gt;"parentFee"&lt;/span&gt;:0,&lt;span class="s2"&gt;"parentFeeRate"&lt;/span&gt;:0,&lt;span class="s2"&gt;"childSize"&lt;/span&gt;:110,&lt;span class="s2"&gt;"totalSize"&lt;/span&gt;:251,&lt;span class="s2"&gt;"targetFeeRate"&lt;/span&gt;:10,&lt;span class="s2"&gt;"totalFeeNeeded"&lt;/span&gt;:2510,&lt;span class="s2"&gt;"childFeeNeeded"&lt;/span&gt;:2510,&lt;span class="s2"&gt;"anchorValue"&lt;/span&gt;:330,&lt;span class="s2"&gt;"feasible"&lt;/span&gt;:false&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test with a lower fee rate (should be feasible):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/feebump/estimate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;txid&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;targetFeeRate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 1}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;susan@susan-githaiga:~/development/projects/lightning-anchor-fee-outputs&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="c"&gt;# Test with 1 sat/vbyte (should be feasible)ts$ # Test with 1 sat/vbyte (should be feasible)&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/feebump/estimate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;txid&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$TXID&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;targetFeeRate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 1}"&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;:true,&lt;span class="s2"&gt;"data"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"txid"&lt;/span&gt;:&lt;span class="s2"&gt;"b3f593509ba7efa9eabeedd159bcc158980917f4804f6a1be8163708c5542c5b"&lt;/span&gt;,&lt;span class="s2"&gt;"parentSize"&lt;/span&gt;:141,&lt;span class="s2"&gt;"parentFee"&lt;/span&gt;:0,&lt;span class="s2"&gt;"parentFeeRate"&lt;/span&gt;:0,&lt;span class="s2"&gt;"childSize"&lt;/span&gt;:110,&lt;span class="s2"&gt;"totalSize"&lt;/span&gt;:251,&lt;span class="s2"&gt;"targetFeeRate"&lt;/span&gt;:1,&lt;span class="s2"&gt;"totalFeeNeeded"&lt;/span&gt;:251,&lt;span class="s2"&gt;"childFeeNeeded"&lt;/span&gt;:251,&lt;span class="s2"&gt;"anchorValue"&lt;/span&gt;:330,&lt;span class="s2"&gt;"feasible"&lt;/span&gt;:true&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This should show&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;"feasible": true&lt;/code&gt;&lt;/strong&gt; because 251 vbytes × 1 sat/vbyte = only 251 sats needed&lt;/p&gt;




&lt;h3&gt;
  
  
  Understanding the Results
&lt;/h3&gt;

&lt;p&gt;When you check the monitor status, look for:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;&lt;code&gt;trackedCount: 1&lt;/code&gt;&lt;/strong&gt; - Service detected your transaction&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;&lt;code&gt;value: 330&lt;/code&gt;&lt;/strong&gt; - Confirmed it's an anchor output&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;&lt;code&gt;anchorOutputs&lt;/code&gt;&lt;/strong&gt; array - Shows which outputs are anchors&lt;/p&gt;

&lt;p&gt;When you estimate fees:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;&lt;code&gt;feasible: true&lt;/code&gt;&lt;/strong&gt; - Can bump with just the anchor&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;&lt;code&gt;feasible: false&lt;/code&gt;&lt;/strong&gt; - Need additional inputs from wallet&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; 330 sats can only bump to about 10-20 sats/vbyte maximum.&lt;/p&gt;


&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Port conflicts?&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop redis-server postgresql
docker compose restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Services not starting?&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs bitcoin
docker compose logs lnd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Monitor not detecting transactions?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make sure the transaction is in mempool (not confirmed yet). Don't mine blocks immediately after sending!&lt;/p&gt;




&lt;h3&gt;
  
  
  What We Built
&lt;/h3&gt;

&lt;p&gt;Here's what the service does:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Bitcoin Integration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connects to Bitcoin Core via RPC&lt;/li&gt;
&lt;li&gt;Monitors mempool every 3-10 seconds&lt;/li&gt;
&lt;li&gt;Fetches transaction details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Anchor Detection&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scans all transaction outputs&lt;/li&gt;
&lt;li&gt;Identifies 330 satoshi outputs&lt;/li&gt;
&lt;li&gt;Tracks them as potential anchors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Fee Estimation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calculates parent transaction size&lt;/li&gt;
&lt;li&gt;Estimates child transaction size (~110 vbytes)&lt;/li&gt;
&lt;li&gt;Computes package fee rate&lt;/li&gt;
&lt;li&gt;Determines if anchor alone is sufficient&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. REST API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start/stop monitoring&lt;/li&gt;
&lt;li&gt;Check status and tracked transactions&lt;/li&gt;
&lt;li&gt;Estimate fee bump requirements&lt;/li&gt;
&lt;li&gt;Bitcoin and Lightning info endpoints&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Key Learnings
&lt;/h3&gt;

&lt;h3&gt;
  
  
  1. The 330 Sat Limitation is Real
&lt;/h3&gt;

&lt;p&gt;Anchor outputs can only bump fees to ~10-20 sats/vbyte. For higher rates, you need additional UTXOs from your wallet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Target: 50 sats/vbyte&lt;/li&gt;
&lt;li&gt;Parent + Child: 330 vbytes&lt;/li&gt;
&lt;li&gt;Total fee needed: 16,500 sats&lt;/li&gt;
&lt;li&gt;Anchor provides: 330 sats&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Need to add: 16,170 sats from wallet&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Regtest Makes Testing Easy
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Free Bitcoin (just mine blocks)&lt;/li&gt;
&lt;li&gt;Instant blocks (no waiting)&lt;/li&gt;
&lt;li&gt;No risk of losing real money&lt;/li&gt;
&lt;li&gt;Perfect for learning and development&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Monitoring is the Easy Part
&lt;/h3&gt;

&lt;p&gt;Detecting 330 sat outputs in the mempool is straightforward. The hard part comes next: signing and broadcasting CPFP transactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Production Needs Reserve Funds
&lt;/h3&gt;

&lt;p&gt;Real implementations maintain a pool of UTXOs specifically for fee bumping. You can't rely on the anchor alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Docker Simplifies Setup
&lt;/h3&gt;

&lt;p&gt;Bitcoin Core and LND in Docker means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No system pollution&lt;/li&gt;
&lt;li&gt;Easy cleanup&lt;/li&gt;
&lt;li&gt;Consistent environment&lt;/li&gt;
&lt;li&gt;Works everywhere&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  What's Next
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Current Status:&lt;/strong&gt; ✅ Monitoring and estimation work&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Still Missing:&lt;/strong&gt; ❌ Actual transaction signing and broadcasting&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 4 will cover:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wallet integration for additional inputs&lt;/li&gt;
&lt;li&gt;Building and signing CPFP transactions&lt;/li&gt;
&lt;li&gt;Broadcasting to Bitcoin mainnet network&lt;/li&gt;
&lt;li&gt;Testing with real Lightning channels&lt;/li&gt;
&lt;li&gt;Accepting Lightning payments for the service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Future improvements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic fee bumping (no manual trigger)&lt;/li&gt;
&lt;li&gt;Multiple channel support&lt;/li&gt;
&lt;li&gt;Reserve fund management&lt;/li&gt;
&lt;li&gt;Web dashboard&lt;/li&gt;
&lt;li&gt;Mainnet deployment&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt;&lt;a href="https://github.com/SusanGithaigaN/lightning-anchor-fee-outputs" rel="noopener noreferrer"&gt;Repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 1:&lt;/strong&gt; &lt;a href="https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j"&gt;The Basics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en"&gt;Technical Deep Dive&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.bitcoin.org/examples/testing.html" rel="noopener noreferrer"&gt;Bitcoin Regtest Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.lightning.engineering/" rel="noopener noreferrer"&gt;LND Regtest Setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Next: &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-4-lightning-payment-integration-5615"&gt;Part 4&lt;/a&gt; adds Lightning payment integration, including invoice generation and payment verification&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you have any questions or suggestions, drop them in the comments&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>bitcoin</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The 330 sat number seemed random to me at first, but the dust limit explanation made it click. What other Lightning Network concepts have you found confusing at first but made sense once you dug deeper?</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Mon, 23 Feb 2026 04:17:16 +0000</pubDate>
      <link>https://dev.to/susangithaigan/the-330-sat-number-seemed-random-to-me-at-first-but-the-dust-limit-explanation-made-it-click-what-17ac</link>
      <guid>https://dev.to/susangithaigan/the-330-sat-number-seemed-random-to-me-at-first-but-the-dust-limit-explanation-made-it-click-what-17ac</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en" class="crayons-story__hidden-navigation-link"&gt;Understanding Lightning Network Anchor Outputs - Part 2: The Technical Deep Dive&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/susangithaigan" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1095882%2F2dac7a31-72e5-4268-aa94-92af21e4ac3f.jpg" alt="susangithaigan profile" class="crayons-avatar__image" width="720" height="868"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/susangithaigan" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Susan Githaiga
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Susan Githaiga
                
              
              &lt;div id="story-author-preview-content-3269645" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/susangithaigan" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1095882%2F2dac7a31-72e5-4268-aa94-92af21e4ac3f.jpg" class="crayons-avatar__image" alt="" width="720" height="868"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Susan Githaiga&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Feb 20&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en" id="article-link-3269645"&gt;
          Understanding Lightning Network Anchor Outputs - Part 2: The Technical Deep Dive
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/bitcoin"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;bitcoin&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/lightning"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;lightning&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/web3"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;web3&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            9 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>bitcoin</category>
      <category>lightning</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Understanding Lightning Network Anchor Outputs - Part 2: The Technical Deep Dive</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Fri, 20 Feb 2026 04:43:53 +0000</pubDate>
      <link>https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en</link>
      <guid>https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en</guid>
      <description>&lt;p&gt;&lt;em&gt;Welcome back, and thank you for being here to learn with me.This is Part 2 of my ongoing series where I dive deeper into Lightning Network anchor outputs and share what I'm figuring out as I go&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Welcome Back
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j"&gt;Part 1&lt;/a&gt;, we covered the basic problem: pre-signed Lightning commitment transactions can have outdated fees by the time you need to broadcast them. Anchor outputs solve this by letting you boost fees later using CPFP (Child Pays For Parent).&lt;/p&gt;

&lt;p&gt;Today, we're going deeper. We're answering the "why" and "how" questions that make anchor outputs actually work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we're covering in Part 2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How CPFP really works (the mechanics)&lt;/li&gt;
&lt;li&gt;Why 330 sats? (The magic number explained)&lt;/li&gt;
&lt;li&gt;The one-block delay (CSV) and why it's crucial&lt;/li&gt;
&lt;li&gt;What happens to your other outputs&lt;/li&gt;
&lt;li&gt;Real-world examples with actual numbers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;




&lt;h2&gt;
  
  
  CPFP Mechanics: How Does This Actually Work?
&lt;/h2&gt;

&lt;p&gt;In Part 1, we learned that CPFP means "Child Pays For Parent." But let's understand the mechanics of HOW this works at the Bitcoin protocol level.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Bitcoin Miners Think
&lt;/h3&gt;

&lt;p&gt;Bitcoin miners want to maximize their profits. They look at transactions in the mempool and try to fill each block with the transactions that pay the highest fees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's the key insight:&lt;/strong&gt; Miners don't just look at individual transactions. They look at &lt;strong&gt;transaction packages&lt;/strong&gt; (parent + child combinations) and calculate the total fee rate for the whole package.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Math Behind It
&lt;/h3&gt;

&lt;p&gt;Let's use real numbers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parent Transaction (Your Commitment TX):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Size: 200 vbytes&lt;/li&gt;
&lt;li&gt;Fee: 200 sats (1 sat/vbyte)&lt;/li&gt;
&lt;li&gt;Fee rate: 1 sat/vbyte ❌ Too low!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This transaction alone won't get confirmed during high-fee periods. Miners will ignore it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now you create a Child Transaction:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Size: 150 vbytes&lt;/li&gt;
&lt;li&gt;Spends: Your 330-sat anchor output&lt;/li&gt;
&lt;li&gt;Fee you add: 120,000 sats&lt;/li&gt;
&lt;li&gt;Total fee: 120,330 sats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Combined Package:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total size: 200 + 150 = 350 vbytes&lt;/li&gt;
&lt;li&gt;Total fees: 200 + 120,330 = 120,530 sats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package fee rate: 120,530 ÷ 350 = 344 sats/vbyte&lt;/strong&gt; ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now miners are interested! A 344 sat/vbyte fee rate is competitive even during high congestion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The critical rule:&lt;/strong&gt; The child transaction &lt;strong&gt;cannot be confirmed&lt;/strong&gt; until the parent is confirmed (because the child spends an output from the parent). So miners MUST include both or neither.&lt;/p&gt;

&lt;p&gt;This creates the economic incentive: to get the child's juicy fee, miners must also confirm the parent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why 330 Satoshis? The Magic Number Explained
&lt;/h2&gt;

&lt;p&gt;You might be wondering: "Why exactly 330 sats? Why not 100 sats? Or 500 sats?"&lt;/p&gt;

&lt;p&gt;Great question! There are actually three reasons for this specific number:&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason 1: Bitcoin's Dust Limit
&lt;/h3&gt;

&lt;p&gt;Bitcoin has something called a "dust limit" to prevent spam. Outputs that are too small are considered "dust" and many nodes won't relay transactions that create them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The dust limit for standard outputs is around 546 satoshis.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But anchor outputs use a special script type that has a &lt;strong&gt;lower dust limit of 330 satoshis&lt;/strong&gt;. This makes them the smallest spendable outputs possible without being classified as dust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; You want anchor outputs to be as small as possible so they don't waste channel capacity, but big enough to be spendable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason 2: Standardization Across Lightning
&lt;/h3&gt;

&lt;p&gt;All Lightning implementations (LND, Core Lightning, Eclair, LDK) agreed on 330 sats as the standard in the BOLT (Basis of Lightning Technology) specifications.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Everyone uses the same amount&lt;/li&gt;
&lt;li&gt;Channels work consistently across different implementations&lt;/li&gt;
&lt;li&gt;No confusion or compatibility issues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reason 3: Enough Value to Spend
&lt;/h3&gt;

&lt;p&gt;Even though 330 sats is small (currently worth less than $0.50 USD), it's enough to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be recognized as a valid output by the Bitcoin network&lt;/li&gt;
&lt;li&gt;Cover the cost of creating a small child transaction&lt;/li&gt;
&lt;li&gt;Not be dismissed as too trivial to process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The bottom line:&lt;/strong&gt; 330 sats is the Goldilocks number—not too big (wastes channel funds), not too small (can't be spent), just right.&lt;/p&gt;




&lt;h2&gt;
  
  
  The One-Block Delay (CSV): Why Can't I Spend My Other Outputs Immediately?
&lt;/h2&gt;

&lt;p&gt;Here's something interesting: When you broadcast a commitment transaction, you can spend your anchor output immediately, but you &lt;strong&gt;cannot&lt;/strong&gt; spend your main balance output for at least one block (about 10 minutes).&lt;/p&gt;

&lt;p&gt;This is called a &lt;strong&gt;CSV lock&lt;/strong&gt; (CheckSequenceVerify).&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is CSV?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;CSV&lt;/strong&gt; stands for &lt;strong&gt;CheckSequenceVerify&lt;/strong&gt;. It's a Bitcoin script command that says: "This output cannot be spent until X blocks have been mined after this transaction is confirmed."&lt;/p&gt;

&lt;p&gt;In anchor output commitment transactions, all outputs except the anchors have a &lt;strong&gt;1 block CSV lock&lt;/strong&gt; (written as &lt;code&gt;1 CSV&lt;/code&gt; in the script).&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Does This Delay Exist?
&lt;/h3&gt;

&lt;p&gt;This one-block delay prevents something called &lt;strong&gt;transaction pinning&lt;/strong&gt;, which is a type of attack where your channel partner could try to interfere with your fee bumping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's how the attack would work WITHOUT the CSV lock:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You broadcast your commitment transaction with low fees&lt;/li&gt;
&lt;li&gt;Your evil partner immediately spends their balance output with a LOW fee&lt;/li&gt;
&lt;li&gt;This creates a competing transaction in the mempool&lt;/li&gt;
&lt;li&gt;When you try to CPFP using your anchor, Bitcoin's rules prevent you because there's already a conflicting spend&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;With the CSV lock:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You broadcast your commitment transaction&lt;/li&gt;
&lt;li&gt;Your partner's balance output is &lt;strong&gt;locked for one block&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You can spend your anchor output immediately to boost fees&lt;/li&gt;
&lt;li&gt;Your commitment transaction confirms in the next block&lt;/li&gt;
&lt;li&gt;Only AFTER confirmation can your partner spend their output (but it's too late to interfere)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The CSV lock gives you a clean window to boost fees without interference.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happens to Your Other Outputs?
&lt;/h2&gt;

&lt;p&gt;Let's map out ALL the outputs in a typical anchor output commitment transaction:&lt;/p&gt;

&lt;h3&gt;
  
  
  Output 1: Your Balance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amount:&lt;/strong&gt; Your portion of the channel (e.g., 1,000,000 sats)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock:&lt;/strong&gt; 1 block CSV delay&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why:&lt;/strong&gt; This is your actual money from the channel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Output 2: Partner's Balance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amount:&lt;/strong&gt; Their portion of the channel (e.g., 500,000 sats)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock:&lt;/strong&gt; 1 block CSV delay&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why:&lt;/strong&gt; This is their money from the channel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Output 3: Your Anchor
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amount:&lt;/strong&gt; 330 sats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock:&lt;/strong&gt; None! Can spend immediately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why:&lt;/strong&gt; So YOU can fee-bump right away&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Output 4: Partner's Anchor
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amount:&lt;/strong&gt; 330 sats
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock:&lt;/strong&gt; None! Can spend immediately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why:&lt;/strong&gt; So THEY can fee-bump right away&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optional Output 5: HTLCs (If Any)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amount:&lt;/strong&gt; Varies (pending payments in the channel)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock:&lt;/strong&gt; Time-locked based on payment conditions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why:&lt;/strong&gt; These are in-flight payments that need to be resolved&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Only the anchor outputs can be spent immediately. Everything else waits one block. This is by design for security.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Example: The Numbers in Action
&lt;/h2&gt;

&lt;p&gt;Let's walk through a complete real-world scenario with actual numbers to see how this all works together.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;You have a Lightning channel:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your balance: 5,000,000 sats (0.05 BTC)&lt;/li&gt;
&lt;li&gt;Partner balance: 3,000,000 sats (0.03 BTC)&lt;/li&gt;
&lt;li&gt;Your anchor: 330 sats&lt;/li&gt;
&lt;li&gt;Partner anchor: 330 sats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Total channel capacity:&lt;/strong&gt; 8,000,660 sats&lt;/p&gt;

&lt;h3&gt;
  
  
  The Crisis
&lt;/h3&gt;

&lt;p&gt;Your partner goes offline. You need to force-close the channel to get your funds back.&lt;/p&gt;

&lt;p&gt;You check Bitcoin mempool fees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Current rate:&lt;/strong&gt; 250 sats/vbyte (high congestion!)&lt;/li&gt;
&lt;li&gt;Your commitment transaction size: 220 vbytes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Needed fee:&lt;/strong&gt; 220 × 250 = 55,000 sats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Your pre-signed commitment only has a 440 sat fee (2 sats/vbyte). This won't confirm for days or weeks.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: CPFP
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Broadcast commitment transaction&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fee paid: 440 sats (pathetically low)&lt;/li&gt;
&lt;li&gt;Status: Sitting in mempool, unconfirmed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create child transaction&lt;/strong&gt;&lt;br&gt;
You create a new transaction that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spends: Your 330-sat anchor output&lt;/li&gt;
&lt;li&gt;Adds inputs: 60,000 sats from your regular Bitcoin wallet&lt;/li&gt;
&lt;li&gt;Creates output: Sends to your wallet&lt;/li&gt;
&lt;li&gt;Transaction size: 180 vbytes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fee:&lt;/strong&gt; 54,560 sats + 330 sats (anchor) = 54,890 sats total&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Calculate package fee rate&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parent size: 220 vbytes&lt;/li&gt;
&lt;li&gt;Child size: 180 vbytes
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total size:&lt;/strong&gt; 400 vbytes&lt;/li&gt;
&lt;li&gt;Parent fee: 440 sats&lt;/li&gt;
&lt;li&gt;Child fee: 54,890 sats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total fee:&lt;/strong&gt; 55,330 sats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package rate:&lt;/strong&gt; 55,330 ÷ 400 = &lt;strong&gt;138 sats/vbyte&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wait, that's still lower than the 250 sats/vbyte market rate! Let me recalculate...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 (corrected): Create child transaction with proper fees&lt;/strong&gt;&lt;br&gt;
You need the package to average 250 sats/vbyte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total needed: 400 vbytes × 250 = 100,000 sats&lt;/li&gt;
&lt;li&gt;Parent already paid: 440 sats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Child must pay:&lt;/strong&gt; 100,000 - 440 = 99,560 sats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you create the child transaction with a 99,560 sat fee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 (corrected): Final package&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total size: 400 vbytes&lt;/li&gt;
&lt;li&gt;Total fees: 440 + 99,560 = 100,000 sats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package rate: 250 sats/vbyte&lt;/strong&gt; ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Confirmation&lt;/strong&gt;&lt;br&gt;
Miners see your attractive package and include it in the next block. Both transactions confirm together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cost:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You paid 100,000 sats total (~$130 USD at $65k BTC)&lt;/li&gt;
&lt;li&gt;But you secured your 5,000,000 sats (0.05 BTC = ~$3,250 USD)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Worth it!&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Economics: Is This Expensive?
&lt;/h2&gt;

&lt;p&gt;Let's talk about the elephant in the room: &lt;strong&gt;This can be expensive!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When Fees Are Normal (1-10 sats/vbyte)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your commitment TX: 220 vbytes × 5 = 1,100 sats (~$1.43)&lt;/li&gt;
&lt;li&gt;CPFP child: 180 vbytes × 5 = 900 sats (~$1.17)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total cost: ~$2.60&lt;/strong&gt; ✅ Very affordable!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When Fees Are High (200+ sats/vbyte)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your commitment TX: 220 vbytes × 200 = 44,000 sats (~$57)&lt;/li&gt;
&lt;li&gt;CPFP child: 180 vbytes × 200 = 36,000 sats (~$47)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total cost: ~$104&lt;/strong&gt; ⚠️ Ouch!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When Fees Are INSANE (500+ sats/vbyte)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your commitment TX: 220 vbytes × 500 = 110,000 sats (~$143)&lt;/li&gt;
&lt;li&gt;CPFP child: 180 vbytes × 500 = 90,000 sats (~$117)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total cost: ~$260&lt;/strong&gt; 💀 Very painful!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The reality:&lt;/strong&gt; Force-closing channels during high-fee periods is expensive. This is why:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Lightning encourages cooperative closes (much cheaper)&lt;/li&gt;
&lt;li&gt;You should maintain channels with reliable partners&lt;/li&gt;
&lt;li&gt;Reserve funds for fee-bumping are essential&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;But here's the key:&lt;/strong&gt; Without anchor outputs, you might not be able to close at all during high-fee periods. You'd be completely stuck. Anchor outputs give you the OPTION to pay more to get your funds back—which is better than having no option.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q: Can I use the same anchor output multiple times?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; No! Once you spend an anchor output in a child transaction, that's it. You can't spend it again. If your child transaction doesn't have enough fee and still doesn't confirm, you'd need to create ANOTHER child transaction that spends the FIRST child (a "grandchild") to bump fees further. This is called CPFP chaining.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: What if my partner also tries to fee-bump at the same time?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; That's fine! You each have your own separate anchor output. You can both create child transactions independently. Whichever child transaction (yours or theirs) gets confirmed first will pull the parent through. This is actually good—it means either party can ensure confirmation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: What if I don't have enough Bitcoin to fee-bump?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Then you're stuck. This is why maintaining a reserve fund is crucial. If you operate Lightning channels, you MUST keep separate on-chain Bitcoin available for fee-bumping emergencies. Think of it as insurance—you pay a little to protect a lot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: Can I spend my balance output and my anchor in the same transaction?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; No! Your balance output has a 1-block CSV lock. You must wait one block after the commitment transaction confirms before you can spend your balance. The anchor can be spent immediately, but the balance cannot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q: Why can't commitment transactions just use RBF (Replace-By-Fee) instead?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Great question! RBF (Replace-By-Fee) allows you to replace an unconfirmed transaction with a new version that pays higher fees. However, RBF doesn't work well for Lightning because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Both parties would need to cooperate to create the replacement (but in a force-close, your partner is unresponsive)&lt;/li&gt;
&lt;li&gt;RBF is vulnerable to transaction pinning attacks&lt;/li&gt;
&lt;li&gt;CPFP with anchors is more secure because each party can act independently&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What We've Learned in Part 2
&lt;/h2&gt;

&lt;p&gt;Let's recap the key technical details we covered:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;CPFP mechanics:&lt;/strong&gt; Miners calculate package fee rates (parent + child together), creating the economic incentive to confirm both&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;330 sats explained:&lt;/strong&gt; The minimum spendable amount that's not classified as dust, standardized across all Lightning implementations&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;CSV lock:&lt;/strong&gt; The one-block delay on balance outputs prevents transaction pinning attacks and gives you a clean window to fee-bump&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Output structure:&lt;/strong&gt; Anchors can be spent immediately, but all other outputs must wait one block after confirmation&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Real costs:&lt;/strong&gt; Can range from $3 in normal times to $200+ during extreme congestion—but it beats being stuck with no option&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Economics:&lt;/strong&gt; Force-closing is expensive, which incentivizes cooperative closes and choosing reliable channel partners&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Coming in Part 3
&lt;/h2&gt;

&lt;p&gt;Now that you understand the technical details, Part 3 will cover the practical side:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to check if your Lightning node supports anchor outputs&lt;/li&gt;
&lt;li&gt;Setting up anchor outputs in different implementations (LND, Core Lightning, Eclair)&lt;/li&gt;
&lt;li&gt;How much Bitcoin to keep in reserve for fee-bumping&lt;/li&gt;
&lt;li&gt;Tools and commands for monitoring and managing anchor outputs&lt;/li&gt;
&lt;li&gt;What to do when a force-close happens&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Building Understanding Together
&lt;/h2&gt;

&lt;p&gt;We've gone from "what are anchor outputs?" (Part 1) to "how do they actually work?" (Part 2). The complexity is increasing, but we're building on the foundation step by step.&lt;/p&gt;

&lt;p&gt;If anything in Part 2 was confusing, that's valuable feedback for me. It helps me understand what needs more explanation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next up:&lt;/strong&gt; &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef"&gt;Part 3&lt;/a&gt; will be hands-on and practical. We'll move from theory to "okay, how do I actually USE this stuff?"&lt;/p&gt;

&lt;p&gt;Thanks for sticking with me through the technical details. &lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef"&gt;See you in Part 3&lt;/a&gt;. &lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Summary (For the Skimmers)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPFP works&lt;/strong&gt; because miners calculate package fee rates (parent + child together)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;330 sats&lt;/strong&gt; is the smallest spendable amount that's not dust, standardized in BOLT specs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSV lock&lt;/strong&gt; (1 block delay) on balance outputs prevents transaction pinning attacks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only anchors&lt;/strong&gt; can be spent immediately; all other outputs wait 1 block&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real costs&lt;/strong&gt; vary from $3 to $200+ depending on network congestion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/susangithaigan/building-an-anchor-output-fee-bumping-service-part-3-setup-implementation-pef"&gt;Part 3&lt;/a&gt;:&lt;/strong&gt; Practical setup, implementation guides, and reserve fund management&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you have any questions or feedback? Drop them in the comments&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>lightning</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Do you have stuck Bitcoin transactions? Learn how Lightning Network Anchor Outputs solve fee volatility using CPFP in Part 1 of our technical deep dive.</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Wed, 18 Feb 2026 03:24:16 +0000</pubDate>
      <link>https://dev.to/susangithaigan/do-you-have-stuck-bitcoin-transactions-learn-how-lightning-network-anchor-outputs-solve-fee-1k95</link>
      <guid>https://dev.to/susangithaigan/do-you-have-stuck-bitcoin-transactions-learn-how-lightning-network-anchor-outputs-solve-fee-1k95</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j" class="crayons-story__hidden-navigation-link"&gt;Lightning Network Anchor Outputs Explained: The Basics (Part 1)&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/susangithaigan" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1095882%2F2dac7a31-72e5-4268-aa94-92af21e4ac3f.jpg" alt="susangithaigan profile" class="crayons-avatar__image" width="720" height="868"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/susangithaigan" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Susan Githaiga
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Susan Githaiga
                
              
              &lt;div id="story-author-preview-content-3263977" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/susangithaigan" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1095882%2F2dac7a31-72e5-4268-aa94-92af21e4ac3f.jpg" class="crayons-avatar__image" alt="" width="720" height="868"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Susan Githaiga&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Feb 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j" id="article-link-3263977"&gt;
          Lightning Network Anchor Outputs Explained: The Basics (Part 1)
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/bitcoin"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;bitcoin&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/lightning"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;lightning&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/web3"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;web3&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>bitcoin</category>
      <category>lightning</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Lightning Network Anchor Outputs Explained: The Basics (Part 1)</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Wed, 18 Feb 2026 03:02:36 +0000</pubDate>
      <link>https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j</link>
      <guid>https://dev.to/susangithaigan/understanding-lightning-network-anchor-outputs-part-1-the-basics-2p7j</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 1 of an ongoing series where I'm learning about Lightning Network anchor outputs and sharing what I discover along the way.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A Note Before We Start
&lt;/h3&gt;

&lt;p&gt;Hi! I'm learning about Lightning Network anchor outputs and documenting my journey. This is going to be a continuous series where I break down complex topics into digestible pieces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why a series?&lt;/strong&gt; Because this stuff is complicated, and trying to explain everything at once would be overwhelming, both for me to write and for you to read. So, we're taking it step by step, together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to expect:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1 (this article): The basic problem and what anchor outputs are&lt;/li&gt;
&lt;li&gt;Part 2 (coming next): How they actually work in detail&lt;/li&gt;
&lt;li&gt;Part 3+: Implementation, setup, management, and real-world usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's build this understanding together, one piece at a time.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem We're Solving
&lt;/h2&gt;

&lt;p&gt;We use the Lightning Network for quick and low-cost Bitcoin payments. But there's a challenge: if a channel closes unexpectedly, changing network fees can cause transactions to get stuck and delay your funds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Here's the scenario:
&lt;/h3&gt;

&lt;p&gt;You open a Lightning channel today when Bitcoin network fees are low (let's say 5 sats/vbyte). You and your channel partner sign some special transactions called "commitment transactions" that say "if we need to close this channel, here's who gets what."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem?&lt;/strong&gt; These transactions are signed TODAY, but you might not need to use them until WEEKS or MONTHS later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens then?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Week 1:&lt;/strong&gt; Channel opens, fees are 5 sats/vbyte&lt;br&gt;
&lt;strong&gt;Week 4:&lt;/strong&gt; You need to close the channel, but now fees are 500 sats/vbyte&lt;br&gt;
**Result: **Your transaction with the old 5 sats/vbyte fee sits in the waiting room (mempool) forever because miners prioritize higher-paying transactions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're stuck.&lt;/strong&gt; Your funds are in limbo because you guessed wrong about future fees.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpk178lb2uxxa11fhg3ec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpk178lb2uxxa11fhg3ec.png" alt="Lightning Network Anchor Outputs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the exact problem anchor outputs solve.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Are Lightning Network Anchor Outputs?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Picture this:&lt;/strong&gt; You're vibing with your Lightning channel, everything's smooth, then BAM—network fees spike and your transaction gets stuck. You're basically Mort from Madagascar watching the fossa approach—complete panic mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ygwf5wfe4dax0rtyk7f.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ygwf5wfe4dax0rtyk7f.jpeg" alt="Lightning Network Anchor Outputs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anchor outputs&lt;/strong&gt; are the solution. Think of them like the assistance service you use at a mall when you forget where you parked your car:&lt;/p&gt;
&lt;h3&gt;
  
  
  The Mall Parking Analogy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;WITHOUT Anchor Outputs:&lt;/strong&gt;&lt;br&gt;
You pay for parking when you enter (5 shillings, based on last month's rate). Three weeks later when you're ready to leave, parking now costs 50 shillings. The attendant won't let you out because you paid the old rate. You're stuck in the parking lot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WITH Anchor Outputs:&lt;/strong&gt;&lt;br&gt;
You get a basic ticket when you enter (minimal fee). When you're ready to leave and see parking now costs 50 shillings, you can pay the CURRENT rate right then and there. The attendant is happy, you leave smoothly, everyone wins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's what anchor outputs do for Lightning channels.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of guessing what fees will be weeks from now, you pay a tiny amount upfront and can boost the fee to whatever is needed when you actually close the channel.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Exactly Are Anchor Outputs?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Anchor outputs&lt;/strong&gt; are tiny Bitcoin outputs (around 330 satoshis) that get attached to your commitment transaction. They exist for ONE purpose: to let you boost your transaction fee later using CPFP.&lt;/p&gt;

&lt;p&gt;Think of them as handles or hooks attached to your transaction that you can grab onto later to pull it through when you need to.&lt;/p&gt;


&lt;h2&gt;
  
  
  How This Works (Step by Step)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Opening the Channel
&lt;/h3&gt;

&lt;p&gt;When you create a Lightning channel with anchor outputs enabled, your commitment transaction includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Your balance output&lt;/strong&gt; - Your portion of the channel funds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partner's balance output&lt;/strong&gt; - Their portion of the channel funds
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your anchor output&lt;/strong&gt; - 330 sats that YOU can spend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partner's anchor output&lt;/strong&gt; - 330 sats that THEY can spend&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both of you get your own anchor. This means either person can boost fees independently.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Channel Needs to Close
&lt;/h3&gt;

&lt;p&gt;Something happens—maybe your partner goes offline, maybe there's a dispute—and you need to broadcast your commitment transaction to close the channel (a "force close").&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Check Current Fees
&lt;/h3&gt;

&lt;p&gt;You look at the Bitcoin network. Fees are HIGH. Your pre-signed commitment transaction only has a minimal fee (maybe 1-2 sats/vbyte). At current rates, it won't confirm for days or weeks.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4: CPFP to the Rescue
&lt;/h3&gt;

&lt;p&gt;This is where the magic happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You create a NEW transaction (the "child")&lt;/li&gt;
&lt;li&gt;This child transaction spends your 330-sat anchor output&lt;/li&gt;
&lt;li&gt;You add a LARGE fee to this child transaction (from your regular Bitcoin wallet)&lt;/li&gt;
&lt;li&gt;Miners see: "If I confirm the parent (your commitment tx), I get to also confirm the child and collect that big fee!"&lt;/li&gt;
&lt;li&gt;Both transactions get confirmed together&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You've successfully boosted your commitment transaction's fee AFTER broadcasting it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Visual Breakdown
&lt;/h2&gt;

&lt;p&gt;Here's what it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PARENT TRANSACTION (Your Commitment Transaction)
┌─────────────────────────────────────────────────┐
│ Your balance: 1,000,000 sats                    │
│ Partner's balance: 500,000 sats                 │
│ Your anchor: 330 sats ⚓ ← YOU GRAB THIS        │
│ Partner's anchor: 330 sats ⚓                    │
│                                                  │
│ Fee: 1 sat/vbyte (very low)                    │
└─────────────────────────────────────────────────┘
                    ↓
        (You create a child transaction)
                    ↓
CHILD TRANSACTION
┌─────────────────────────────────────────────────┐
│ Spends: Your 330-sat anchor output              │
│ Adds: 50,000 sats from your wallet             │
│ Total Fee: 50,330 sats (300+ sats/vbyte)       │
│                                                  │
│ Miners: "Great fee! We'll confirm BOTH!"        │
└─────────────────────────────────────────────────┘
                    ↓
            BOTH CONFIRMED! ✅
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  That's Part 1!
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What we covered today:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The fee guessing problem with Lightning channels&lt;/li&gt;
&lt;li&gt;What anchor outputs are (tiny 330-sat outputs for fee bumping)&lt;/li&gt;
&lt;li&gt;The mall parking analogy&lt;/li&gt;
&lt;li&gt;The four-step process of how anchor outputs work&lt;/li&gt;
&lt;li&gt;A visual diagram of parent and child transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What's coming in Part 2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deep dive into CPFP mechanics&lt;/li&gt;
&lt;li&gt;Why anchor outputs are 330 sats specifically&lt;/li&gt;
&lt;li&gt;What happens to the other outputs in commitment transactions&lt;/li&gt;
&lt;li&gt;The one-block delay (CSV) and why it matters&lt;/li&gt;
&lt;li&gt;Real-world examples with actual numbers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Building This Series Together
&lt;/h2&gt;

&lt;p&gt;I'm learning this stuff as I go, breaking it down into pieces that make sense to me, and sharing it with you. If something's unclear, that helps me learn where I need to explain better in future parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is a continuous series.&lt;/strong&gt; I'm not trying to teach everything at once. We're building understanding brick by brick.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next up:&lt;/strong&gt; Part 2 will go deeper into the technical details and answer questions like "why 330 sats?" and "what prevents my channel partner from messing with my fee bump?"&lt;/p&gt;

&lt;p&gt;Thanks for learning with me. See you in Part 2! &lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Summary (If You're Skimming)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; Lightning channels use pre-signed transactions, but fees might change by the time you need to close&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Anchor outputs let you boost fees AFTER broadcasting using CPFP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How:&lt;/strong&gt; Tiny 330-sat outputs attached to commitment transactions that you can spend to create a high-fee child transaction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analogy:&lt;/strong&gt; Like paying for mall parking when you LEAVE instead of guessing the fee weeks earlier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en"&gt;Part 2&lt;/a&gt;, we’re getting technical: we’ll break down the 'magic' 330-sat calculation, the mechanics of CPFP, and why a one-block delay is actually your best friend. &lt;a href="https://dev.to/susangithaigan/lightning-network-anchor-outputs-explained-the-basics-part-1-27en"&gt;See you in the deep dive&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>lightning</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Connect MySQL Workbench to MySQL Server in WSL using Node</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Wed, 13 Nov 2024 06:14:30 +0000</pubDate>
      <link>https://dev.to/susangithaigan/connect-mysql-workbench-to-mysql-server-in-wsl-using-node-14g7</link>
      <guid>https://dev.to/susangithaigan/connect-mysql-workbench-to-mysql-server-in-wsl-using-node-14g7</guid>
      <description>&lt;p&gt;Windows Subsystem for Linux(WSL) is a Microsoft Windows feature that lets you run a Linux environment directly on your Windows machine without needing a separate virtual machine or dual booting your PC.&lt;/p&gt;

&lt;p&gt;WSL uses your window's system IP address as it's own. However, the default MySQL workbench config only accepts requests and connections from 127.0.0.1&lt;/p&gt;

&lt;p&gt;If you want to connect your NodeJS app to MySQL workbench, then you need to use the ip address of your host machine(PC). To do this, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open your WSL terminal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the following command in your WSL terminal to retrieve your host machine's(PC's) IP address:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;ip route show | grep -i default | awk '{ print $3}'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkix12tdazia9f9pi6gi9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkix12tdazia9f9pi6gi9.png" alt=" " width="800" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On your .env file, change the DB_HOST(the variable which stores your host IP Address) to your host machine's IP Address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1qljs414ss9t19u5jwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1qljs414ss9t19u5jwu.png" alt=" " width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, your NodeJS server is running and ready for use:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgx5qk8xgst0ttmckwea5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgx5qk8xgst0ttmckwea5.png" alt=" " width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To learn more about how to access your PC's network applications through WSL, read the following article on Microsoft learn: &lt;a href="https://learn.microsoft.com/en-us/windows/wsl/networking#accessing-windows-networking-apps-from-linux-host-ip" rel="noopener noreferrer"&gt;Accessing network applications with WSL&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>mysql</category>
      <category>node</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Leetcode #2623. Memoize</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Tue, 01 Oct 2024 15:33:26 +0000</pubDate>
      <link>https://dev.to/susangithaigan/leetcode-2623-memoize-532g</link>
      <guid>https://dev.to/susangithaigan/leetcode-2623-memoize-532g</guid>
      <description>&lt;p&gt;Given a function &lt;code&gt;fn&lt;/code&gt;, return a &lt;strong&gt;memoized&lt;/strong&gt; version of that function.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;memoized&lt;/strong&gt; function is a function that will never be called twice with the same inputs. Instead it will return a cached value.&lt;/p&gt;

&lt;p&gt;You can assume there are 3 possible input functions: &lt;code&gt;sum&lt;/code&gt;, &lt;code&gt;fib&lt;/code&gt;, and &lt;code&gt;factorial&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;sum&lt;/code&gt; accepts two integers &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; and returns &lt;code&gt;a + b&lt;/code&gt;. Assume that if a value has already been cached for the arguments &lt;code&gt;(b, a)&lt;/code&gt; where &lt;code&gt;a != b&lt;/code&gt;, it cannot be used for the arguments &lt;code&gt;(a, b)&lt;/code&gt;. For example, if the arguments are &lt;code&gt;(3, 2)&lt;/code&gt; and &lt;code&gt;(2, 3)&lt;/code&gt;, two separate calls should be made.&lt;br&gt;
&lt;code&gt;fib&lt;/code&gt; accepts a single integer &lt;code&gt;n&lt;/code&gt; and returns &lt;code&gt;1&lt;/code&gt; if &lt;code&gt;n &amp;lt;= 1&lt;/code&gt; or &lt;code&gt;fib(n - 1) + fib(n - 2)&lt;/code&gt; otherwise.&lt;br&gt;
factorial accepts a single integer &lt;code&gt;n&lt;/code&gt; and returns &lt;code&gt;1&lt;/code&gt; if &lt;code&gt;n &amp;lt;= 1&lt;/code&gt; or &lt;code&gt;factorial(n - 1) * n&lt;/code&gt; otherwise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fnName = "sum"
actions = ["call","call","getCallCount","call","getCallCount"]
values = [[2,2],[2,2],[],[1,2],[]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;[4,4,1,3,2]&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const sum = (a, b) =&amp;gt; a + b;
const memoizedSum = memoize(sum);
memoizedSum(2, 2); // "call" - returns 4. sum() was called as (2, 2) was not seen before.
memoizedSum(2, 2); // "call" - returns 4. However sum() was not called because the same inputs were seen before.
// "getCallCount" - total call count: 1
memoizedSum(1, 2); // "call" - returns 3. sum() was called as (1, 2) was not seen before.
// "getCallCount" - total call count: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fnName = "factorial"
actions = ["call","call","call","getCallCount","call","getCallCount"]
values = [[2],[3],[2],[],[3],[]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;[2,6,2,2,6,2]&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const factorial = (n) =&amp;gt; (n &amp;lt;= 1) ? 1 : (n * factorial(n - 1));
const memoFactorial = memoize(factorial);
memoFactorial(2); // "call" - returns 2.
memoFactorial(3); // "call" - returns 6.
memoFactorial(2); // "call" - returns 2. However factorial was not called because 2 was seen before.
// "getCallCount" - total call count: 2
memoFactorial(3); // "call" - returns 6. However factorial was not called because 3 was seen before.
// "getCallCount" - total call count: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fnName = "fib"
actions = ["call","getCallCount"]
values = [[5],[]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;[8,1]&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fib(5) = 8 // "call"
// "getCallCount" - total call count: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; 0 &amp;lt;= a, b &amp;lt;= 105
&amp;gt; 1 &amp;lt;= n &amp;lt;= 10
&amp;gt; 0 &amp;lt;= actions.length &amp;lt;= 105
&amp;gt; actions.length === values.length
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;actions[i]&lt;/code&gt; is one of "call" and "getCallCount"&lt;br&gt;
&lt;code&gt;fnName&lt;/code&gt; is one of "sum", "factorial" and "fib"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Solution&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hjv2znv344x71y4n6je.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hjv2znv344x71y4n6je.png" alt=" " width="800" height="735"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you continue to develop and optimize your JavaScript applications, remember the power of memoization. By identifying the right functions to memoize and implementing the appropriate caching strategies, you can unlock significant performance gains and create a more seamless and responsive user experience for your customers.&lt;/p&gt;

&lt;p&gt;I hope this article helps. If you like the article, please leave a like and feel free to leave any concerns on the comment section. That is all for today.&lt;/p&gt;

</description>
      <category>30daysofjavascript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>leetcode</category>
    </item>
    <item>
      <title>JavaScript memoization</title>
      <dc:creator>Susan Githaiga</dc:creator>
      <pubDate>Tue, 01 Oct 2024 15:13:06 +0000</pubDate>
      <link>https://dev.to/susangithaigan/javascript-memoization-hbg</link>
      <guid>https://dev.to/susangithaigan/javascript-memoization-hbg</guid>
      <description>&lt;p&gt;JavaScript is a powerful programming language that plays a significant role in developing interactive websites. However, when dealing with complex and data-intensive applications, JavaScript performance can become an issue. This is where memoization comes into play. By unleashing the power of caching, memoization is a technique that significantly boosts JavaScript performance, making your applications run faster and smoother.&lt;/p&gt;

&lt;p&gt;In this article, we will dive deep into the world of memoization and explore how it can optimize your JavaScript code. We will uncover the benefits of caching and how it reduces unnecessary calculations, resulting in improved execution time. Whether you are a seasoned JavaScript developer or a beginner, understanding and implementing memoization can take your code to the next level.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is memoization and how does it work?
&lt;/h2&gt;

&lt;p&gt;Memoization is a powerful optimization technique in programming that involves caching the results of expensive function calls and returning the cached result when the same inputs occur again. This process helps to avoid redundant computations and significantly improves the performance of your JavaScript applications.&lt;/p&gt;

&lt;p&gt;The core idea behind memoization is to store the results of function calls in a cache, typically an object or a Map, where the function's arguments are used as keys and the corresponding results are stored as values. When the function is called again with the same arguments, the cached result is retrieved instead of recomputing the entire operation.&lt;/p&gt;

&lt;p&gt;This caching mechanism is particularly beneficial for functions that perform complex or time-consuming calculations, as it can dramatically reduce the overall execution time of your application. Memoization is especially useful for functions that are called repeatedly with the same inputs, as it allows you to avoid redundant computations and improve the overall responsiveness of your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to implement memoization in JavaScript
&lt;/h2&gt;

&lt;p&gt;Implementing memoization in JavaScript is relatively straightforward. The basic approach involves creating a cache, typically an object or a Map, to store the results of function calls. When the function is called, the implementation checks if the result is already cached, and if so, it returns the cached value. If the result is not cached, the function is executed, and the result is stored in the cache for future use.&lt;/p&gt;

&lt;p&gt;One common way to implement memoization in JavaScript is to use a higher-order function, which takes a function as an argument and returns a new function that incorporates the memoization logic. Here's a simple example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ok33daztp1lws01slj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ok33daztp1lws01slj8.png" alt=" " width="800" height="996"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and final thoughts
&lt;/h2&gt;

&lt;p&gt;In this article, we have explored the power of memoization and how it can be used to boost the performance of your JavaScript applications. By caching the results of expensive function calls, memoization can dramatically reduce the time required to execute those functions, leading to faster and more responsive applications.&lt;/p&gt;

&lt;p&gt;As you continue to develop and optimize your JavaScript applications, remember the power of memoization. By identifying the right functions to memoize and implementing the appropriate caching strategies, you can unlock significant performance gains and create a more seamless and responsive user experience for your customers.&lt;/p&gt;

&lt;p&gt;Embrace the power of memoization, and let it be a key part of your JavaScript optimization toolkit. With the right approach, you can supercharge your applications and take them to new heights of performance and efficiency.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>productivity</category>
      <category>learning</category>
      <category>development</category>
    </item>
  </channel>
</rss>
