Withdraw On Chain from Lightning Wallet

Overview

Bitcoin held in your Lightning wallets remains on the Lightning Network (L2), unless you request to move the assets on-chain to the Bitcoin (L1) blockchain. Upon request, BitGo moves the specified balance from Lightning channels into an L1 addresses within your Lightning wallet. Once the bitcoin is in an L1 address, you can create an on-chain withdrawal using a Lightning-specific Express endpoint.

Prerequisites

1. Transfer Bitcoin from L2 to L1

Specify the amount of bitcoin you intend to withdraw on chain by contacting [email protected]. BitGo transfers the bitcoin within your wallet from an L2 address to an L1 address.

2. Build, Authenticate, and Send Transaction

Use BitGo Express to build, authenticate, and broadcast an on-chain bitcoin transaction all in one call.

Endpoint: Lightning - Onchain Withdrawal

export BITGO_EXPRESS_HOST="<YOUR_LOCALHOST>"
export COIN="tlntbc"
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export WALLET_PASSPHRASE="<YOUR_WALLET_PASSPHRASE>"
export DESTINATION_ADDRESS="<DESTINATION_ADDRESS>"
export AMOUNT_SAT="<AMOUNT_IN_SATS>"
export SATSPERVBYTE="<FEE_RATE_IN_SATS_PER_VBYTE>"
export NUM_BLOCKS="<NUMBER_OF_BLOCKS_FOR_CONFIRMATION>"

curl -X POST \
  http://$BITGO_EXPRESS_HOST/api/v2/$COIN/wallet/$WALLET_ID/lightning/withdraw \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "passphrase": "'"$WALLET_PASSPHRASE"'",     # Wallet passphrase
    "recipients": [                             # Array of recipients for the withdrawal
      {
        "amount": "'"$AMOUNT_SAT"'",            # Amount to send in satoshis
        "address": "'"$DESTINATION_ADDRESS"'"   # Destination Bitcoin address
      }
    ],
    "satsPerVbyte": "'"$SATSPERVBYTE"'"         # Fee rate in satoshis per virtual byte
    "numBlocks": "'"$NUM_BLOCKS"'"              # Target number of blocks for confirmation, used for fee estimation
    "sequenceId": string,                       # Custom identifier for tracking the payment
    "comment": string                           # Additional note or comment for internal reference
  }'
  const { BitGo } = require('bitgo');
  const { getLightningWallet } = require('@bitgo/abstract-lightning');
  const accessToken = '<YOUR_ACCESS_TOKEN>';

  // Initialize the SDK
  const bitgo = new BitGo({
    accessToken: accessToken,
    env: 'test',
    customRootURI: 'https://app.bitgo-test.com',
  });

  // Enter your Lightning wallet
  const walletId = '<YOUR_WALLET_ID>'
  const existingWallet = await bitgo.coin('tlnbtc').wallets().get({ id: walletId });
  const lightningWallet = getLightningWallet(existingWallet);

  const onchainWithdrawal = await lightningWallet.withdrawOnchain({
    recipients: [
      {
        amount: 'amount to be sent in satoshis',
        address: 'destination address'
      }
    ],
    satsPerVbyte: 'fee rate in satoshis per virtual byte',
    passphrase: 'your wallet passphrase',
  });
  console.dir(onchainWithdrawal);

Step Result

If your on-chain withdrawal doesn't require approval, BitGo publishes the transaction to the blockchain. If you Create Policy Rules to require approvals on withdrawals, the transaction remains in a pending-approval status until a wallet admin approves it.

{
    "txRequestId": string,        // Unique identifier for the transaction request
    "txRequestState": delivered,  // State of the transaction request
    "withdrawStatus": {
      "status": delivered,        // Withdrawal status
      "txid": string,             // transaction ID of the on-chain transaction
      "failureReason": string     // Reason for failure if withdrawal failed
    },
    "transfer": {
      "id": string,
      "coin": string,
      "wallet": string,
      "walletType": string,
      "enterprise": string,
      "organization": string,
      "txid": string,
      "state": string,            // initialized, unconfirmed, confirmed
      "type": string,             // send, receive
      "value": number             // millisatoshis
      "valueString": string,      // millisatoshis
      "coinSpecific": {
        "isOffchain": false,
        "invoice": string
      },
    }
}

3. (Optional) Approve Transaction

Note: If you configure an approval requirement for withdrawals, you can't approve your own transactions - another admin must approve them.

Endpoint: Update Pending Approval

export APPROVAL_ID="<APPROVAL_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export OTP="<YOUR_OTP>"

curl -X PUT \
  https://app.bitgo-test.com/api/v2/pendingApprovals/$APPROVAL_ID \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "state": "approved",
    "otp": "'"$OTP"'"
  }'
const baseCoin = this.bitgoSDK.coin(initialPendingApproval.coin);
const pendingApproval = await baseCoin.pendingApprovals().get({ id: initialPendingApproval.id });
const result = await pendingApproval.approve(params);

Step Result

Once approved, BitGo submits the transaction to a Lightning Network Daemon (LND) for broadcasting to the Bitcoin blockchain.

{
  "id": "655686880765186f0b3e9e88e1bdd0f4",
  "coin": "tbtc4",
  "wallet": "6553e933288be490293ae748efafeaaf",
  "enterprise": "62c5ae8174ac860007aff138a2d74df7",
  "creator": "62ab90e06dfda30007974f0a52a12995",
  "createDate": "2023-11-16T21:15:52.703Z",
  "info": {
    "type": "transactionRequest",
    "transactionRequest": {
      "requestedAmount": "10000",
      "fee": 45242,
      "sourceWallet": "6553e933288be490293ae748efafeaaf",
      "policyUniqueId": "6553e933288be490293ae753",
      "recipients": [
        {
          "address": "2N3sBpM1RnWRMXnEVoUWnM7xtYzL756JE2Q",
          "amount": "10000",
          "_id": "655686880765186f0b3e9e8a"
        }
      ],
      "buildParams": {
        "recipients": [
          {
            "address": "2N3sBpM1RnWRMXnEVoUWnM7xtYzL756JE2Q",
            "amount": "10000"
          }
        ],
        "changeAddressType": [
          "p2trMusig2",
          "p2wsh",
          "p2shP2wsh",
          "p2sh",
          "p2tr"
        ],
        "txFormat": "psbt"
      },
      "coinSpecific": {
        "tbtc4": {
          "txHex": "01000000000101eac5e7d68acfc2349672fb99094e79c2006e5fd663475c8f1eb6f29095970b740100000000ffffffff02102700000000000017a914747e6b7f5db53794b0fc01d95878e0f9b6db96738746c1020000000000220020efb9deaabeab5d62cecc363f24cd6deafcdd14d93d8734f7f01ed3953e732a640500483045022100cd6c221e4cefbb51aa82087d86e7d089b2473eb21ae809dba89eb9f13c4caf19022000f3b7f1b7fec2dc49cbfce35baa69ca37ab9ee8e51c779cde5b98a653f3e142010000695221029bde55661e4f359cf9ca2d1846c235e752f760ed6d65e2018f821253e78b3c722103f2b4a813ab79e4fb46edf3a6a87fb154a85b45810158e949f41a3fce3c9bf574210399a6d766f6d3a3843f8479446ee766b5745dc15c24d1db5149214d27987bd29453ae00000000"
        }
      },
      "validTransaction": "01000000000101eac5e7d68acfc2349672fb99094e79c2006e5fd663475c8f1eb6f29095970b740100000000ffffffff02102700000000000017a914747e6b7f5db53794b0fc01d95878e0f9b6db96738746c1020000000000220020efb9deaabeab5d62cecc363f24cd6deafcdd14d93d8734f7f01ed3953e732a640400483045022100cd6c221e4cefbb51aa82087d86e7d089b2473eb21ae809dba89eb9f13c4caf19022000f3b7f1b7fec2dc49cbfce35baa69ca37ab9ee8e51c779cde5b98a653f3e14201473044022002b246c43722fec49858caf6b0605a0d01a6b6c13c964b84bf272604fd3951780220324ee44ba3ce8febb154c317e688a5f6ed410ad8e2bd77c5678bf1f7f3eb45f601695221029bde55661e4f359cf9ca2d1846c235e752f760ed6d65e2018f821253e78b3c722103f2b4a813ab79e4fb46edf3a6a87fb154a85b45810158e949f41a3fce3c9bf574210399a6d766f6d3a3843f8479446ee766b5745dc15c24d1db5149214d27987bd29453ae00000000",
      "validTransactionHash": "ff40ccd5c8ba75ffdce27d8584b6e8ee625cb3f71f7cbb6d072a445a97c2c8c3"
    }
  },
  "state": "approved",
  "scope": "wallet",
  "userIds": [
    "62ab90e06dfda30007974f0a52a12995",
    "627ff9325a5c1b0007c05a40d15e1522"
  ],
  "approvalsRequired": 1,
  "singleRunResults": [
    {
      "ruleId": "Custody Enterprise Transaction ID Verification",
      "triggered": false,
      "_id": "655686880765186f0b3e9e89"
    }
  ],
  "resolvers": [
    {
      "user": "627ff9325a5c1b0007c05a40d15e1522",
      "date": "2023-11-16T21:33:24.644Z",
      "resolutionType": "pending"
    }
  ]
}
{
  "id": "65568eda6c1568b87f40a6131d4fbc89",
  "coin": "tbtc4",
  "wallet": "6553e933288be490293ae748efafeaaf",
  "enterprise": "62c5ae8174ac860007aff138a2d74df7",
  "creator": "62ab90e06dfda30007974f0a52a12995",
  "createDate": "2023-11-16T21:51:22.221Z",
  "info": {
    "type": "transactionRequest",
    "transactionRequest": {
      "requestedAmount": "20000",
      "fee": 51622,
      "sourceWallet": "6553e933288be490293ae748efafeaaf",
      "policyUniqueId": "6553e933288be490293ae753",
      "recipients": [
        {
          "address": "2MzCZbQ2pcHKBPzhpdeaKBgu52YtqdDv6Le",
          "amount": "10000",
          "_id": "65568eda6c1568b87f40a615"
        },
        {
          "address": "2NDi4jr1DjqpWBU6FJRqq5uaiWihwXJAWSU",
          "amount": "10000",
          "_id": "65568eda6c1568b87f40a616"
        }
      ],
      "buildParams": {
        "recipients": [
          {
            "address": "2MzCZbQ2pcHKBPzhpdeaKBgu52YtqdDv6Le",
            "amount": "10000"
          },
          {
            "address": "2NDi4jr1DjqpWBU6FJRqq5uaiWihwXJAWSU",
            "amount": "10000"
          }
        ],
        "changeAddressType": [
          "p2trMusig2",
          "p2wsh",
          "p2shP2wsh",
          "p2sh",
          "p2tr"
        ],
        "txFormat": "psbt"
      },
      "coinSpecific": {
        "tbtc4": {
          "txHex": "01000000000101c3c8c2975a442a076dbb7c1ff7b35c62eee8b684857de2dcff75bac8d5cc40ff0100000000ffffffff03102700000000000017a9144c47ff958d212988eb5e0407ede223d66d8fdbab87102700000000000017a914e07606a95924b156375a9b3929c2ae63db20e3bd8780a90100000000002200209cb3a333ddbdb7496ad4c7ee8f339ac2e3dfb648e3dfec4b0635b0e5541102610500483045022100883f2c5a762a307380723cad5d536329b45aa2a67ee5c53940551c926902929002203cba824bc6fc01ba6510131b47137dc7b292d96938c89c7e12b891ca039781f601000069522102b2c328f03cb3b1a513adaae92226dfbaeabfdf053ba5057b1922a327798261a22102c0e4a38730d48b220547abe8d6676c9a432245c7c968b5e8e9609a7837c773b021025e180472019c87bc2cdef9e1bc787369e9e5fd6212c5f01549bb96c155e8cdbd53ae00000000"
        }
      },
      "validTransaction": "01000000000101c3c8c2975a442a076dbb7c1ff7b35c62eee8b684857de2dcff75bac8d5cc40ff0100000000ffffffff03102700000000000017a9144c47ff958d212988eb5e0407ede223d66d8fdbab87102700000000000017a914e07606a95924b156375a9b3929c2ae63db20e3bd8780a90100000000002200209cb3a333ddbdb7496ad4c7ee8f339ac2e3dfb648e3dfec4b0635b0e5541102610400483045022100883f2c5a762a307380723cad5d536329b45aa2a67ee5c53940551c926902929002203cba824bc6fc01ba6510131b47137dc7b292d96938c89c7e12b891ca039781f60147304402203c309e14eda12d133d7885deddbf95b15878fc69758bd81bb3b3fbaf1088c8dc02201286da4e0ffde355d399b6659feb2d30db28f26586e0de9d3f8f9a6fb701ac330169522102b2c328f03cb3b1a513adaae92226dfbaeabfdf053ba5057b1922a327798261a22102c0e4a38730d48b220547abe8d6676c9a432245c7c968b5e8e9609a7837c773b021025e180472019c87bc2cdef9e1bc787369e9e5fd6212c5f01549bb96c155e8cdbd53ae00000000",
      "validTransactionHash": "4929b91960acdf2d18076ac6151d0aff96b4f4834f9f78eaf40e78ce05f5c0a4"
    }
  },
  "state": "approved",
  "scope": "wallet",
  "userIds": [
    "62ab90e06dfda30007974f0a52a12995",
    "627ff9325a5c1b0007c05a40d15e1522"
  ],
  "approvalsRequired": 1,
  "singleRunResults": [
    {
      "ruleId": "Custody Enterprise Transaction ID Verification",
      "triggered": false,
      "_id": "65568eda6c1568b87f40a614"
    }
  ],
  "resolvers": [
    {
      "user": "627ff9325a5c1b0007c05a40d15e1522",
      "date": "2023-11-16T21:56:45.511Z",
      "resolutionType": "pending"
    }
  ]
}

Next

You can view your completed on-chain withdrawal in BitGo or on a blockchain explorer.

See Also