From 23e455d1b7be217bbd5393ab1256bc5571c956f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 13 Apr 2024 18:33:21 +0000 Subject: [PATCH] deploy: 6a7be89920bed46ecdb9d35a097f50250ec6aae7 --- 404.html | 6 +++--- assets/js/{19.cd75d4ee.js => 19.785a19b0.js} | 2 +- assets/js/{31.dd5b652f.js => 31.c8bce6b9.js} | 2 +- assets/js/{40.3a3417fd.js => 40.578db781.js} | 2 +- assets/js/{46.5deb1292.js => 46.5ebccb0a.js} | 2 +- assets/js/{47.a9e58080.js => 47.95bbbc17.js} | 2 +- assets/js/{49.b70acbda.js => 49.fd2860a9.js} | 2 +- assets/js/{51.29907970.js => 51.1ead12c8.js} | 2 +- assets/js/{53.fb891878.js => 53.f85cebcd.js} | 2 +- assets/js/{54.e6d144fb.js => 54.d9630932.js} | 2 +- assets/js/{55.79f489cb.js => 55.aa8604b5.js} | 2 +- assets/js/{56.56539c8a.js => 56.06ad83c0.js} | 2 +- assets/js/{57.cd2a1739.js => 57.3820b93c.js} | 2 +- assets/js/{60.08677303.js => 60.e9dd6778.js} | 2 +- assets/js/{61.26c526fa.js => 61.d25557c6.js} | 2 +- assets/js/{62.3d27b00b.js => 62.3606e160.js} | 2 +- assets/js/{63.db35ff13.js => 63.2518a4ae.js} | 2 +- assets/js/{64.97b3560d.js => 64.c74d4d9a.js} | 2 +- assets/js/{65.86cbe84e.js => 65.836aeb00.js} | 2 +- assets/js/{67.36d97d2b.js => 67.c883933d.js} | 2 +- assets/js/{68.447d7bfd.js => 68.139a2d38.js} | 2 +- assets/js/{70.42bd691e.js => 70.c2fb4c8e.js} | 2 +- assets/js/{71.2b84faa7.js => 71.d8561b66.js} | 2 +- assets/js/{75.24e75de4.js => 75.b98ce942.js} | 2 +- assets/js/{76.2c842c7e.js => 76.e04e7796.js} | 2 +- assets/js/{77.1bd88a81.js => 77.4d718ed0.js} | 2 +- assets/js/{78.c52dfcaf.js => 78.2338c463.js} | 2 +- assets/js/{79.5fec677c.js => 79.675cb888.js} | 2 +- .../js/{app.041f1d93.js => app.93627953.js} | 4 ++-- bdk-cli/compiler/index.html | 6 +++--- bdk-cli/concept/index.html | 6 +++--- bdk-cli/index.html | 6 +++--- bdk-cli/installation/index.html | 6 +++--- bdk-cli/interface/index.html | 6 +++--- bdk-cli/introduction/index.html | 6 +++--- bdk-cli/playground/index.html | 6 +++--- bdk-cli/regtest/index.html | 6 +++--- blog/2020/12/hello-world/index.html | 6 +++--- blog/2020/12/release-v0.2.0/index.html | 6 +++--- .../index.html | 6 +++--- .../index.html | 6 +++--- .../index.html | 6 +++--- blog/2021/01/release-v0.3.0/index.html | 6 +++--- blog/2021/02/release-v0.4.0/index.html | 6 +++--- blog/2021/03/release-v0.5.0/index.html | 6 +++--- blog/2021/04/release-v0.6.0/index.html | 6 +++--- blog/2021/05/release-v0.7.0/index.html | 6 +++--- blog/2021/06/release-v0.8.0/index.html | 6 +++--- blog/2021/07/release-v0.9.0/index.html | 6 +++--- .../index.html | 6 +++--- .../index.html | 6 +++--- blog/_2024-q4-update/index.html | 6 +++--- blog/author/Alekos Filini/index.html | 4 ++-- blog/author/Alekos Filini/page/2/index.html | 4 ++-- blog/author/Bitcoin Zavior/index.html | 4 ++-- .../C\303\251sar Alvarez Vallero/index.html" | 4 ++-- blog/author/Daniela Brozzoni/index.html | 4 ++-- blog/author/Gabriele Domenichini/index.html | 4 ++-- blog/author/Lloyd Fournier/index.html | 4 ++-- blog/author/Rajarshi Maitra/index.html | 4 ++-- blog/author/Riccardo Casatta/index.html | 8 ++++---- blog/author/Sandipan Dey/index.html | 4 ++-- blog/author/Steve Myers/index.html | 4 ++-- blog/author/Wszdexdrf/index.html | 4 ++-- blog/author/index.html | 6 +++--- blog/author/rorp/index.html | 4 ++-- blog/author/thunderbiscuit/index.html | 4 ++-- blog/author/waterst0ne/index.html | 4 ++-- blog/bdk-cli-basics-multisig-2of3/index.html | 6 +++--- blog/bdk-cli-basics/index.html | 6 +++--- blog/bdk-core-pt1/index.html | 6 +++--- blog/bdk-rn-making-of/index.html | 6 +++--- blog/bdk-with-tor/index.html | 6 +++--- blog/bindings-scope/index.html | 6 +++--- blog/bitcoin-core-rpc-demo/index.html | 6 +++--- blog/compact-filters-demo/index.html | 6 +++--- blog/descriptor-based-paper-wallet/index.html | 6 +++--- blog/descriptors-in-the-wild/index.html | 6 +++--- blog/exploring-bdk-flutter/index.html | 6 +++--- blog/exploring-bdk-rn/index.html | 6 +++--- blog/getting-started-with-rust-hwi/index.html | 6 +++--- blog/hidden-power-of-bitcoin/index.html | 6 +++--- .../index.html | 6 +++--- blog/index.html | 4 ++-- blog/miniscript-vulnerability/index.html | 6 +++--- blog/page/2/index.html | 4 ++-- blog/page/3/index.html | 8 ++++---- blog/page/4/index.html | 4 ++-- blog/road-to-bdk-1/index.html | 6 +++--- blog/spending-policy-demo/index.html | 6 +++--- blog/tags/Android/index.html | 4 ++-- blog/tags/Architecture/index.html | 4 ++-- blog/tags/BDK-RN/index.html | 4 ++-- blog/tags/BDK/index.html | 4 ++-- blog/tags/BIP157/index.html | 4 ++-- blog/tags/Bitcoin Core/index.html | 4 ++-- blog/tags/Development/index.html | 4 ++-- blog/tags/Flutter/index.html | 4 ++-- blog/tags/Hardware Wallets/index.html | 4 ++-- blog/tags/Neutrino/index.html | 4 ++-- blog/tags/RPC/index.html | 4 ++-- blog/tags/React Native/index.html | 4 ++-- blog/tags/Wallet/index.html | 4 ++-- blog/tags/architecture/index.html | 4 ++-- blog/tags/basics/index.html | 4 ++-- blog/tags/bdk-cli/index.html | 4 ++-- blog/tags/bdk-rn/index.html | 4 ++-- blog/tags/bdk/index.html | 4 ++-- blog/tags/bindings/index.html | 4 ++-- blog/tags/bitcoin-cli/index.html | 4 ++-- blog/tags/bitcoin/index.html | 4 ++-- blog/tags/blockchain/index.html | 4 ++-- blog/tags/coin selection/index.html | 4 ++-- blog/tags/compact_filters/index.html | 4 ++-- blog/tags/descriptor/index.html | 4 ++-- blog/tags/development/index.html | 4 ++-- blog/tags/fee/index.html | 8 ++++---- blog/tags/getting started/index.html | 4 ++-- blog/tags/guide/index.html | 4 ++-- blog/tags/iOS/index.html | 4 ++-- blog/tags/index.html | 6 +++--- blog/tags/machine learning/index.html | 8 ++++---- blog/tags/miniscript/index.html | 4 ++-- blog/tags/mobile/index.html | 4 ++-- blog/tags/multi-sig/index.html | 4 ++-- blog/tags/novice/index.html | 4 ++-- blog/tags/paper wallets/index.html | 4 ++-- blog/tags/project/index.html | 4 ++-- blog/tags/release/index.html | 4 ++-- blog/tags/rust/index.html | 4 ++-- blog/tags/security/index.html | 4 ++-- blog/tags/summer of bitcoin/index.html | 4 ++-- blog/tags/taproot/index.html | 4 ++-- blog/tags/tor/index.html | 4 ++-- blog/tags/tutorial/index.html | 4 ++-- blog/tags/wallet/index.html | 4 ++-- .../index.html | 6 +++--- case-studies/index.html | 4 ++-- descriptors/index.html | 6 +++--- examples/index.html | 6 +++--- foundation/index.html | 4 ++-- getting-started/index.html | 6 +++--- img/case-studies-logos/lexe-130.png | Bin 3552 -> 8563 bytes index.html | 4 ++-- sitemap.xml | 2 +- supporters/index.html | 6 +++--- 146 files changed, 319 insertions(+), 319 deletions(-) rename assets/js/{19.cd75d4ee.js => 19.785a19b0.js} (99%) rename assets/js/{31.dd5b652f.js => 31.c8bce6b9.js} (99%) rename assets/js/{40.3a3417fd.js => 40.578db781.js} (99%) rename assets/js/{46.5deb1292.js => 46.5ebccb0a.js} (99%) rename assets/js/{47.a9e58080.js => 47.95bbbc17.js} (99%) rename assets/js/{49.b70acbda.js => 49.fd2860a9.js} (99%) rename assets/js/{51.29907970.js => 51.1ead12c8.js} (99%) rename assets/js/{53.fb891878.js => 53.f85cebcd.js} (99%) rename assets/js/{54.e6d144fb.js => 54.d9630932.js} (99%) rename assets/js/{55.79f489cb.js => 55.aa8604b5.js} (99%) rename assets/js/{56.56539c8a.js => 56.06ad83c0.js} (99%) rename assets/js/{57.cd2a1739.js => 57.3820b93c.js} (99%) rename assets/js/{60.08677303.js => 60.e9dd6778.js} (98%) rename assets/js/{61.26c526fa.js => 61.d25557c6.js} (99%) rename assets/js/{62.3d27b00b.js => 62.3606e160.js} (99%) rename assets/js/{63.db35ff13.js => 63.2518a4ae.js} (99%) rename assets/js/{64.97b3560d.js => 64.c74d4d9a.js} (99%) rename assets/js/{65.86cbe84e.js => 65.836aeb00.js} (99%) rename assets/js/{67.36d97d2b.js => 67.c883933d.js} (99%) rename assets/js/{68.447d7bfd.js => 68.139a2d38.js} (92%) rename assets/js/{70.42bd691e.js => 70.c2fb4c8e.js} (98%) rename assets/js/{71.2b84faa7.js => 71.d8561b66.js} (99%) rename assets/js/{75.24e75de4.js => 75.b98ce942.js} (98%) rename assets/js/{76.2c842c7e.js => 76.e04e7796.js} (99%) rename assets/js/{77.1bd88a81.js => 77.4d718ed0.js} (99%) rename assets/js/{78.c52dfcaf.js => 78.2338c463.js} (98%) rename assets/js/{79.5fec677c.js => 79.675cb888.js} (97%) rename assets/js/{app.041f1d93.js => app.93627953.js} (76%) diff --git a/404.html b/404.html index 6bda3c6146..f885ec872a 100644 --- a/404.html +++ b/404.html @@ -15,7 +15,7 @@ - + @@ -39,7 +39,7 @@ Blog GitHub - (opens new window)

404 - Not Found

Looks like we've got some broken links.
+ (opens new window)

404 - Not Found

How did we get here?
Take me home.
- + diff --git a/assets/js/19.cd75d4ee.js b/assets/js/19.785a19b0.js similarity index 99% rename from assets/js/19.cd75d4ee.js rename to assets/js/19.785a19b0.js index bd758b15f0..eb98dc08f6 100644 --- a/assets/js/19.cd75d4ee.js +++ b/assets/js/19.785a19b0.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[19],{347:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_complete_app.c74da859.png"},348:function(t,a,s){t.exports=s.p+"assets/img/default_flutter_app.31b31721.png"},349:function(t,a,s){t.exports=s.p+"assets/img/assets_section.b43d75e9.png"},350:function(t,a,s){t.exports=s.p+"assets/img/folder_structure.b2750bd6.png"},351:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_title.e4e3484a.png"},352:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_tutorial_screen_mnemonic.df703b77.png"},353:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_tutorial_screen_createwallet.3b052736.png"},354:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_get_balance.bfdf9ced.png"},355:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_get_address.5db2e3cc.png"},356:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_get_restore.db8e7e55.png"},357:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_send.1688372b.png"},395:function(t,a,s){"use strict";s.r(a);var n=s(7),e=Object(n.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h2",{attrs:{id:"introduction"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#introduction"}},[t._v("#")]),t._v(" Introduction")]),t._v(" "),a("p",[a("code",[t._v("bdk-flutter")]),t._v(" is the "),a("strong",[t._v("Bitcoin Dev kit")]),t._v("'s "),a("strong",[t._v("Flutter")]),t._v(" library which enables building bitcoin applications for Android and iOS mobile platforms. Using "),a("code",[t._v("bdk-flutter")]),t._v(" is similar to using any other Flutter module. Just do "),a("code",[t._v("flutter pub add bdk_flutter")]),t._v(" and you are ready to code! This is the first tutorial on how to use "),a("code",[t._v("bdk-flutter")]),t._v(", more coming soon, make sure to "),a("a",{attrs:{href:"https://twitter.com/BitcoinZavior",target:"_blank",rel:"noopener noreferrer"}},[t._v("follow"),a("OutboundLink")],1),t._v(" to be notified of new ones. There will also be a "),a("strong",[a("code",[t._v("bdk-flutter")])]),t._v(" focused Livestream on "),a("a",{attrs:{href:"https://www.twitch.tv/bitcoindevelopers",target:"_blank",rel:"noopener noreferrer"}},[t._v("Twitch"),a("OutboundLink")],1),t._v(" on the Bitcoin Developers "),a("a",{attrs:{href:"https://www.youtube.com/channel/UCUq_ZdezVWKPvkWRicAYxLA/videos",target:"_blank",rel:"noopener noreferrer"}},[t._v("YouTube Channel"),a("OutboundLink")],1),t._v(" so make sure to subscribe.")]),t._v(" "),a("p",[t._v("This tutorial will explore "),a("code",[t._v("bdk-flutter")]),t._v(" usage and the API it provides. This guide will walk through the development process and code for making a bitcoin application. The bitcoin application we create will be a non-custodial HD Wallet. The application will have the functionality to create a new wallet or restore from a known mnemonic seed phrase. This application will also be able to interact with the bitcoin network to sync UTXOs from new blocks and broadcast transactions.")]),t._v(" "),a("p",[t._v("The tutorial will focus on bitcoin concepts and "),a("code",[t._v("bdk-flutter")]),t._v(" API. So it will gloss over Flutter and Dart. If you are interested in learning more about Flutter and Dart please refer to the Flutter "),a("a",{attrs:{href:"https://flutter.dev/learn",target:"_blank",rel:"noopener noreferrer"}},[t._v("learning portal"),a("OutboundLink")],1),t._v(". The code for this tutorial is available on the "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart",target:"_blank",rel:"noopener noreferrer"}},[t._v("LtbLightning GitHub"),a("OutboundLink")],1)]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(347)}}),t._v(" "),a("h3",{attrs:{id:"prerequisites"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#prerequisites"}},[t._v("#")]),t._v(" Prerequisites")]),t._v(" "),a("p",[t._v("To use "),a("code",[t._v("bdk-flutter")]),t._v(" in a Flutter App, a Flutter development environment is required. Please refer to resources out there on the internet if you need to set this up, here is one of many good resources to guide you on "),a("a",{attrs:{href:"https://docs.flutter.dev/get-started/install",target:"_blank",rel:"noopener noreferrer"}},[t._v("environment setup"),a("OutboundLink")],1)]),t._v(" "),a("h3",{attrs:{id:"bitcoin-basics"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#bitcoin-basics"}},[t._v("#")]),t._v(" Bitcoin Basics")]),t._v(" "),a("p",[t._v("The bitcoin concepts used in this blog post are detailed and explained very well in external bitcoin resources. Here are some links for reference:")]),t._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch04.asciidoc",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mastering Bitcoin(HD Wallet chapter)"),a("OutboundLink")],1)]),t._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("Bitcoin Output Descriptors from bitcoin GitHub"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("Now let's jump into Bitcoin Dev Kit")]),t._v(" "),a("h2",{attrs:{id:"bitcoin-dev-kit-and-bdk-flutter"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#bitcoin-dev-kit-and-bdk-flutter"}},[t._v("#")]),t._v(" Bitcoin Dev Kit and bdk-flutter")]),t._v(" "),a("p",[a("code",[t._v("bdk-flutter")]),t._v(" is "),a("strong",[t._v("Bitcoin Dev kit")]),t._v("'s "),a("strong",[t._v("Flutter")]),t._v(" library for building Bitcoin apps in "),a("strong",[t._v("Flutter")]),t._v(".\nIt encapsulates all of the low-level APIs and methods for BDK and exposes them in a Flutter context. To use BDK in Flutter apps only the "),a("code",[t._v("bdk-flutter")]),t._v(" module is required. "),a("code",[t._v("bdk-flutter")]),t._v(" can be used like any other Flutter library and is available on "),a("a",{attrs:{href:"https://pub.dev/packages/bdk_flutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("pub.dev"),a("OutboundLink")],1)]),t._v(" "),a("h2",{attrs:{id:"getting-started"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#getting-started"}},[t._v("#")]),t._v(" Getting Started")]),t._v(" "),a("p",[t._v("Although we won't delve deep into Flutter we will focus more on bitcoin and "),a("code",[t._v("bdk-flutter")]),t._v(", however, some rudimentary Flutter setup is required, especially a basic Flutter app to add our code.")]),t._v(" "),a("p",[t._v("start by creating a new Flutter project.")]),t._v(" "),a("p",[a("code",[t._v("flutter create bdk-flutter-quickstart")])]),t._v(" "),a("p",[t._v("Once done let's "),a("code",[t._v("cd")]),t._v(" into the new project directory and run the basic Flutter app that's created")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" bdk-flutter-quickstart\nflutter run\n")])])]),a("p",[t._v("This should start building the app and then launch the app in a simulator. So far we have created a basic Flutter project if this doesn't work then refer to the Flutter development setup guide to troubleshoot.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(348),alt:"BDK Flutter Quick Start"}}),t._v(" "),a("h2",{attrs:{id:"setting-up-flutter-app-structure"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#setting-up-flutter-app-structure"}},[t._v("#")]),t._v(" Setting up Flutter app structure")]),t._v(" "),a("p",[t._v("Let's set up a very basic app structure. Let's create an "),a("code",[t._v("assets")]),t._v(" folder in the project root and then add new folders "),a("code",[t._v("widgets")]),t._v(", "),a("code",[t._v("screens")]),t._v(", and "),a("code",[t._v("styles")]),t._v(" inside the existing "),a("code",[t._v("lib")]),t._v(" folder.")]),t._v(" "),a("p",[t._v("Paste the following code in your "),a("code",[t._v("pubspec.yaml")]),t._v(" file, assets section.")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",[a("code",[t._v("- assets/\n")])])]),a("p",[t._v("Please make sure your assets section looks like the screenshot below.\n"),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"60%"},attrs:{src:s(349),alt:"BDK Flutter Quick Start"}})]),t._v(" "),a("p",[t._v("Once done let's run a "),a("code",[t._v("get")]),t._v(" command from the pub tool commands, this will get all the required dependencies for our project.")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("flutter pub get\n")])])]),a("p",[t._v("To make this quick you can download the theme, styled widgets and images used in the tutorial from the repository. The "),a("code",[t._v("theme.dart")]),t._v(" file has the theme we will use and this can be taken from "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart/blob/master/lib/styles/theme.dart",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(" and moved to the styles folder. The "),a("code",[t._v("widgets.dart")]),t._v(" file has the styled widgets we will use and these can be taken from "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart/blob/master/lib/widgets/widgets.dart",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(" and moved to the widgets folder. The image assets can be taken from "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart/tree/master/assets",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(" Alternatively, you can write your theme, widgets and use your images if you intend to style the app differently.")]),t._v(" "),a("p",[t._v("In addition to the the theme, widgets and assets. We also need to create a "),a("code",[t._v("screens")]),t._v(" folder and create a "),a("code",[t._v("home.dart")]),t._v(" file inside it, this will be where most of the code will be added.")]),t._v(" "),a("p",[t._v("Once done the file structure should look like this:")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0px auto",zoom:"60%"},attrs:{src:s(350)}}),t._v(" "),a("p",[a("br"),t._v("Locate "),a("code",[t._v("main.dart")]),t._v(" in the project root, this will have the default code added by "),a("code",[t._v("flutter create")]),t._v(", let's delete all contents of "),a("code",[t._v("main.dart")]),t._v(" and replace it with the following code to use "),a("code",[t._v("home.dart")]),t._v(" as our main screen. This will probably crash the app but that's fine, it will be up and running once we add code to "),a("code",[t._v("home.dart")]),t._v(" in the next few steps")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// main.dart")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter_quickstart/screens/home.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter_quickstart/styles/theme.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:flutter/material.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("runApp")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StatelessWidget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Key")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("super")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// This widget is the root of your application.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MaterialApp")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n debugShowCheckedModeBanner"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n title"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'BDK-FLUTTER TUTORIAL'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n theme"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("theme")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n home"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"installing-bdk-flutter"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#installing-bdk-flutter"}},[t._v("#")]),t._v(" Installing "),a("code",[t._v("bdk-flutter")])]),t._v(" "),a("p",[t._v("With the Flutter project in place, we can now add "),a("code",[t._v("bdk-flutter")]),t._v(" using "),a("code",[t._v("flutter pub add")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("flutter pub "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" bdk_flutter\n")])])]),a("p",[t._v("This will add a line like this to your package's "),a("code",[t._v("pubspec.yaml")]),t._v(" and this will also run an implicit flutter pub get to download "),a("code",[t._v("bdk-flutter")]),t._v(" from "),a("code",[t._v("pub.dev")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("dependencies:\n bdk_flutter: ^0.28.2\n")])])]),a("h2",{attrs:{id:"configuring"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#configuring"}},[t._v("#")]),t._v(" Configuring")]),t._v(" "),a("p",[t._v("Make sure your app meets the following requirements for using "),a("code",[t._v("bdk-flutter")])]),t._v(" "),a("p",[a("strong",[t._v("Android")])]),t._v(" "),a("p",[t._v("MinSdkVersion : API 23 or higher.")]),t._v(" "),a("p",[a("strong",[t._v("IOS")])]),t._v(" "),a("p",[t._v("Deployment target: iOS 12.0 or greater.")]),t._v(" "),a("p",[t._v("Locate your Podfile in the ios folder of your project and paste the following code at the beginning")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("platform :ios, '12.0'\n")])])]),a("p",[t._v("After changing the deployment target in your project's "),a("code",[t._v("PodFile")]),t._v(", let's use the following "),a("code",[t._v("command")]),t._v(" to install pod dependencies for iOS.")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" ios "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" pod "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v("\n")])])]),a("p",[t._v("Once done, bdk-flutter is installed and configured and ready to be used in our "),a("strong",[t._v("bdk-flutter-quickstart")]),t._v(" App.")]),t._v(" "),a("h2",{attrs:{id:"importing-bdk-flutter"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#importing-bdk-flutter"}},[t._v("#")]),t._v(" Importing "),a("code",[t._v("bdk-flutter")])]),t._v(" "),a("p",[t._v("Locate "),a("code",[t._v("home.dart")]),t._v(" which we added in the setup section and import "),a("code",[t._v("bdk-flutter")]),t._v(" at the top of the file. Create a stateful widget called "),a("code",[t._v("Home")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// screens/home.dart")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter/bdk_flutter.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StatefulWidget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Key")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("super")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_HomeState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" _HomeState "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" mnemonic "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Container")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Before we start using "),a("code",[t._v("bdk-flutter")]),t._v(" let's add some additional imports and also import styles, to create a basic layout to build our home screen")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// screens/home.dart")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter/bdk_flutter.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter_quickstart/widgets/widgets.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:flutter/cupertino.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:flutter/material.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StatefulWidget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Key")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("super")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_HomeState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" _HomeState "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Scaffold")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n resizeToAvoidBottomInset"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n backgroundColor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Colors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("white"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* AppBar */")]),t._v("\n appBar"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("buildAppBar")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n body"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SingleChildScrollView")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Container")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n padding"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EdgeInsets")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("symmetric")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("horizontal"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("30")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Column")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n children"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Balance */")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Create Wallet */")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Send Transaction */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("We now have an app title section and a structure to hold the rest of our app components.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"33%"},attrs:{src:s(351)}}),t._v(" "),a("h2",{attrs:{id:"calling-bdk-flutter-methods"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#calling-bdk-flutter-methods"}},[t._v("#")]),t._v(" Calling bdk-flutter methods")]),t._v(" "),a("p",[t._v("To call all methods properly from the "),a("code",[t._v("bdk-flutter")]),t._v(" package, first, we need to create state variables to store "),a("code",[t._v("Wallet")]),t._v(" and "),a("code",[t._v("Blockchain")]),t._v(" objects.")]),t._v(" "),a("p",[t._v("Here we use the late keyword to declare both "),a("code",[t._v("Wallet")]),t._v(" and "),a("code",[t._v("Blockchain")]),t._v(". These are non-nullable variables that are initialized after the declaration.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter/bdk_flutter.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nlate "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Wallet")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nlate "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Blockchain")]),t._v(" blockchain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("The first step in creating a non-custodial bitcoin app is creating a mnemonic seed phrase for the wallet.")]),t._v(" "),a("p",[a("code",[t._v("bdk-flutter")]),t._v(" provides a "),a("code",[t._v("Mnemonic")]),t._v(" class to create a "),a("code",[t._v("Mnemonic")]),t._v(". The "),a("code",[t._v("create")]),t._v(" method is a named constructor and can be used to create a mnemonic, it takes "),a("code",[t._v("WordCount")]),t._v(" as its required parameter.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WordCount.Words12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("We can generate a mnemonic of longer length by passing in a wordCount argument of required length.")]),t._v(" "),a("p",[t._v("To create a mnemonic with a "),a("code",[t._v("WordCount")]),t._v(" of 18 words, we can use "),a("code",[t._v("(WordCount.Words18)")]),t._v("\nRefer to the API docs on "),a("a",{attrs:{href:"https://pub.dev/documentation/bdk_flutter/latest/bdk_flutter/bdk_flutter-library.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("pub.dev"),a("OutboundLink")],1),t._v(" for more details.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WordCount.Words18")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// here response is saved as a 'Mnemonic' object")]),t._v("\n")])])]),a("p",[t._v("In order to use this in our Flutter app, we want a button that will generate a mnemonic when clicked, and a text input box to show the generated mnemonic. Let's first create a "),a("code",[t._v("TextEditingController")]),t._v(" for the "),a("code",[t._v("mnemonic")]),t._v(" textfield to store the mnemonic, and an internal "),a("code",[t._v("generateMnemonicHandler")]),t._v(" method which can be called on button click. We will also need a button that will call the internal "),a("code",[t._v("generateMnemonicHandler")]),t._v(" method when clicked. Adding the following code achieves all of this.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StatefulWidget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Key")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("super")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_HomeState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" _HomeState "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n late "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Wallet")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n late "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Blockchain")]),t._v(" blockchain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" mnemonic "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateMnemonicHandler")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WordCount.Words12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("asString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Scaffold")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n resizeToAvoidBottomInset"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n backgroundColor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Colors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("white"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Header */")]),t._v("\n appBar"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("buildAppBar")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n body"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SingleChildScrollView")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Container")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n padding"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EdgeInsets")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("symmetric")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("horizontal"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("30")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Column")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n children"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Balance */")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Result */")]),t._v("\n\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Create Wallet */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StyledContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Column")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n mainAxisAlignment"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MainAxisAlignment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n crossAxisAlignment"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CrossAxisAlignment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("center"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n children"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Generate Mnemonic"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateMnemonicHandler")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFieldContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFormField")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n style"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Theme")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("of")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("textTheme"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bodyText1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n keyboardType"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextInputType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("multiline"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n maxLines"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n decoration"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("InputDecoration")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n hintText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Enter your mnemonic"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Send Transaction Buttons */")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Now we need to add a component to display the output of our method calls and this will also need a "),a("code",[t._v("displayText")]),t._v(" variable to track our method call response. To achieve this add the following code.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// screens/home.dart")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// add this as another state variable under mnemonic")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" displayText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// modify the generateMnemonicHandler method to also set mnemonic as displayText")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateMnemonicHandler")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WordCount.Words12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("asString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("asString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("and finally, let's add the component to display the output under "),a("code",[t._v("/* Result */")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// screens/home.dart")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Result */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// display the component only if displayText has a value")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ResponseContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"No Response"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),a("p",[t._v('We should now have a working "Generate Mnemonic" button that displays the new mnemonic')]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(352)}}),t._v(" "),a("p",[t._v("A quick recap, we added a button to call a click handler ("),a("code",[t._v("generateMnemonicHandler")]),t._v(") which calls "),a("code",[t._v("generateMnemonic")]),t._v(" API of "),a("code",[t._v("bdk-flutter")]),t._v(". The click handler also sets the state for the app and also updates the "),a("code",[t._v("displayText")]),t._v(" variable to display the output of the call in the display section. We will follow this pattern for the remaining calls to "),a("code",[t._v("bdk-flutter")]),t._v(".")]),t._v(" "),a("h2",{attrs:{id:"creating-a-wallet"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#creating-a-wallet"}},[t._v("#")]),t._v(" Creating a wallet")]),t._v(" "),a("p",[t._v("Before moving on to creating a wallet, let's add a section at the top to display the balance of the wallet.")]),t._v(" "),a("p",[t._v("To display the balance we will need a state variable to store the balance and a display component to display it. We will also be creating a receive address for the wallet so a state variable will be required for the address as well.")]),t._v(" "),a("p",[t._v("Under the "),a("code",[t._v("mnemonic")]),t._v(" and "),a("code",[t._v("displayText")]),t._v(" state variables, let's add one for "),a("code",[t._v("balance")]),t._v(" and one for "),a("code",[t._v("address")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" _HomeState "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" mnemonic "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" displayText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" balance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Just below "),a("code",[t._v("/* Balance */")]),t._v(" and above "),a("code",[t._v("/* Result */")]),t._v(" add the following UI components to display the balance. We only want to show the balance when it has a value so we will use a null-aware operator "),a("code",[t._v("??")]),t._v(" for a quick "),a("code",[t._v("null")]),t._v(" check and use "),a("code",[t._v("0")]),t._v(" in case of a "),a("code",[t._v("null")]),t._v(" value.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Balance */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BalanceContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"${balance ?? "')])]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"} Sats"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Result */")]),t._v("\n")])])]),a("p",[a("code",[t._v("bdk_flutter")]),t._v(" creates a wallet using output descriptors which define the derivation path to derive addresses and sign transactions. More about output descriptors "),a("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(". Before creating the "),a("code",[t._v("Wallet")]),t._v(" we need to create a "),a("code",[t._v("descriptor")]),t._v(" object which will be used to generate receive addresses and a "),a("code",[t._v("changeDescriptor")]),t._v(" object to to create change addresses to collect from outgoing transactions.")]),t._v(" "),a("p",[a("code",[t._v("bdk_flutter")]),t._v("'s "),a("code",[t._v("Descriptor")]),t._v(" class has a number of descriptor templates that will help you create a simple wallet.")]),t._v(" "),a("p",[t._v("Let's add some code to create a simple "),a("code",[t._v("wpkh")]),t._v(" descriptor object by using the "),a("code",[t._v("BIP84")]),t._v(" template. This template will create a descriptor in the format "),a("code",[t._v("wpkh(key/84'/{0,1}'/0'/{0,1}/*)")])]),t._v(" "),a("p",[t._v("This descriptor will create receive ("),a("code",[t._v("KeyChainKind.External")]),t._v(") and change descriptor ("),a("code",[t._v("KeyChainKind.Internal")]),t._v(") for a specified mnemonic.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Descriptor")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getDescriptors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" descriptors "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Descriptor")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" e "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("KeychainKind.External")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("KeychainKind.Internal")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" mnemonicObj "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("fromString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" descriptorSecretKey "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DescriptorSecretKey")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Network.Testnet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mnemonicObj"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" descriptor "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Descriptor")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("newBip84")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n secretKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" descriptorSecretKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Network.Testnet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n keychain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n descriptors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("descriptor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" descriptors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("on")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Exception")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Error : ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("rethrow")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("Under the "),a("code",[t._v("address")]),t._v(" state variable, let's add a state variable called "),a("code",[t._v("wallet")]),t._v(" of the type "),a("code",[t._v("Wallet")]),t._v(" for saving the bitcoin wallet.")]),t._v(" "),a("p",[t._v("To create a wallet with "),a("code",[t._v("bdk-flutter")]),t._v(" call the "),a("code",[t._v("create")]),t._v(" constructor with "),a("code",[t._v("descriptor")]),t._v(", "),a("code",[t._v("changeDescriptor")]),t._v(" "),a("code",[t._v("network")]),t._v(", and the "),a("code",[t._v("databaseConfig")]),t._v(". For database, we can use memory as the database by specifying "),a("code",[t._v("DatabaseConfig.memory()")]),t._v("\nFollowing our pattern of a button, click handler and bdk-flutter API call, Let's add an internal method which will serve as the click handler for the \"Create Wallet\" button. We want to see the output of this call so let's use "),a("code",[t._v("setState()")]),t._v(" to set the "),a("code",[t._v("wallet")]),t._v(" object created and the "),a("code",[t._v("displayText")]),t._v(" variable with the wallet's first receive address.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createOrRestoreWallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Network")]),t._v(" network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" password"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" descriptors "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getDescriptors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Wallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n descriptor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" descriptors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n changeDescriptor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" descriptors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n databaseConfig"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DatabaseConfig")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("memory")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" addressInfo "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getAddress")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("addressIndex"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AddressIndex")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" addressInfo"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n wallet "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Wallet Created: ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("address")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("on")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Exception")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Error: ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("A new button will be required to call "),a("code",[t._v("createOrRestoreWallet()")])]),t._v(" "),a("p",[t._v("Let's add a new button just below the mnemonic "),a("code",[t._v("TextFieldContainer")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Create Wallet"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createOrRestoreWallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Network.Testnet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"password"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),a("p",[t._v("The response returned by "),a("code",[t._v("create()")]),t._v(" is a "),a("code",[t._v("Wallet")]),t._v(" object.")]),t._v(" "),a("p",[t._v("The App should now be creating a wallet when we click "),a("strong",[t._v("Create Mnemonic")]),t._v(" followed by "),a("strong",[t._v("Create Wallet")]),t._v(".")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(353)}}),t._v(" "),a("p",[t._v("Before going forward, we need to create a "),a("code",[t._v("Blockchain")]),t._v(" object as well. The Blockchain object will encapsulate the bitcoin node configuration which the wallet will use for syncing blocks and broadcasting transactions.")]),t._v(" "),a("p",[t._v("Let's add an internal method to create and initialize the "),a("code",[t._v("Blockchain")]),t._v(" object.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("blockchainInit")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n blockchain "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Blockchain")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BlockchainConfig")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("electrum")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ElectrumConfig")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n stopGap"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n timeout"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n retry"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n url"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ssl://electrum.blockstream.info:60002"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n validateDomain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Here we are initializing the "),a("code",[t._v("late")]),t._v(" non-nullable "),a("code",[t._v("blockchain")]),t._v(" variable, by calling the named constructor "),a("code",[t._v("create")]),t._v(", which takes a "),a("code",[t._v("BlockchainConfig")]),t._v(" object.\nThe bitcoin node specified is an Electrum node and we are specifying the url for Blockstream's public Electrum Testnet servers over SSL.")]),t._v(" "),a("p",[t._v("After creating the "),a("code",[t._v("blockchainInit()")]),t._v(" method, call it from "),a("code",[t._v("createOrRestoreWallet()")]),t._v(", so the "),a("code",[t._v("blockchain")]),t._v(" variable gets initialized before the "),a("code",[t._v("wallet")]),t._v(" is created.")]),t._v(" "),a("p",[t._v("Include the following line of code inside "),a("code",[t._v("createOrRestoreWallet()")]),t._v(" just before calling Wallet.create().")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("blockchainInit")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Wallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n")])])]),a("p",[a("strong",[t._v("blockChainConfig")]),t._v(": BlockchainConfig is an enum that has 3 values, "),a("code",[t._v("BlockchainConfig.electrum")]),t._v(" for "),a("a",{attrs:{href:"https://github.com/romanz/electrs",target:"_blank",rel:"noopener noreferrer"}},[a("code",[t._v("electrum")]),a("OutboundLink")],1),t._v(" ,"),a("code",[t._v("BlockchainConfig.esplora")]),t._v(" for "),a("a",{attrs:{href:"https://github.com/Blockstream/esplora",target:"_blank",rel:"noopener noreferrer"}},[a("code",[t._v("esplora")]),a("OutboundLink")],1),t._v(" and "),a("code",[t._v("BlockchainConfig.rpc")]),t._v(" .")]),t._v(" "),a("p",[a("code",[t._v("BlockchainConfig.electrum")]),t._v(", "),a("code",[t._v("BlockchainConfig.rpc")]),t._v(" & "),a("code",[t._v("BlockchainConfig.esplora")]),t._v(" has "),a("code",[t._v("ElectrumConfig")]),t._v(" object, "),a("code",[t._v("RpcConfig")]),t._v(" object and "),a("code",[t._v("EsploraConfig")]),t._v(" object, respectively as its parameter.")]),t._v(" "),a("p",[a("strong",[t._v("ElectrumConfig")]),t._v(": This is the object type of "),a("code",[t._v("BlockchainConfig.electrum")]),t._v("'s config that takes a timeout, retry & url as its required parameter.")]),t._v(" "),a("p",[a("strong",[t._v("EsploraConfig")]),t._v(": This is the object type of "),a("code",[t._v("BlockchainConfig.esplora")]),t._v("'s config that takes baseUrl & stopGap as its required parameter.")]),t._v(" "),a("p",[a("strong",[t._v("RpcConfig")]),t._v(": This is the object type of "),a("code",[t._v("BlockchainConfig.rpc")]),t._v("'s config that takes url, network, & walletName as its required parameter. If "),a("code",[t._v("Rpc Blockchain")]),t._v(" has its authentication values inside a cookie file, please pass in cookie path as authCookie parameter, or you can pass in rpc username and password using "),a("code",[t._v("UserPass")]),t._v(" class.")]),t._v(" "),a("p",[t._v("Refer to the readme for a complete list of options for "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter#createwallet",target:"_blank",rel:"noopener noreferrer"}},[t._v("createWallet()"),a("OutboundLink")],1)]),t._v(" "),a("h2",{attrs:{id:"utxos-and-balance"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#utxos-and-balance"}},[t._v("#")]),t._v(" UTXOs and balance")]),t._v(" "),a("p",[t._v("With the "),a("code",[t._v("Wallet")]),t._v(" and "),a("code",[t._v("Blockchain")]),t._v(" created, we can now add methods to sync UTXOs and compute balance.")]),t._v(" "),a("p",[a("code",[t._v("Wallet")]),t._v(" has a "),a("code",[t._v("sync")]),t._v(" method to sync all UTXOs belonging to the wallet using the "),a("code",[t._v("Blockchain")]),t._v(" object. Once the wallet sync is complete balance is computed and "),a("code",[t._v("getBalance")]),t._v(" can fetch the balance.")]),t._v(" "),a("p",[t._v("Earlier we have already added a variable for "),a("code",[t._v("balance")]),t._v(". Now we will add buttons to call "),a("code",[t._v("sync")]),t._v(" and "),a("code",[t._v("getBalance")]),t._v(". Just below the Create Wallet button let's add two buttons as follows:")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Sync Wallet"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("syncWallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Get Balance"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),a("p",[t._v("Let's add two internal functions for syncing UTXOs and compute balance.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" balanceObj "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Total Balance: ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("balanceObj"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("total"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n balance "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" balanceObj"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("total"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("syncWallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("sync")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("blockchain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("We should now be able to create a wallet, sync UTXOs, and get the balance")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(354)}}),t._v(" "),a("p",[t._v("We can use a public testnet faucet to send testnet coins to the wallet and check that the UTXO sync and balance fetch are working correctly. Before we do that add one more method to generate a new address we can then use this address to get testnet coins from a faucet.")]),t._v(" "),a("p",[t._v("Let's use the "),a("code",[t._v("address")]),t._v(" variable that was created before for this, we need to add a button for "),a("strong",[t._v("Get Address")]),t._v(" and an internal function to call "),a("code",[t._v("Wallet")]),t._v(" and create a new address. Let's do the following")]),t._v(" "),a("p",[t._v("Add a new "),a("code",[t._v("getNewAddress")]),t._v(" function below the "),a("code",[t._v("syncWallet()")]),t._v(" function:")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getNewAddress")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getAddress")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("addressIndex"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AddressIndex")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("And a "),a("strong",[t._v("Get Address")]),t._v(" button below the existing "),a("strong",[t._v("Get Balance")]),t._v(" button:")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getNewAddress")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Get Address"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),a("p",[t._v("We should now have the following, and "),a("strong",[t._v("Get Address")]),t._v(" will be able to display a new address.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0px auto",zoom:"25%"},attrs:{src:s(355)}}),t._v(" "),a("p",[t._v("Now that we are able to generate a receive address we can get some testnet bitcoin from one of the public "),a("a",{attrs:{href:"https://coinfaucet.eu/en/btc-testnet/",target:"_blank",rel:"noopener noreferrer"}},[t._v("testnet faucets"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("After we send and after the transaction is confirmed we will need to sync the wallet before we can see the new balance from the received transaction.")]),t._v(" "),a("h2",{attrs:{id:"restoring-a-wallet"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#restoring-a-wallet"}},[t._v("#")]),t._v(" Restoring a wallet")]),t._v(" "),a("p",[t._v("The "),a("code",[t._v("create")]),t._v(" method creates a wallet using a "),a("code",[t._v("mnemonic")]),t._v(", to restore we can use the same method, we won't need to call "),a("code",[t._v("generateMnemonic")]),t._v(" as we will already have a "),a("code",[t._v("mnemonic")]),t._v(" to restore with.")]),t._v(" "),a("p",[t._v("This text field below the "),a("code",[t._v("Generate Mnemonic")]),t._v(" button will also display the mnemonic variable if we click Generate Mnemonic' button. The generated mnemonic will show up in the text field. We can overwrite it with our mnemonic and doing so will also overwrite the mnemonic state variable. This way the mnemonic displayed will be the one used to create the wallet.")]),t._v(" "),a("p",[t._v("We can now use our mnemonic and use it to restore a wallet. This will come in handy if we have a wallet with testnet bitcoin as these are hard to come by.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0px auto",zoom:"25%"},attrs:{src:s(356)}}),t._v(" "),a("h2",{attrs:{id:"sending-bitcoin"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#sending-bitcoin"}},[t._v("#")]),t._v(" Sending bitcoin")]),t._v(" "),a("p",[t._v("We are now able to receive bitcoin, now its time to add functionality to send a bitcoin transaction.")]),t._v(" "),a("p",[t._v("For making a successful bitcoin transaction "),a("code",[t._v("bdk-flutter")]),t._v(" utilizes a couple of methods. A new unsigned transaction can be created by using TxBuilder](https://github.com/LtbLightning/bdk-flutter#quicksend).")]),t._v(" "),a("p",[t._v("First, we have to initialize the "),a("code",[t._v("TxBuilder")]),t._v(" object and call the "),a("code",[t._v("addRecipient()")]),t._v(" method.\n"),a("code",[t._v("addRecipient()")]),t._v(" takes a "),a("code",[t._v("Script")]),t._v(" object and the transaction "),a("code",[t._v("amount")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" txBuilder"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addRecipient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("We can create the"),a("code",[t._v("Script")]),t._v(" object by using the "),a("code",[t._v("Address")]),t._v(" class, by specifying the recipient address.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Address")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" addressStr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" script "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("scriptPubKey")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" txBuilder"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addRecipient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("We can create a "),a("code",[t._v("psbt")]),t._v(" object by calling the "),a("code",[t._v("finish()")]),t._v(" method using the response object from "),a("code",[t._v("addRecipient()")]),t._v(" method.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" txBuilder "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TxBuilder")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Address")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" addressStr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" script "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("scriptPubKey")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" psbt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" txBuilder\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addRecipient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("feeRate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("finish")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("This "),a("code",[t._v("psbt")]),t._v(" can be signed later with "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter#signtx",target:"_blank",rel:"noopener noreferrer"}},[t._v("sign()"),a("OutboundLink")],1),t._v(" method from the "),a("code",[t._v("Wallet")]),t._v(" and broadcast using "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter#broadcasttx",target:"_blank",rel:"noopener noreferrer"}},[t._v("broadcast()"),a("OutboundLink")],1),t._v(" from the "),a("code",[t._v("Blockchain")]),t._v(" .")]),t._v(" "),a("p",[t._v("We will need textfield controllers for the recipient address, amount, and for transaction, these can be added below our existing variable for "),a("code",[t._v("mnemonic")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" recipientAddress "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" amount "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Let's make an internal function to send a bitcoin transaction, using "),a("code",[t._v("Wallet")]),t._v(", "),a("code",[t._v("Blockchain")]),t._v(" and "),a("code",[t._v("TxBuilder")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sendTx")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" addressStr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" int amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" txBuilder "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TxBuilder")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Address")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" addressStr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" script "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("scriptPubKey")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" txBuilderResult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" txBuilder\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addRecipient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("feeRate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("finish")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" sbt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sign")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("psbt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" txBuilderResult"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("psbt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" tx "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" sbt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("extractTx")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" blockchain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("broadcast")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("tx"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Successfully broadcast ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("amount")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(" Sats to ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("addressStr")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("on")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Exception")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Error: ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("Add a new section for send transaction functionality. We will need a "),a("code",[t._v("form")]),t._v(", a "),a("code",[t._v("TextFormField")]),t._v(" for the receiver address and a "),a("code",[t._v("TextFormField")]),t._v(" for the amount to send. We will also need a button to call the "),a("code",[t._v("sendTx")]),t._v(" function.")]),t._v(" "),a("p",[t._v("Before submitting the form we need to make sure all the input fields are valid, for that purpose, we need to initialize a "),a("a",{attrs:{href:"https://api.flutter.dev/flutter/widgets/GlobalKey-class.html",target:"_blank",rel:"noopener noreferrer"}},[a("code",[t._v("GlobalKey")]),a("OutboundLink")],1),t._v(". This can be added above our "),a("code",[t._v("Scaffold")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" _formKey "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GlobalKey")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FormState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Let's add the send transaction section and UI components below "),a("code",[t._v("/* Send Transaction */")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StyledContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Form")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _formKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Column")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n mainAxisAlignment"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MainAxisAlignment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n crossAxisAlignment"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CrossAxisAlignment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("center"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n children"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFieldContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFormField")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" recipientAddress"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n validator"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isEmpty"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Please enter your address'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n style"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Theme")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("of")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("textTheme"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bodyText1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n decoration"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("InputDecoration")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n hintText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Enter Address"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFieldContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFormField")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n validator"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isEmpty"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Please enter the amount'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n keyboardType"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextInputType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("number"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n style"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Theme")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("of")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("textTheme"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bodyText1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n decoration"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("InputDecoration")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n hintText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Enter Amount"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Send Bit"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_formKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentState"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("validate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sendTx")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("recipientAddress"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n int"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("parse")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])])]),a("p",[t._v("We should now be able to send a transaction as long as there is sufficient balance.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0px auto",zoom:"25%"},attrs:{src:s(357)}}),t._v(" "),a("h2",{attrs:{id:"conclusion"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#conclusion"}},[t._v("#")]),t._v(" Conclusion")]),t._v(" "),a("p",[t._v("The App we created can be built and distributed for both iOS and Android thus sharing a code base and reducing development time. The development and coding focused on application-level code for use cases and we did not have to code intricate internal bitcoin protocol-level code or bitcoin node interactions, and transactions. UTXOs and sync-related functionalities were also not required. All this was managed by "),a("code",[t._v("bdk-flutter")]),t._v(" allowing us to focus on the product, functionality, and user journey. This is how "),a("code",[t._v("bdk")]),t._v(" and "),a("code",[t._v("bdk-flutter")]),t._v(" intend to make Rapid Bitcoin Application Development possible by allowing product and application developers to focus on what they know best while "),a("code",[t._v("bdk")]),t._v(" handles bitcoin complexity.")]),t._v(" "),a("p",[a("code",[t._v("bdk-flutter")]),t._v(" intends to expose functionality and APIs from "),a("code",[t._v("bdk")]),t._v(" which has a wide variety of APIs with granular details allowing for many interesting use cases to be implemented. "),a("code",[t._v("bdk-flutter")]),t._v(" and "),a("code",[t._v("bdk")]),t._v(" are constantly updated and enhanced based on feedback from product teams and developers in the bitcoin community.")]),t._v(" "),a("p",[t._v("Stay tuned for more APIs and enhancements coming to "),a("code",[t._v("bdk-flutter")]),t._v(" in the near future. Feature and API requests are most welcome. New blogs and tutorials will be published soon for a more in-depth exploration of "),a("code",[t._v("bdk-flutter")]),t._v(".")]),t._v(" "),a("p",[t._v("In the meantime keep in touch with the project by following us on "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("GitHub"),a("OutboundLink")],1),t._v(" and "),a("a",{attrs:{href:"https://twitter.com/BitcoinZavior",target:"_blank",rel:"noopener noreferrer"}},[t._v("Twitter"),a("OutboundLink")],1)]),t._v(" "),a("h4",{attrs:{id:"references"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#references"}},[t._v("#")]),t._v(" References:")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/bitcoindevkit",target:"_blank",rel:"noopener noreferrer"}},[t._v("bdk"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("bdk-flutter"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart",target:"_blank",rel:"noopener noreferrer"}},[t._v("bdk-flutter-quickstart GitHub Repository"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://docs.flutter.dev/get-started/install",target:"_blank",rel:"noopener noreferrer"}},[t._v("Setup Flutter Development Environment"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch04.asciidoc",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mastering Bitcoin(HD Wallet chapter)"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("Bitcoin Output Descriptors from bitcoin GitHub"),a("OutboundLink")],1)]),t._v(" "),a("li",[t._v("Testnet Faucet: "),a("a",{attrs:{href:"https://coinfaucet.eu/en/btc-testnet/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://coinfaucet.eu/en/btc-testnet/"),a("OutboundLink")],1),t._v(" or "),a("a",{attrs:{href:"https://bitcoinfaucet.uo1.net",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://bitcoinfaucet.uo1.net"),a("OutboundLink")],1)])])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[19],{347:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_complete_app.c74da859.png"},348:function(t,a,s){t.exports=s.p+"assets/img/default_flutter_app.31b31721.png"},349:function(t,a,s){t.exports=s.p+"assets/img/assets_section.b43d75e9.png"},350:function(t,a,s){t.exports=s.p+"assets/img/folder_structure.b2750bd6.png"},351:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_title.e4e3484a.png"},352:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_tutorial_screen_mnemonic.df703b77.png"},353:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_tutorial_screen_createwallet.3b052736.png"},354:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_get_balance.bfdf9ced.png"},355:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_get_address.5db2e3cc.png"},356:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_get_restore.db8e7e55.png"},357:function(t,a,s){t.exports=s.p+"assets/img/bdk_flutter_send.1688372b.png"},394:function(t,a,s){"use strict";s.r(a);var n=s(7),e=Object(n.a)({},(function(){var t=this,a=t._self._c;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h2",{attrs:{id:"introduction"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#introduction"}},[t._v("#")]),t._v(" Introduction")]),t._v(" "),a("p",[a("code",[t._v("bdk-flutter")]),t._v(" is the "),a("strong",[t._v("Bitcoin Dev kit")]),t._v("'s "),a("strong",[t._v("Flutter")]),t._v(" library which enables building bitcoin applications for Android and iOS mobile platforms. Using "),a("code",[t._v("bdk-flutter")]),t._v(" is similar to using any other Flutter module. Just do "),a("code",[t._v("flutter pub add bdk_flutter")]),t._v(" and you are ready to code! This is the first tutorial on how to use "),a("code",[t._v("bdk-flutter")]),t._v(", more coming soon, make sure to "),a("a",{attrs:{href:"https://twitter.com/BitcoinZavior",target:"_blank",rel:"noopener noreferrer"}},[t._v("follow"),a("OutboundLink")],1),t._v(" to be notified of new ones. There will also be a "),a("strong",[a("code",[t._v("bdk-flutter")])]),t._v(" focused Livestream on "),a("a",{attrs:{href:"https://www.twitch.tv/bitcoindevelopers",target:"_blank",rel:"noopener noreferrer"}},[t._v("Twitch"),a("OutboundLink")],1),t._v(" on the Bitcoin Developers "),a("a",{attrs:{href:"https://www.youtube.com/channel/UCUq_ZdezVWKPvkWRicAYxLA/videos",target:"_blank",rel:"noopener noreferrer"}},[t._v("YouTube Channel"),a("OutboundLink")],1),t._v(" so make sure to subscribe.")]),t._v(" "),a("p",[t._v("This tutorial will explore "),a("code",[t._v("bdk-flutter")]),t._v(" usage and the API it provides. This guide will walk through the development process and code for making a bitcoin application. The bitcoin application we create will be a non-custodial HD Wallet. The application will have the functionality to create a new wallet or restore from a known mnemonic seed phrase. This application will also be able to interact with the bitcoin network to sync UTXOs from new blocks and broadcast transactions.")]),t._v(" "),a("p",[t._v("The tutorial will focus on bitcoin concepts and "),a("code",[t._v("bdk-flutter")]),t._v(" API. So it will gloss over Flutter and Dart. If you are interested in learning more about Flutter and Dart please refer to the Flutter "),a("a",{attrs:{href:"https://flutter.dev/learn",target:"_blank",rel:"noopener noreferrer"}},[t._v("learning portal"),a("OutboundLink")],1),t._v(". The code for this tutorial is available on the "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart",target:"_blank",rel:"noopener noreferrer"}},[t._v("LtbLightning GitHub"),a("OutboundLink")],1)]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(347)}}),t._v(" "),a("h3",{attrs:{id:"prerequisites"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#prerequisites"}},[t._v("#")]),t._v(" Prerequisites")]),t._v(" "),a("p",[t._v("To use "),a("code",[t._v("bdk-flutter")]),t._v(" in a Flutter App, a Flutter development environment is required. Please refer to resources out there on the internet if you need to set this up, here is one of many good resources to guide you on "),a("a",{attrs:{href:"https://docs.flutter.dev/get-started/install",target:"_blank",rel:"noopener noreferrer"}},[t._v("environment setup"),a("OutboundLink")],1)]),t._v(" "),a("h3",{attrs:{id:"bitcoin-basics"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#bitcoin-basics"}},[t._v("#")]),t._v(" Bitcoin Basics")]),t._v(" "),a("p",[t._v("The bitcoin concepts used in this blog post are detailed and explained very well in external bitcoin resources. Here are some links for reference:")]),t._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch04.asciidoc",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mastering Bitcoin(HD Wallet chapter)"),a("OutboundLink")],1)]),t._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("Bitcoin Output Descriptors from bitcoin GitHub"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("Now let's jump into Bitcoin Dev Kit")]),t._v(" "),a("h2",{attrs:{id:"bitcoin-dev-kit-and-bdk-flutter"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#bitcoin-dev-kit-and-bdk-flutter"}},[t._v("#")]),t._v(" Bitcoin Dev Kit and bdk-flutter")]),t._v(" "),a("p",[a("code",[t._v("bdk-flutter")]),t._v(" is "),a("strong",[t._v("Bitcoin Dev kit")]),t._v("'s "),a("strong",[t._v("Flutter")]),t._v(" library for building Bitcoin apps in "),a("strong",[t._v("Flutter")]),t._v(".\nIt encapsulates all of the low-level APIs and methods for BDK and exposes them in a Flutter context. To use BDK in Flutter apps only the "),a("code",[t._v("bdk-flutter")]),t._v(" module is required. "),a("code",[t._v("bdk-flutter")]),t._v(" can be used like any other Flutter library and is available on "),a("a",{attrs:{href:"https://pub.dev/packages/bdk_flutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("pub.dev"),a("OutboundLink")],1)]),t._v(" "),a("h2",{attrs:{id:"getting-started"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#getting-started"}},[t._v("#")]),t._v(" Getting Started")]),t._v(" "),a("p",[t._v("Although we won't delve deep into Flutter we will focus more on bitcoin and "),a("code",[t._v("bdk-flutter")]),t._v(", however, some rudimentary Flutter setup is required, especially a basic Flutter app to add our code.")]),t._v(" "),a("p",[t._v("start by creating a new Flutter project.")]),t._v(" "),a("p",[a("code",[t._v("flutter create bdk-flutter-quickstart")])]),t._v(" "),a("p",[t._v("Once done let's "),a("code",[t._v("cd")]),t._v(" into the new project directory and run the basic Flutter app that's created")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" bdk-flutter-quickstart\nflutter run\n")])])]),a("p",[t._v("This should start building the app and then launch the app in a simulator. So far we have created a basic Flutter project if this doesn't work then refer to the Flutter development setup guide to troubleshoot.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(348),alt:"BDK Flutter Quick Start"}}),t._v(" "),a("h2",{attrs:{id:"setting-up-flutter-app-structure"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#setting-up-flutter-app-structure"}},[t._v("#")]),t._v(" Setting up Flutter app structure")]),t._v(" "),a("p",[t._v("Let's set up a very basic app structure. Let's create an "),a("code",[t._v("assets")]),t._v(" folder in the project root and then add new folders "),a("code",[t._v("widgets")]),t._v(", "),a("code",[t._v("screens")]),t._v(", and "),a("code",[t._v("styles")]),t._v(" inside the existing "),a("code",[t._v("lib")]),t._v(" folder.")]),t._v(" "),a("p",[t._v("Paste the following code in your "),a("code",[t._v("pubspec.yaml")]),t._v(" file, assets section.")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",[a("code",[t._v("- assets/\n")])])]),a("p",[t._v("Please make sure your assets section looks like the screenshot below.\n"),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"60%"},attrs:{src:s(349),alt:"BDK Flutter Quick Start"}})]),t._v(" "),a("p",[t._v("Once done let's run a "),a("code",[t._v("get")]),t._v(" command from the pub tool commands, this will get all the required dependencies for our project.")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("flutter pub get\n")])])]),a("p",[t._v("To make this quick you can download the theme, styled widgets and images used in the tutorial from the repository. The "),a("code",[t._v("theme.dart")]),t._v(" file has the theme we will use and this can be taken from "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart/blob/master/lib/styles/theme.dart",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(" and moved to the styles folder. The "),a("code",[t._v("widgets.dart")]),t._v(" file has the styled widgets we will use and these can be taken from "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart/blob/master/lib/widgets/widgets.dart",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(" and moved to the widgets folder. The image assets can be taken from "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart/tree/master/assets",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(" Alternatively, you can write your theme, widgets and use your images if you intend to style the app differently.")]),t._v(" "),a("p",[t._v("In addition to the the theme, widgets and assets. We also need to create a "),a("code",[t._v("screens")]),t._v(" folder and create a "),a("code",[t._v("home.dart")]),t._v(" file inside it, this will be where most of the code will be added.")]),t._v(" "),a("p",[t._v("Once done the file structure should look like this:")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0px auto",zoom:"60%"},attrs:{src:s(350)}}),t._v(" "),a("p",[a("br"),t._v("Locate "),a("code",[t._v("main.dart")]),t._v(" in the project root, this will have the default code added by "),a("code",[t._v("flutter create")]),t._v(", let's delete all contents of "),a("code",[t._v("main.dart")]),t._v(" and replace it with the following code to use "),a("code",[t._v("home.dart")]),t._v(" as our main screen. This will probably crash the app but that's fine, it will be up and running once we add code to "),a("code",[t._v("home.dart")]),t._v(" in the next few steps")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// main.dart")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter_quickstart/screens/home.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter_quickstart/styles/theme.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:flutter/material.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("runApp")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StatelessWidget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MyApp")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Key")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("super")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// This widget is the root of your application.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MaterialApp")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n debugShowCheckedModeBanner"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n title"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'BDK-FLUTTER TUTORIAL'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n theme"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("theme")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n home"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"installing-bdk-flutter"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#installing-bdk-flutter"}},[t._v("#")]),t._v(" Installing "),a("code",[t._v("bdk-flutter")])]),t._v(" "),a("p",[t._v("With the Flutter project in place, we can now add "),a("code",[t._v("bdk-flutter")]),t._v(" using "),a("code",[t._v("flutter pub add")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("flutter pub "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),t._v(" bdk_flutter\n")])])]),a("p",[t._v("This will add a line like this to your package's "),a("code",[t._v("pubspec.yaml")]),t._v(" and this will also run an implicit flutter pub get to download "),a("code",[t._v("bdk-flutter")]),t._v(" from "),a("code",[t._v("pub.dev")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[t._v("dependencies:\n bdk_flutter: ^0.28.2\n")])])]),a("h2",{attrs:{id:"configuring"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#configuring"}},[t._v("#")]),t._v(" Configuring")]),t._v(" "),a("p",[t._v("Make sure your app meets the following requirements for using "),a("code",[t._v("bdk-flutter")])]),t._v(" "),a("p",[a("strong",[t._v("Android")])]),t._v(" "),a("p",[t._v("MinSdkVersion : API 23 or higher.")]),t._v(" "),a("p",[a("strong",[t._v("IOS")])]),t._v(" "),a("p",[t._v("Deployment target: iOS 12.0 or greater.")]),t._v(" "),a("p",[t._v("Locate your Podfile in the ios folder of your project and paste the following code at the beginning")]),t._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[t._v("platform :ios, '12.0'\n")])])]),a("p",[t._v("After changing the deployment target in your project's "),a("code",[t._v("PodFile")]),t._v(", let's use the following "),a("code",[t._v("command")]),t._v(" to install pod dependencies for iOS.")]),t._v(" "),a("div",{staticClass:"language-shell extra-class"},[a("pre",{pre:!0,attrs:{class:"language-shell"}},[a("code",[a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" ios "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" pod "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),t._v("\n")])])]),a("p",[t._v("Once done, bdk-flutter is installed and configured and ready to be used in our "),a("strong",[t._v("bdk-flutter-quickstart")]),t._v(" App.")]),t._v(" "),a("h2",{attrs:{id:"importing-bdk-flutter"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#importing-bdk-flutter"}},[t._v("#")]),t._v(" Importing "),a("code",[t._v("bdk-flutter")])]),t._v(" "),a("p",[t._v("Locate "),a("code",[t._v("home.dart")]),t._v(" which we added in the setup section and import "),a("code",[t._v("bdk-flutter")]),t._v(" at the top of the file. Create a stateful widget called "),a("code",[t._v("Home")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// screens/home.dart")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter/bdk_flutter.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StatefulWidget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Key")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("super")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_HomeState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" _HomeState "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" mnemonic "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Container")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Before we start using "),a("code",[t._v("bdk-flutter")]),t._v(" let's add some additional imports and also import styles, to create a basic layout to build our home screen")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// screens/home.dart")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter/bdk_flutter.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter_quickstart/widgets/widgets.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:flutter/cupertino.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:flutter/material.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StatefulWidget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Key")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("super")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_HomeState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" _HomeState "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Scaffold")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n resizeToAvoidBottomInset"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n backgroundColor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Colors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("white"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* AppBar */")]),t._v("\n appBar"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("buildAppBar")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n body"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SingleChildScrollView")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Container")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n padding"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EdgeInsets")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("symmetric")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("horizontal"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("30")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Column")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n children"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Balance */")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Create Wallet */")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Send Transaction */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("We now have an app title section and a structure to hold the rest of our app components.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"33%"},attrs:{src:s(351)}}),t._v(" "),a("h2",{attrs:{id:"calling-bdk-flutter-methods"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#calling-bdk-flutter-methods"}},[t._v("#")]),t._v(" Calling bdk-flutter methods")]),t._v(" "),a("p",[t._v("To call all methods properly from the "),a("code",[t._v("bdk-flutter")]),t._v(" package, first, we need to create state variables to store "),a("code",[t._v("Wallet")]),t._v(" and "),a("code",[t._v("Blockchain")]),t._v(" objects.")]),t._v(" "),a("p",[t._v("Here we use the late keyword to declare both "),a("code",[t._v("Wallet")]),t._v(" and "),a("code",[t._v("Blockchain")]),t._v(". These are non-nullable variables that are initialized after the declaration.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'package:bdk_flutter/bdk_flutter.dart'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nlate "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Wallet")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nlate "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Blockchain")]),t._v(" blockchain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("The first step in creating a non-custodial bitcoin app is creating a mnemonic seed phrase for the wallet.")]),t._v(" "),a("p",[a("code",[t._v("bdk-flutter")]),t._v(" provides a "),a("code",[t._v("Mnemonic")]),t._v(" class to create a "),a("code",[t._v("Mnemonic")]),t._v(". The "),a("code",[t._v("create")]),t._v(" method is a named constructor and can be used to create a mnemonic, it takes "),a("code",[t._v("WordCount")]),t._v(" as its required parameter.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WordCount.Words12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("We can generate a mnemonic of longer length by passing in a wordCount argument of required length.")]),t._v(" "),a("p",[t._v("To create a mnemonic with a "),a("code",[t._v("WordCount")]),t._v(" of 18 words, we can use "),a("code",[t._v("(WordCount.Words18)")]),t._v("\nRefer to the API docs on "),a("a",{attrs:{href:"https://pub.dev/documentation/bdk_flutter/latest/bdk_flutter/bdk_flutter-library.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("pub.dev"),a("OutboundLink")],1),t._v(" for more details.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WordCount.Words18")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// here response is saved as a 'Mnemonic' object")]),t._v("\n")])])]),a("p",[t._v("In order to use this in our Flutter app, we want a button that will generate a mnemonic when clicked, and a text input box to show the generated mnemonic. Let's first create a "),a("code",[t._v("TextEditingController")]),t._v(" for the "),a("code",[t._v("mnemonic")]),t._v(" textfield to store the mnemonic, and an internal "),a("code",[t._v("generateMnemonicHandler")]),t._v(" method which can be called on button click. We will also need a button that will call the internal "),a("code",[t._v("generateMnemonicHandler")]),t._v(" method when clicked. Adding the following code achieves all of this.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StatefulWidget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Key")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("super")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("_HomeState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" _HomeState "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n late "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Wallet")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n late "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Blockchain")]),t._v(" blockchain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" mnemonic "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateMnemonicHandler")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WordCount.Words12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("asString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token metadata function"}},[t._v("@override")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Widget")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BuildContext")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Scaffold")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n resizeToAvoidBottomInset"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n backgroundColor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Colors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("white"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Header */")]),t._v("\n appBar"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("buildAppBar")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n body"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SingleChildScrollView")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Container")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n padding"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("EdgeInsets")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("symmetric")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("horizontal"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("30")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Column")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n children"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Balance */")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Result */")]),t._v("\n\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Create Wallet */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StyledContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Column")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n mainAxisAlignment"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MainAxisAlignment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n crossAxisAlignment"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CrossAxisAlignment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("center"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n children"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Generate Mnemonic"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateMnemonicHandler")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFieldContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFormField")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n style"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Theme")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("of")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("textTheme"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bodyText1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n keyboardType"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextInputType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("multiline"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n maxLines"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n decoration"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("InputDecoration")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n hintText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Enter your mnemonic"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Send Transaction Buttons */")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Now we need to add a component to display the output of our method calls and this will also need a "),a("code",[t._v("displayText")]),t._v(" variable to track our method call response. To achieve this add the following code.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// screens/home.dart")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// add this as another state variable under mnemonic")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" displayText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// modify the generateMnemonicHandler method to also set mnemonic as displayText")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateMnemonicHandler")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("WordCount.Words12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("asString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("asString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("and finally, let's add the component to display the output under "),a("code",[t._v("/* Result */")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// screens/home.dart")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Result */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// display the component only if displayText has a value")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ResponseContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"No Response"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),a("p",[t._v('We should now have a working "Generate Mnemonic" button that displays the new mnemonic')]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(352)}}),t._v(" "),a("p",[t._v("A quick recap, we added a button to call a click handler ("),a("code",[t._v("generateMnemonicHandler")]),t._v(") which calls "),a("code",[t._v("generateMnemonic")]),t._v(" API of "),a("code",[t._v("bdk-flutter")]),t._v(". The click handler also sets the state for the app and also updates the "),a("code",[t._v("displayText")]),t._v(" variable to display the output of the call in the display section. We will follow this pattern for the remaining calls to "),a("code",[t._v("bdk-flutter")]),t._v(".")]),t._v(" "),a("h2",{attrs:{id:"creating-a-wallet"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#creating-a-wallet"}},[t._v("#")]),t._v(" Creating a wallet")]),t._v(" "),a("p",[t._v("Before moving on to creating a wallet, let's add a section at the top to display the balance of the wallet.")]),t._v(" "),a("p",[t._v("To display the balance we will need a state variable to store the balance and a display component to display it. We will also be creating a receive address for the wallet so a state variable will be required for the address as well.")]),t._v(" "),a("p",[t._v("Under the "),a("code",[t._v("mnemonic")]),t._v(" and "),a("code",[t._v("displayText")]),t._v(" state variables, let's add one for "),a("code",[t._v("balance")]),t._v(" and one for "),a("code",[t._v("address")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" _HomeState "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("State")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Home")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" mnemonic "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" displayText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" balance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Just below "),a("code",[t._v("/* Balance */")]),t._v(" and above "),a("code",[t._v("/* Result */")]),t._v(" add the following UI components to display the balance. We only want to show the balance when it has a value so we will use a null-aware operator "),a("code",[t._v("??")]),t._v(" for a quick "),a("code",[t._v("null")]),t._v(" check and use "),a("code",[t._v("0")]),t._v(" in case of a "),a("code",[t._v("null")]),t._v(" value.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Balance */")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BalanceContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"${balance ?? "')])]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"} Sats"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* Result */")]),t._v("\n")])])]),a("p",[a("code",[t._v("bdk_flutter")]),t._v(" creates a wallet using output descriptors which define the derivation path to derive addresses and sign transactions. More about output descriptors "),a("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(". Before creating the "),a("code",[t._v("Wallet")]),t._v(" we need to create a "),a("code",[t._v("descriptor")]),t._v(" object which will be used to generate receive addresses and a "),a("code",[t._v("changeDescriptor")]),t._v(" object to to create change addresses to collect from outgoing transactions.")]),t._v(" "),a("p",[a("code",[t._v("bdk_flutter")]),t._v("'s "),a("code",[t._v("Descriptor")]),t._v(" class has a number of descriptor templates that will help you create a simple wallet.")]),t._v(" "),a("p",[t._v("Let's add some code to create a simple "),a("code",[t._v("wpkh")]),t._v(" descriptor object by using the "),a("code",[t._v("BIP84")]),t._v(" template. This template will create a descriptor in the format "),a("code",[t._v("wpkh(key/84'/{0,1}'/0'/{0,1}/*)")])]),t._v(" "),a("p",[t._v("This descriptor will create receive ("),a("code",[t._v("KeyChainKind.External")]),t._v(") and change descriptor ("),a("code",[t._v("KeyChainKind.Internal")]),t._v(") for a specified mnemonic.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("List")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Descriptor")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getDescriptors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" descriptors "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Descriptor")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" e "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("KeychainKind.External")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("KeychainKind.Internal")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" mnemonicObj "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Mnemonic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("fromString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" descriptorSecretKey "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DescriptorSecretKey")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Network.Testnet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" mnemonicObj"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" descriptor "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Descriptor")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("newBip84")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n secretKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" descriptorSecretKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Network.Testnet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n keychain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n descriptors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("descriptor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" descriptors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("on")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Exception")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Error : ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("rethrow")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("Under the "),a("code",[t._v("address")]),t._v(" state variable, let's add a state variable called "),a("code",[t._v("wallet")]),t._v(" of the type "),a("code",[t._v("Wallet")]),t._v(" for saving the bitcoin wallet.")]),t._v(" "),a("p",[t._v("To create a wallet with "),a("code",[t._v("bdk-flutter")]),t._v(" call the "),a("code",[t._v("create")]),t._v(" constructor with "),a("code",[t._v("descriptor")]),t._v(", "),a("code",[t._v("changeDescriptor")]),t._v(" "),a("code",[t._v("network")]),t._v(", and the "),a("code",[t._v("databaseConfig")]),t._v(". For database, we can use memory as the database by specifying "),a("code",[t._v("DatabaseConfig.memory()")]),t._v("\nFollowing our pattern of a button, click handler and bdk-flutter API call, Let's add an internal method which will serve as the click handler for the \"Create Wallet\" button. We want to see the output of this call so let's use "),a("code",[t._v("setState()")]),t._v(" to set the "),a("code",[t._v("wallet")]),t._v(" object created and the "),a("code",[t._v("displayText")]),t._v(" variable with the wallet's first receive address.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createOrRestoreWallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Network")]),t._v(" network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" password"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" descriptors "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getDescriptors")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Wallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n descriptor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" descriptors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n changeDescriptor"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" descriptors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" network"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n databaseConfig"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DatabaseConfig")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("memory")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" addressInfo "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getAddress")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("addressIndex"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AddressIndex")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" addressInfo"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n wallet "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Wallet Created: ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("address")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("on")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Exception")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Error: ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("A new button will be required to call "),a("code",[t._v("createOrRestoreWallet()")])]),t._v(" "),a("p",[t._v("Let's add a new button just below the mnemonic "),a("code",[t._v("TextFieldContainer")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Create Wallet"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createOrRestoreWallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n mnemonic"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" \n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Network.Testnet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"password"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),a("p",[t._v("The response returned by "),a("code",[t._v("create()")]),t._v(" is a "),a("code",[t._v("Wallet")]),t._v(" object.")]),t._v(" "),a("p",[t._v("The App should now be creating a wallet when we click "),a("strong",[t._v("Create Mnemonic")]),t._v(" followed by "),a("strong",[t._v("Create Wallet")]),t._v(".")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(353)}}),t._v(" "),a("p",[t._v("Before going forward, we need to create a "),a("code",[t._v("Blockchain")]),t._v(" object as well. The Blockchain object will encapsulate the bitcoin node configuration which the wallet will use for syncing blocks and broadcasting transactions.")]),t._v(" "),a("p",[t._v("Let's add an internal method to create and initialize the "),a("code",[t._v("Blockchain")]),t._v(" object.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("blockchainInit")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n blockchain "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Blockchain")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("BlockchainConfig")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("electrum")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ElectrumConfig")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n stopGap"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n timeout"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n retry"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n url"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ssl://electrum.blockstream.info:60002"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n validateDomain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Here we are initializing the "),a("code",[t._v("late")]),t._v(" non-nullable "),a("code",[t._v("blockchain")]),t._v(" variable, by calling the named constructor "),a("code",[t._v("create")]),t._v(", which takes a "),a("code",[t._v("BlockchainConfig")]),t._v(" object.\nThe bitcoin node specified is an Electrum node and we are specifying the url for Blockstream's public Electrum Testnet servers over SSL.")]),t._v(" "),a("p",[t._v("After creating the "),a("code",[t._v("blockchainInit()")]),t._v(" method, call it from "),a("code",[t._v("createOrRestoreWallet()")]),t._v(", so the "),a("code",[t._v("blockchain")]),t._v(" variable gets initialized before the "),a("code",[t._v("wallet")]),t._v(" is created.")]),t._v(" "),a("p",[t._v("Include the following line of code inside "),a("code",[t._v("createOrRestoreWallet()")]),t._v(" just before calling Wallet.create().")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("blockchainInit")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Wallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n")])])]),a("p",[a("strong",[t._v("blockChainConfig")]),t._v(": BlockchainConfig is an enum that has 3 values, "),a("code",[t._v("BlockchainConfig.electrum")]),t._v(" for "),a("a",{attrs:{href:"https://github.com/romanz/electrs",target:"_blank",rel:"noopener noreferrer"}},[a("code",[t._v("electrum")]),a("OutboundLink")],1),t._v(" ,"),a("code",[t._v("BlockchainConfig.esplora")]),t._v(" for "),a("a",{attrs:{href:"https://github.com/Blockstream/esplora",target:"_blank",rel:"noopener noreferrer"}},[a("code",[t._v("esplora")]),a("OutboundLink")],1),t._v(" and "),a("code",[t._v("BlockchainConfig.rpc")]),t._v(" .")]),t._v(" "),a("p",[a("code",[t._v("BlockchainConfig.electrum")]),t._v(", "),a("code",[t._v("BlockchainConfig.rpc")]),t._v(" & "),a("code",[t._v("BlockchainConfig.esplora")]),t._v(" has "),a("code",[t._v("ElectrumConfig")]),t._v(" object, "),a("code",[t._v("RpcConfig")]),t._v(" object and "),a("code",[t._v("EsploraConfig")]),t._v(" object, respectively as its parameter.")]),t._v(" "),a("p",[a("strong",[t._v("ElectrumConfig")]),t._v(": This is the object type of "),a("code",[t._v("BlockchainConfig.electrum")]),t._v("'s config that takes a timeout, retry & url as its required parameter.")]),t._v(" "),a("p",[a("strong",[t._v("EsploraConfig")]),t._v(": This is the object type of "),a("code",[t._v("BlockchainConfig.esplora")]),t._v("'s config that takes baseUrl & stopGap as its required parameter.")]),t._v(" "),a("p",[a("strong",[t._v("RpcConfig")]),t._v(": This is the object type of "),a("code",[t._v("BlockchainConfig.rpc")]),t._v("'s config that takes url, network, & walletName as its required parameter. If "),a("code",[t._v("Rpc Blockchain")]),t._v(" has its authentication values inside a cookie file, please pass in cookie path as authCookie parameter, or you can pass in rpc username and password using "),a("code",[t._v("UserPass")]),t._v(" class.")]),t._v(" "),a("p",[t._v("Refer to the readme for a complete list of options for "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter#createwallet",target:"_blank",rel:"noopener noreferrer"}},[t._v("createWallet()"),a("OutboundLink")],1)]),t._v(" "),a("h2",{attrs:{id:"utxos-and-balance"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#utxos-and-balance"}},[t._v("#")]),t._v(" UTXOs and balance")]),t._v(" "),a("p",[t._v("With the "),a("code",[t._v("Wallet")]),t._v(" and "),a("code",[t._v("Blockchain")]),t._v(" created, we can now add methods to sync UTXOs and compute balance.")]),t._v(" "),a("p",[a("code",[t._v("Wallet")]),t._v(" has a "),a("code",[t._v("sync")]),t._v(" method to sync all UTXOs belonging to the wallet using the "),a("code",[t._v("Blockchain")]),t._v(" object. Once the wallet sync is complete balance is computed and "),a("code",[t._v("getBalance")]),t._v(" can fetch the balance.")]),t._v(" "),a("p",[t._v("Earlier we have already added a variable for "),a("code",[t._v("balance")]),t._v(". Now we will add buttons to call "),a("code",[t._v("sync")]),t._v(" and "),a("code",[t._v("getBalance")]),t._v(". Just below the Create Wallet button let's add two buttons as follows:")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Sync Wallet"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("syncWallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Get Balance"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),a("p",[t._v("Let's add two internal functions for syncing UTXOs and compute balance.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" balanceObj "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Total Balance: ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("balanceObj"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("total"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n balance "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" balanceObj"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("total"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("syncWallet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("sync")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("blockchain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("We should now be able to create a wallet, sync UTXOs, and get the balance")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"25%"},attrs:{src:s(354)}}),t._v(" "),a("p",[t._v("We can use a public testnet faucet to send testnet coins to the wallet and check that the UTXO sync and balance fetch are working correctly. Before we do that add one more method to generate a new address we can then use this address to get testnet coins from a faucet.")]),t._v(" "),a("p",[t._v("Let's use the "),a("code",[t._v("address")]),t._v(" variable that was created before for this, we need to add a button for "),a("strong",[t._v("Get Address")]),t._v(" and an internal function to call "),a("code",[t._v("Wallet")]),t._v(" and create a new address. Let's do the following")]),t._v(" "),a("p",[t._v("Add a new "),a("code",[t._v("getNewAddress")]),t._v(" function below the "),a("code",[t._v("syncWallet()")]),t._v(" function:")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getNewAddress")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getAddress")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("addressIndex"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("AddressIndex")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("And a "),a("strong",[t._v("Get Address")]),t._v(" button below the existing "),a("strong",[t._v("Get Balance")]),t._v(" button:")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getNewAddress")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Get Address"')])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n")])])]),a("p",[t._v("We should now have the following, and "),a("strong",[t._v("Get Address")]),t._v(" will be able to display a new address.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0px auto",zoom:"25%"},attrs:{src:s(355)}}),t._v(" "),a("p",[t._v("Now that we are able to generate a receive address we can get some testnet bitcoin from one of the public "),a("a",{attrs:{href:"https://coinfaucet.eu/en/btc-testnet/",target:"_blank",rel:"noopener noreferrer"}},[t._v("testnet faucets"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("After we send and after the transaction is confirmed we will need to sync the wallet before we can see the new balance from the received transaction.")]),t._v(" "),a("h2",{attrs:{id:"restoring-a-wallet"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#restoring-a-wallet"}},[t._v("#")]),t._v(" Restoring a wallet")]),t._v(" "),a("p",[t._v("The "),a("code",[t._v("create")]),t._v(" method creates a wallet using a "),a("code",[t._v("mnemonic")]),t._v(", to restore we can use the same method, we won't need to call "),a("code",[t._v("generateMnemonic")]),t._v(" as we will already have a "),a("code",[t._v("mnemonic")]),t._v(" to restore with.")]),t._v(" "),a("p",[t._v("This text field below the "),a("code",[t._v("Generate Mnemonic")]),t._v(" button will also display the mnemonic variable if we click Generate Mnemonic' button. The generated mnemonic will show up in the text field. We can overwrite it with our mnemonic and doing so will also overwrite the mnemonic state variable. This way the mnemonic displayed will be the one used to create the wallet.")]),t._v(" "),a("p",[t._v("We can now use our mnemonic and use it to restore a wallet. This will come in handy if we have a wallet with testnet bitcoin as these are hard to come by.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0px auto",zoom:"25%"},attrs:{src:s(356)}}),t._v(" "),a("h2",{attrs:{id:"sending-bitcoin"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#sending-bitcoin"}},[t._v("#")]),t._v(" Sending bitcoin")]),t._v(" "),a("p",[t._v("We are now able to receive bitcoin, now its time to add functionality to send a bitcoin transaction.")]),t._v(" "),a("p",[t._v("For making a successful bitcoin transaction "),a("code",[t._v("bdk-flutter")]),t._v(" utilizes a couple of methods. A new unsigned transaction can be created by using TxBuilder](https://github.com/LtbLightning/bdk-flutter#quicksend).")]),t._v(" "),a("p",[t._v("First, we have to initialize the "),a("code",[t._v("TxBuilder")]),t._v(" object and call the "),a("code",[t._v("addRecipient()")]),t._v(" method.\n"),a("code",[t._v("addRecipient()")]),t._v(" takes a "),a("code",[t._v("Script")]),t._v(" object and the transaction "),a("code",[t._v("amount")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" txBuilder"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addRecipient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("We can create the"),a("code",[t._v("Script")]),t._v(" object by using the "),a("code",[t._v("Address")]),t._v(" class, by specifying the recipient address.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Address")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" addressStr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" script "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("scriptPubKey")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" res "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" txBuilder"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addRecipient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("We can create a "),a("code",[t._v("psbt")]),t._v(" object by calling the "),a("code",[t._v("finish()")]),t._v(" method using the response object from "),a("code",[t._v("addRecipient()")]),t._v(" method.")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" txBuilder "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TxBuilder")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Address")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" addressStr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" script "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("scriptPubKey")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" psbt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" txBuilder\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addRecipient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("feeRate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("finish")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("This "),a("code",[t._v("psbt")]),t._v(" can be signed later with "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter#signtx",target:"_blank",rel:"noopener noreferrer"}},[t._v("sign()"),a("OutboundLink")],1),t._v(" method from the "),a("code",[t._v("Wallet")]),t._v(" and broadcast using "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter#broadcasttx",target:"_blank",rel:"noopener noreferrer"}},[t._v("broadcast()"),a("OutboundLink")],1),t._v(" from the "),a("code",[t._v("Blockchain")]),t._v(" .")]),t._v(" "),a("p",[t._v("We will need textfield controllers for the recipient address, amount, and for transaction, these can be added below our existing variable for "),a("code",[t._v("mnemonic")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" recipientAddress "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),t._v(" amount "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextEditingController")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Let's make an internal function to send a bitcoin transaction, using "),a("code",[t._v("Wallet")]),t._v(", "),a("code",[t._v("Blockchain")]),t._v(" and "),a("code",[t._v("TxBuilder")]),t._v(".")]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Future")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sendTx")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("String")]),t._v(" addressStr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" int amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("try")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" txBuilder "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TxBuilder")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Address")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" addressStr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" script "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("scriptPubKey")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" txBuilderResult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" txBuilder\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("addRecipient")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("feeRate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("finish")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" sbt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" wallet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sign")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("psbt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" txBuilderResult"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("psbt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" tx "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" sbt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("extractTx")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" blockchain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("broadcast")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("tx"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Successfully broadcast ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("amount")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(" Sats to ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("$")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("addressStr")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("on")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Exception")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("catch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n displayText "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Error: ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("${")]),a("span",{pre:!0,attrs:{class:"token expression"}},[t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("Add a new section for send transaction functionality. We will need a "),a("code",[t._v("form")]),t._v(", a "),a("code",[t._v("TextFormField")]),t._v(" for the receiver address and a "),a("code",[t._v("TextFormField")]),t._v(" for the amount to send. We will also need a button to call the "),a("code",[t._v("sendTx")]),t._v(" function.")]),t._v(" "),a("p",[t._v("Before submitting the form we need to make sure all the input fields are valid, for that purpose, we need to initialize a "),a("a",{attrs:{href:"https://api.flutter.dev/flutter/widgets/GlobalKey-class.html",target:"_blank",rel:"noopener noreferrer"}},[a("code",[t._v("GlobalKey")]),a("OutboundLink")],1),t._v(". This can be added above our "),a("code",[t._v("Scaffold")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("final")]),t._v(" _formKey "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("GlobalKey")]),a("span",{pre:!0,attrs:{class:"token generics"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("FormState")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Let's add the send transaction section and UI components below "),a("code",[t._v("/* Send Transaction */")])]),t._v(" "),a("div",{staticClass:"language-dart extra-class"},[a("pre",{pre:!0,attrs:{class:"language-dart"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("StyledContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Form")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" _formKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Column")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n mainAxisAlignment"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("MainAxisAlignment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n crossAxisAlignment"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CrossAxisAlignment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("center"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n children"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFieldContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFormField")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" recipientAddress"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n validator"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isEmpty"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Please enter your address'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n style"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Theme")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("of")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("textTheme"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bodyText1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n decoration"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("InputDecoration")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n hintText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Enter Address"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFieldContainer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n child"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextFormField")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n controller"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n validator"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("isEmpty"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Please enter the amount'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n keyboardType"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TextInputType")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("number"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n style"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Theme")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("of")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("textTheme"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bodyText1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n decoration"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("InputDecoration")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n hintText"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Enter Amount"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SubmitButton")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-literal"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Send Bit"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n callback"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_formKey"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("currentState"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("validate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sendTx")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("recipientAddress"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n int"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("parse")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("amount"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])])]),a("p",[t._v("We should now be able to send a transaction as long as there is sufficient balance.")]),t._v(" "),a("img",{staticStyle:{display:"block",margin:"0px auto",zoom:"25%"},attrs:{src:s(357)}}),t._v(" "),a("h2",{attrs:{id:"conclusion"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#conclusion"}},[t._v("#")]),t._v(" Conclusion")]),t._v(" "),a("p",[t._v("The App we created can be built and distributed for both iOS and Android thus sharing a code base and reducing development time. The development and coding focused on application-level code for use cases and we did not have to code intricate internal bitcoin protocol-level code or bitcoin node interactions, and transactions. UTXOs and sync-related functionalities were also not required. All this was managed by "),a("code",[t._v("bdk-flutter")]),t._v(" allowing us to focus on the product, functionality, and user journey. This is how "),a("code",[t._v("bdk")]),t._v(" and "),a("code",[t._v("bdk-flutter")]),t._v(" intend to make Rapid Bitcoin Application Development possible by allowing product and application developers to focus on what they know best while "),a("code",[t._v("bdk")]),t._v(" handles bitcoin complexity.")]),t._v(" "),a("p",[a("code",[t._v("bdk-flutter")]),t._v(" intends to expose functionality and APIs from "),a("code",[t._v("bdk")]),t._v(" which has a wide variety of APIs with granular details allowing for many interesting use cases to be implemented. "),a("code",[t._v("bdk-flutter")]),t._v(" and "),a("code",[t._v("bdk")]),t._v(" are constantly updated and enhanced based on feedback from product teams and developers in the bitcoin community.")]),t._v(" "),a("p",[t._v("Stay tuned for more APIs and enhancements coming to "),a("code",[t._v("bdk-flutter")]),t._v(" in the near future. Feature and API requests are most welcome. New blogs and tutorials will be published soon for a more in-depth exploration of "),a("code",[t._v("bdk-flutter")]),t._v(".")]),t._v(" "),a("p",[t._v("In the meantime keep in touch with the project by following us on "),a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("GitHub"),a("OutboundLink")],1),t._v(" and "),a("a",{attrs:{href:"https://twitter.com/BitcoinZavior",target:"_blank",rel:"noopener noreferrer"}},[t._v("Twitter"),a("OutboundLink")],1)]),t._v(" "),a("h4",{attrs:{id:"references"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#references"}},[t._v("#")]),t._v(" References:")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/bitcoindevkit",target:"_blank",rel:"noopener noreferrer"}},[t._v("bdk"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("bdk-flutter"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/LtbLightning/bdk-flutter-quickstart",target:"_blank",rel:"noopener noreferrer"}},[t._v("bdk-flutter-quickstart GitHub Repository"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://docs.flutter.dev/get-started/install",target:"_blank",rel:"noopener noreferrer"}},[t._v("Setup Flutter Development Environment"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch04.asciidoc",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mastering Bitcoin(HD Wallet chapter)"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("Bitcoin Output Descriptors from bitcoin GitHub"),a("OutboundLink")],1)]),t._v(" "),a("li",[t._v("Testnet Faucet: "),a("a",{attrs:{href:"https://coinfaucet.eu/en/btc-testnet/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://coinfaucet.eu/en/btc-testnet/"),a("OutboundLink")],1),t._v(" or "),a("a",{attrs:{href:"https://bitcoinfaucet.uo1.net",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://bitcoinfaucet.uo1.net"),a("OutboundLink")],1)])])])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/31.dd5b652f.js b/assets/js/31.c8bce6b9.js similarity index 99% rename from assets/js/31.dd5b652f.js rename to assets/js/31.c8bce6b9.js index 57c0528089..542adce355 100644 --- a/assets/js/31.dd5b652f.js +++ b/assets/js/31.c8bce6b9.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[31],{344:function(t,e,a){t.exports=a.p+"assets/img/BDK-RN-Architecture.42fbc351.png"},345:function(t,e,a){t.exports=a.p+"assets/img/BDK-RN.370f20c3.png"},346:function(t,e,a){t.exports=a.p+"assets/img/android_folder.0ff999be.png"},388:function(t,e,a){"use strict";a.r(e);var n=a(7),s=Object(n.a)({},(function(){var t=this,e=t._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("p",[t._v("The "),e("strong",[t._v("BitcoinDevkit")]),t._v("'s "),e("strong",[t._v("React Native")]),t._v(" library ("),e("code",[t._v("bdk-rn")]),t._v(") makes it easy to develop bitcoin applications for both Android and iOS mobile platforms. Using "),e("code",[t._v("bdk-rn")]),t._v(", knowledge of the underlying bitcoin and BDK API is not required and using "),e("code",[t._v("bdk-rn")]),t._v(" is similar to using any other RN module. The goal is "),e("strong",[t._v("Rapid Bitcoin Application Development")]),t._v(" by doing the heavy lifting in advance and providing a reusable library for other developers to use. Developers simply install using "),e("code",[t._v("yarn add")]),t._v(" and start using it in a React Native Project. The native code, Rust lang implementation, configuration and other setup details are all taken care of by "),e("code",[t._v("bdk-rn")]),t._v(".")]),t._v(" "),e("p",[t._v("This article is "),e("strong",[t._v("NOT a guide on how to use bdk-rn")]),t._v(" to build a bitcoin application, rather this is an insight into how "),e("code",[t._v("bdk-rn")]),t._v(" was developed. For help on how to use "),e("code",[t._v("bdk-rn")]),t._v(" to develop a bitcoin wallet or application please refer to the user guide in the "),e("a",{attrs:{href:"https://github.com/LtbLightning/bdk-rn#usage",target:"_blank",rel:"noopener noreferrer"}},[t._v("readme"),e("OutboundLink")],1),t._v(" on Github. There will be "),e("code",[t._v("how to guides")]),t._v(" published shortly on getting started with "),e("code",[t._v("bdk-rn")]),t._v(".")]),t._v(" "),e("h2",{attrs:{id:"react-native-architecture"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#react-native-architecture"}},[t._v("#")]),t._v(" React Native Architecture")]),t._v(" "),e("p",[t._v("At a high level, RN consists of the UI front which is essentially JavaScript which interacts with the native iOS and Android platforms over a bridge. When communicating over the bridge values from JS are converted to native and vice versa.")]),t._v(" "),e("p",[t._v("The native part of RN consists of Android as well as iOS modules and components. The Android and iOS sections are full fledged native projects which interact with the JS side over the native bridge. A RN project has all the build configuraiton required to build both Android and iOS projects.")]),t._v(" "),e("p",[t._v("For the purpose of making "),e("code",[t._v("bdk-rn")]),t._v(", "),e("code",[t._v("bdk-kotlin")]),t._v(" is used as the native Android module and "),e("code",[t._v("bdk-swift")]),t._v(" as the native iOS module. These are configured and wrapped in a RN Project as part of the platform specific native modules within the RN Project. This RN Project is then built to be a reusable React Native module.")]),t._v(" "),e("figure",[e("img",{attrs:{src:a(344),alt:""}})]),t._v(" "),e("h2",{attrs:{id:"native-integration"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#native-integration"}},[t._v("#")]),t._v(" Native Integration")]),t._v(" "),e("p",[t._v("In order to communicate to native modules on Android and iOS, React Native provides React Context API for Java/Kotlin as well as Swift. React Context API are used to build the interface to the native bridge allowing communication from JS to native modules.")]),t._v(" "),e("p",[t._v("bdk-rn uses React Context API plus some native code to wrap and enhance bdk-kotlin and bdk-swift APIs. The native code calls and interacts with the Android and iOS native modules which interface with the underlying mobile platform.")]),t._v(" "),e("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"20%"},attrs:{src:a(345)}}),t._v(" "),e("h2",{attrs:{id:"android-module"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#android-module"}},[t._v("#")]),t._v(" Android Module")]),t._v(" "),e("p",[t._v("We will go into the details of how the BDK Android Module is integrated, we wont cover iOS.")]),t._v(" "),e("p",[t._v("Starting off with a basic RN project. This project will be enhanced with bdk-kotlin and bdk-swift binaries and native code. For now lets go into the details for Android, iOS has similar steps to be done in Swift.")]),t._v(" "),e("p",[t._v("The Android native project is located under the root project folder.")]),t._v(" "),e("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"120%"},attrs:{src:a(346)}}),t._v(" "),e("p",[t._v("Here we need to add a dependency in "),e("code",[t._v("build.gradle")]),t._v(" for bdk-kotlin's android native binary. This will enable bdk-kotlin to be downloaded and available as one of the native modules.")]),t._v(" "),e("div",{staticClass:"language-javascript extra-class"},[e("pre",{pre:!0,attrs:{class:"language-javascript"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: build.gradle")]),t._v("\n\nrepositories "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("mavenCentral")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\ndependencies "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//noinspection GradleDynamicVersion")]),t._v("\n implementation "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'com.facebook.react:react-native:+'")]),t._v("\n\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// bitcoindevkit")]),t._v("\n implementation "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'org.bitcoindevkit:bdk-android:0.7.1'")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("We will create an Android native module which will interact with "),e("code",[t._v("bdk-android")]),t._v(".\nThis is done by adding a new Kotlin file "),e("code",[t._v("BdkRnModule.kt")]),t._v(" inside "),e("code",[t._v("android/app/src/main/java/com/bdkrn/")]),t._v(" folder")]),t._v(" "),e("p",[t._v("This will be part of the native code for bdk-rn module.Here a new class will be created to encapsulate the interaction with bitcoindevkit's android native binary.")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnModule.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" android"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("annotation"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("SuppressLint\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" android"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("util"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Log\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Arguments\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Promise "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" Result\n")])])]),e("p",[e("code",[t._v("org.bitcoindevkit")]),t._v(" will also need to be imported here")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" org"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bitcoindevkit"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Wallet "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" BdkWallet\n")])])]),e("p",[t._v("To use React Context API "),e("code",[t._v("com.facebook.react.bridge.*")]),t._v(" also needs to be imported")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("\n")])])]),e("p",[t._v("A new class needs to be defined here which will implement the React Context API")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnModule.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("BdkRnModule")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reactContext"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ReactApplicationContext"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v("\n\t"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("ReactContextBaseJavaModule")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reactContext"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fun")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("getName")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal singleline"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"BdkRnModule"')])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("With the base imports and class defined, we can start writing methods.\nThis will demonstrate how bdk native module will be called and how values will be returned to JS over the native bridge")]),t._v(" "),e("p",[t._v("Lets create a method that can be called from JaveScript, to do so we use the "),e("code",[t._v("@ReactMethod")]),t._v(" directive which is part of the React Context API. This will expose the method so that it can be called from JavaScript.")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnModule.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token annotation builtin"}},[t._v("@ReactMethod")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fun")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("createWallet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Promise"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("We need one more file to complete our native framework. A new Kotlin file, "),e("code",[t._v("BdkRnPackage.kt")]),t._v(" is required to package all our native code into a new android module, here we specify the name of the file we just crated as the module name("),e("code",[t._v("BdkRnModule")]),t._v("). This can be done by adding the following code:")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnPackage.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ReactPackage\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("NativeModule\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ReactApplicationContext\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("uimanager"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ViewManager\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" BdkRnPackage "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ReactPackage "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fun")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("createNativeModules")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reactContext"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ReactApplicationContext"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v("\n MutableList"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("NativeModule"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("mutableListOf")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("BdkRnModule")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reactContext"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),e("p",[t._v("Now lets add code for creating a wallet in BdkRnModule.kt")]),t._v(" "),e("p",[t._v("The methods used here are for bdk-kotlin and available in the bdk-kotlin documentation.")]),t._v(" "),e("p",[t._v("We first create a key info object")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnModule.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token annotation builtin"}},[t._v("@ReactMethod")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fun")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("createWallet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Promise"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Create key info with a new mnemonic")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" keys"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ExtendedKeyInfo "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateExtendedKey")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n Network"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("TESTNET"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n WordCount"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WORDS12"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string-literal singleline"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n \n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// more code to follow...")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create descriptor and change descriptor")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create databaseConfig and blockchainconfig")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create wallet")]),t._v("\n \n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("Then key info used to create a wallet descriptor and change descriptor:")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" descriptor"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" String "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal singleline"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"wpkh("')])]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" keys"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("xprv "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(' "'),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("84")]),t._v("'"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("'"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("'"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/*)\"\n\nval changeDescriptor: String = descriptor.replace(\"/84'/1'/0'/0/*\",\"/84'/1'/0'/1/*\")\n")])])])]),e("p",[t._v("To create a wallet with bdk we need to specify wallet descriptor, network, a database config, blockchain config. We intend to use bitcoin testnet and want to use default memory for data. For bitcoin node we will use a public electrum server. We will need to define these parameters to create a wallet.")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" network "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" `Network"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("TESTNET`\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" databaseConfig "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" DatabaseConfig"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Memory\nblockchainConfig "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n BlockchainConfig"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("Electrum")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("ElectrumConfig")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string-literal singleline"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ssl://electrum.blockstream.info:60002"')])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" 5u"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" 10u"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("Once done we can use these parameters to create a BDK wallet using the native android BDK library:")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" wallet"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" BdkWallet "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("BdkWallet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n descriptor"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n changeDescriptor"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("setNetwork")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("network"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n databaseConfig"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n config\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("Once we have a wallet initialised, we can call methods on it to sync, generate a new address and to get balance")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[t._v("wallet"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("sync")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ProgressLog"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" maxAddress"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nwallet"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("getNewAddress")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nwallet"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("toLong")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("To return a value from the native android code to React Native’s Javascript side over the JS Native bridge we will use "),e("code",[t._v("com.facebook.react.bridge.Promise")]),t._v(". To return balance information to JS, the following code can be used")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" balance"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" String "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" wallet"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("toLong")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nresult"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("resolve")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("balance"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("At this point we have an Android native module and it can be invoked from JS by calling "),e("code",[t._v("createWallet")]),t._v(" and it will return the balance.")]),t._v(" "),e("p",[t._v("This project can be imported into any RN project to reuse the defined "),e("code",[t._v("createWallet")]),t._v(" method without the need to carry out the setup described above.")]),t._v(" "),e("div",{staticClass:"language-javascript extra-class"},[e("pre",{pre:!0,attrs:{class:"language-javascript"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// any js file in React Native")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BdkRn "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'bdk-rn'")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create a wallet and retrieve current balance")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" balance "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" BdkRn"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("createWallet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nconsole"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" balance "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])])]),e("p",[t._v("The actual "),e("code",[t._v("bdk-rn")]),t._v(" module has organised the native code into granular methods for different stages of creating a wallet and for different interactions and use cases for a bitcoin application, like generating, mnemonic, keys, creating wallet for different networks, creating descriptors, creating or restoring wallet, fetching balance, fetching transactions and many other methods. Please refer to the "),e("a",{attrs:{href:"https://github.com/LtbLightning/bdk-rn#usage",target:"_blank",rel:"noopener noreferrer"}},[t._v("user guide in the readme"),e("OutboundLink")],1),t._v(" on Github for the complete API. The set of APIs available will grow in the near future as more APIs are added. This article can also be used as a guide to add new methods to the existing bdk-rn project.")]),t._v(" "),e("p",[t._v("The objective of "),e("code",[t._v("bdk-rn")]),t._v(" is to enable React Native developers to quickly start developing applications without the need to package BDK as described above.")]),t._v(" "),e("p",[t._v("Be on the lookout for user guides and tutorials on how to build bitcoin applications using "),e("code",[t._v("bdk-rn")]),t._v(" and "),e("code",[t._v("bdk-flutter")]),t._v(".")]),t._v(" "),e("h2",{attrs:{id:"references"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#references"}},[t._v("#")]),t._v(" References")]),t._v(" "),e("p",[t._v("Creating native modules for Android and iOS: "),e("a",{attrs:{href:"https://reactnative.dev/docs/native-modules-intro",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://reactnative.dev/docs/native-modules-intro"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("React Native Architecture: "),e("a",{attrs:{href:"https://www.reactnative.guide/3-react-native-internals/3.1-react-native-internals.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://www.reactnative.guide/3-react-native-internals/3.1-react-native-internals.html"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("BDK-Android API: "),e("a",{attrs:{href:"https://bitcoindevkit.org/bdk-jvm/bdk-jvm/org.bitcoindevkit/index.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://bitcoindevkit.org/bdk-jvm/bdk-jvm/org.bitcoindevkit/index.html"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("BDK-RN: "),e("a",{attrs:{href:"https://github.com/LtbLightning/bdk-rn",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LtbLightning/bdk-rn"),e("OutboundLink")],1)]),t._v(" "),e("h2",{attrs:{id:"feedback"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#feedback"}},[t._v("#")]),t._v(" Feedback")]),t._v(" "),e("p",[t._v("The best way to give feedback on this would be to comment on the "),e("a",{attrs:{href:"https://github.com/bitcoindevkit/bitcoindevkit.org/pull/107",target:"_blank",rel:"noopener noreferrer"}},[t._v("pull request"),e("OutboundLink")],1),t._v(" for this blog post.\nThanks in advance.")])])}),[],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[31],{344:function(t,e,a){t.exports=a.p+"assets/img/BDK-RN-Architecture.42fbc351.png"},345:function(t,e,a){t.exports=a.p+"assets/img/BDK-RN.370f20c3.png"},346:function(t,e,a){t.exports=a.p+"assets/img/android_folder.0ff999be.png"},389:function(t,e,a){"use strict";a.r(e);var n=a(7),s=Object(n.a)({},(function(){var t=this,e=t._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("p",[t._v("The "),e("strong",[t._v("BitcoinDevkit")]),t._v("'s "),e("strong",[t._v("React Native")]),t._v(" library ("),e("code",[t._v("bdk-rn")]),t._v(") makes it easy to develop bitcoin applications for both Android and iOS mobile platforms. Using "),e("code",[t._v("bdk-rn")]),t._v(", knowledge of the underlying bitcoin and BDK API is not required and using "),e("code",[t._v("bdk-rn")]),t._v(" is similar to using any other RN module. The goal is "),e("strong",[t._v("Rapid Bitcoin Application Development")]),t._v(" by doing the heavy lifting in advance and providing a reusable library for other developers to use. Developers simply install using "),e("code",[t._v("yarn add")]),t._v(" and start using it in a React Native Project. The native code, Rust lang implementation, configuration and other setup details are all taken care of by "),e("code",[t._v("bdk-rn")]),t._v(".")]),t._v(" "),e("p",[t._v("This article is "),e("strong",[t._v("NOT a guide on how to use bdk-rn")]),t._v(" to build a bitcoin application, rather this is an insight into how "),e("code",[t._v("bdk-rn")]),t._v(" was developed. For help on how to use "),e("code",[t._v("bdk-rn")]),t._v(" to develop a bitcoin wallet or application please refer to the user guide in the "),e("a",{attrs:{href:"https://github.com/LtbLightning/bdk-rn#usage",target:"_blank",rel:"noopener noreferrer"}},[t._v("readme"),e("OutboundLink")],1),t._v(" on Github. There will be "),e("code",[t._v("how to guides")]),t._v(" published shortly on getting started with "),e("code",[t._v("bdk-rn")]),t._v(".")]),t._v(" "),e("h2",{attrs:{id:"react-native-architecture"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#react-native-architecture"}},[t._v("#")]),t._v(" React Native Architecture")]),t._v(" "),e("p",[t._v("At a high level, RN consists of the UI front which is essentially JavaScript which interacts with the native iOS and Android platforms over a bridge. When communicating over the bridge values from JS are converted to native and vice versa.")]),t._v(" "),e("p",[t._v("The native part of RN consists of Android as well as iOS modules and components. The Android and iOS sections are full fledged native projects which interact with the JS side over the native bridge. A RN project has all the build configuraiton required to build both Android and iOS projects.")]),t._v(" "),e("p",[t._v("For the purpose of making "),e("code",[t._v("bdk-rn")]),t._v(", "),e("code",[t._v("bdk-kotlin")]),t._v(" is used as the native Android module and "),e("code",[t._v("bdk-swift")]),t._v(" as the native iOS module. These are configured and wrapped in a RN Project as part of the platform specific native modules within the RN Project. This RN Project is then built to be a reusable React Native module.")]),t._v(" "),e("figure",[e("img",{attrs:{src:a(344),alt:""}})]),t._v(" "),e("h2",{attrs:{id:"native-integration"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#native-integration"}},[t._v("#")]),t._v(" Native Integration")]),t._v(" "),e("p",[t._v("In order to communicate to native modules on Android and iOS, React Native provides React Context API for Java/Kotlin as well as Swift. React Context API are used to build the interface to the native bridge allowing communication from JS to native modules.")]),t._v(" "),e("p",[t._v("bdk-rn uses React Context API plus some native code to wrap and enhance bdk-kotlin and bdk-swift APIs. The native code calls and interacts with the Android and iOS native modules which interface with the underlying mobile platform.")]),t._v(" "),e("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"20%"},attrs:{src:a(345)}}),t._v(" "),e("h2",{attrs:{id:"android-module"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#android-module"}},[t._v("#")]),t._v(" Android Module")]),t._v(" "),e("p",[t._v("We will go into the details of how the BDK Android Module is integrated, we wont cover iOS.")]),t._v(" "),e("p",[t._v("Starting off with a basic RN project. This project will be enhanced with bdk-kotlin and bdk-swift binaries and native code. For now lets go into the details for Android, iOS has similar steps to be done in Swift.")]),t._v(" "),e("p",[t._v("The Android native project is located under the root project folder.")]),t._v(" "),e("img",{staticStyle:{display:"block",margin:"0 auto",zoom:"120%"},attrs:{src:a(346)}}),t._v(" "),e("p",[t._v("Here we need to add a dependency in "),e("code",[t._v("build.gradle")]),t._v(" for bdk-kotlin's android native binary. This will enable bdk-kotlin to be downloaded and available as one of the native modules.")]),t._v(" "),e("div",{staticClass:"language-javascript extra-class"},[e("pre",{pre:!0,attrs:{class:"language-javascript"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: build.gradle")]),t._v("\n\nrepositories "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("mavenCentral")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\ndependencies "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//noinspection GradleDynamicVersion")]),t._v("\n implementation "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'com.facebook.react:react-native:+'")]),t._v("\n\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// bitcoindevkit")]),t._v("\n implementation "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'org.bitcoindevkit:bdk-android:0.7.1'")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("We will create an Android native module which will interact with "),e("code",[t._v("bdk-android")]),t._v(".\nThis is done by adding a new Kotlin file "),e("code",[t._v("BdkRnModule.kt")]),t._v(" inside "),e("code",[t._v("android/app/src/main/java/com/bdkrn/")]),t._v(" folder")]),t._v(" "),e("p",[t._v("This will be part of the native code for bdk-rn module.Here a new class will be created to encapsulate the interaction with bitcoindevkit's android native binary.")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnModule.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" android"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("annotation"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("SuppressLint\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" android"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("util"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Log\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Arguments\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Promise "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" Result\n")])])]),e("p",[e("code",[t._v("org.bitcoindevkit")]),t._v(" will also need to be imported here")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" org"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bitcoindevkit"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Wallet "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" BdkWallet\n")])])]),e("p",[t._v("To use React Context API "),e("code",[t._v("com.facebook.react.bridge.*")]),t._v(" also needs to be imported")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("\n")])])]),e("p",[t._v("A new class needs to be defined here which will implement the React Context API")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnModule.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("BdkRnModule")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reactContext"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ReactApplicationContext"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v("\n\t"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("ReactContextBaseJavaModule")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reactContext"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fun")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("getName")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal singleline"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"BdkRnModule"')])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("With the base imports and class defined, we can start writing methods.\nThis will demonstrate how bdk native module will be called and how values will be returned to JS over the native bridge")]),t._v(" "),e("p",[t._v("Lets create a method that can be called from JaveScript, to do so we use the "),e("code",[t._v("@ReactMethod")]),t._v(" directive which is part of the React Context API. This will expose the method so that it can be called from JavaScript.")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnModule.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token annotation builtin"}},[t._v("@ReactMethod")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fun")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("createWallet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Promise"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("We need one more file to complete our native framework. A new Kotlin file, "),e("code",[t._v("BdkRnPackage.kt")]),t._v(" is required to package all our native code into a new android module, here we specify the name of the file we just crated as the module name("),e("code",[t._v("BdkRnModule")]),t._v("). This can be done by adding the following code:")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnPackage.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ReactPackage\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("NativeModule\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bridge"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ReactApplicationContext\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" com"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("facebook"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("react"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("uimanager"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ViewManager\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" BdkRnPackage "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ReactPackage "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fun")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("createNativeModules")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reactContext"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ReactApplicationContext"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v("\n MutableList"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("NativeModule"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("mutableListOf")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("BdkRnModule")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reactContext"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),e("p",[t._v("Now lets add code for creating a wallet in BdkRnModule.kt")]),t._v(" "),e("p",[t._v("The methods used here are for bdk-kotlin and available in the bdk-kotlin documentation.")]),t._v(" "),e("p",[t._v("We first create a key info object")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File: BdkRnModule.kt")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token annotation builtin"}},[t._v("@ReactMethod")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fun")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("createWallet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Promise"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Create key info with a new mnemonic")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" keys"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ExtendedKeyInfo "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("generateExtendedKey")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n Network"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("TESTNET"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n WordCount"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WORDS12"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string-literal singleline"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n \n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// more code to follow...")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create descriptor and change descriptor")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create databaseConfig and blockchainconfig")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create wallet")]),t._v("\n \n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),e("p",[t._v("Then key info used to create a wallet descriptor and change descriptor:")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" descriptor"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" String "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string-literal singleline"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"wpkh("')])]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" keys"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("xprv "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(' "'),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("84")]),t._v("'"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("'"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("'"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/*)\"\n\nval changeDescriptor: String = descriptor.replace(\"/84'/1'/0'/0/*\",\"/84'/1'/0'/1/*\")\n")])])])]),e("p",[t._v("To create a wallet with bdk we need to specify wallet descriptor, network, a database config, blockchain config. We intend to use bitcoin testnet and want to use default memory for data. For bitcoin node we will use a public electrum server. We will need to define these parameters to create a wallet.")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" network "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" `Network"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("TESTNET`\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" databaseConfig "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" DatabaseConfig"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Memory\nblockchainConfig "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n BlockchainConfig"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("Electrum")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("ElectrumConfig")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string-literal singleline"}},[e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ssl://electrum.blockstream.info:60002"')])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" 5u"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("null")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" 10u"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("Once done we can use these parameters to create a BDK wallet using the native android BDK library:")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" wallet"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" BdkWallet "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("BdkWallet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n descriptor"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n changeDescriptor"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("setNetwork")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("network"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n databaseConfig"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n config\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("Once we have a wallet initialised, we can call methods on it to sync, generate a new address and to get balance")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[t._v("wallet"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("sync")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ProgressLog"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" maxAddress"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nwallet"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("getNewAddress")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nwallet"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("toLong")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("To return a value from the native android code to React Native’s Javascript side over the JS Native bridge we will use "),e("code",[t._v("com.facebook.react.bridge.Promise")]),t._v(". To return balance information to JS, the following code can be used")]),t._v(" "),e("div",{staticClass:"language-kotlin extra-class"},[e("pre",{pre:!0,attrs:{class:"language-kotlin"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" balance"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" String "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" wallet"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("getBalance")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("toLong")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nresult"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("resolve")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("balance"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("p",[t._v("At this point we have an Android native module and it can be invoked from JS by calling "),e("code",[t._v("createWallet")]),t._v(" and it will return the balance.")]),t._v(" "),e("p",[t._v("This project can be imported into any RN project to reuse the defined "),e("code",[t._v("createWallet")]),t._v(" method without the need to carry out the setup described above.")]),t._v(" "),e("div",{staticClass:"language-javascript extra-class"},[e("pre",{pre:!0,attrs:{class:"language-javascript"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// any js file in React Native")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BdkRn "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'bdk-rn'")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create a wallet and retrieve current balance")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" balance "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" BdkRn"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("createWallet")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\nconsole"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" balance "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])])]),e("p",[t._v("The actual "),e("code",[t._v("bdk-rn")]),t._v(" module has organised the native code into granular methods for different stages of creating a wallet and for different interactions and use cases for a bitcoin application, like generating, mnemonic, keys, creating wallet for different networks, creating descriptors, creating or restoring wallet, fetching balance, fetching transactions and many other methods. Please refer to the "),e("a",{attrs:{href:"https://github.com/LtbLightning/bdk-rn#usage",target:"_blank",rel:"noopener noreferrer"}},[t._v("user guide in the readme"),e("OutboundLink")],1),t._v(" on Github for the complete API. The set of APIs available will grow in the near future as more APIs are added. This article can also be used as a guide to add new methods to the existing bdk-rn project.")]),t._v(" "),e("p",[t._v("The objective of "),e("code",[t._v("bdk-rn")]),t._v(" is to enable React Native developers to quickly start developing applications without the need to package BDK as described above.")]),t._v(" "),e("p",[t._v("Be on the lookout for user guides and tutorials on how to build bitcoin applications using "),e("code",[t._v("bdk-rn")]),t._v(" and "),e("code",[t._v("bdk-flutter")]),t._v(".")]),t._v(" "),e("h2",{attrs:{id:"references"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#references"}},[t._v("#")]),t._v(" References")]),t._v(" "),e("p",[t._v("Creating native modules for Android and iOS: "),e("a",{attrs:{href:"https://reactnative.dev/docs/native-modules-intro",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://reactnative.dev/docs/native-modules-intro"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("React Native Architecture: "),e("a",{attrs:{href:"https://www.reactnative.guide/3-react-native-internals/3.1-react-native-internals.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://www.reactnative.guide/3-react-native-internals/3.1-react-native-internals.html"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("BDK-Android API: "),e("a",{attrs:{href:"https://bitcoindevkit.org/bdk-jvm/bdk-jvm/org.bitcoindevkit/index.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://bitcoindevkit.org/bdk-jvm/bdk-jvm/org.bitcoindevkit/index.html"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("BDK-RN: "),e("a",{attrs:{href:"https://github.com/LtbLightning/bdk-rn",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/LtbLightning/bdk-rn"),e("OutboundLink")],1)]),t._v(" "),e("h2",{attrs:{id:"feedback"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#feedback"}},[t._v("#")]),t._v(" Feedback")]),t._v(" "),e("p",[t._v("The best way to give feedback on this would be to comment on the "),e("a",{attrs:{href:"https://github.com/bitcoindevkit/bitcoindevkit.org/pull/107",target:"_blank",rel:"noopener noreferrer"}},[t._v("pull request"),e("OutboundLink")],1),t._v(" for this blog post.\nThanks in advance.")])])}),[],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/40.3a3417fd.js b/assets/js/40.578db781.js similarity index 99% rename from assets/js/40.3a3417fd.js rename to assets/js/40.578db781.js index e21c325a26..0051d28d33 100644 --- a/assets/js/40.3a3417fd.js +++ b/assets/js/40.578db781.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[40],{389:function(t,e,s){"use strict";s.r(e);var a=s(7),r=Object(a.a)({},(function(){var t=this,e=t._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h2",{attrs:{id:"2-of-3-multi-signature-descriptor-wallet-using-bdk-cli"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#2-of-3-multi-signature-descriptor-wallet-using-bdk-cli"}},[t._v("#")]),t._v(" 2-of-3 Multi-Signature Descriptor Wallet using bdk-cli")]),t._v(" "),e("h2",{attrs:{id:"overview-of-the-tutorial"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#overview-of-the-tutorial"}},[t._v("#")]),t._v(" Overview of the tutorial")]),t._v(" "),e("ul",[e("li",[t._v("The purpose of this tutorial is to continue learning "),e("code",[t._v("bdk-cli")]),t._v(" as our tool to manage a 2 of 3 multi-signature wallet.")]),t._v(" "),e("li",[t._v("Generate a receive address with a spending Policy of 2 out of 3 escrow aka multi-signature.")]),t._v(" "),e("li",[t._v("Intro to more complex but standard policies to create custom encumberances aka custom spending conditions for transactions.")])]),t._v(" "),e("p",[t._v("Note that to complete this tutorial, you'll need to enable the "),e("code",[t._v("compiler")]),t._v(" and "),e("code",[t._v("electrum")]),t._v(" flags when installing or building bdk-cli, for example by installing using:")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("cargo")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" bdk-cli "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--features")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("compiler,electrum\n")])])]),e("h2",{attrs:{id:"step-1-generate-the-xprvs-extended-keys-and-save-to-environment-variables"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-1-generate-the-xprvs-extended-keys-and-save-to-environment-variables"}},[t._v("#")]),t._v(" Step 1: Generate the XPRVs (Extended-Keys) and Save to environment variables")]),t._v(" "),e("blockquote",[e("p",[t._v("Create three private keys and each in their own environment variable")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export XPRV_00=$(bdk-cli key generate | jq -r '.xprv')")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export XPRV_01=$(bdk-cli key generate | jq -r '.xprv')")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export XPRV_02=$(bdk-cli key generate | jq -r '.xprv')")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/FwgUdwK.gif",alt:""}})]),t._v(" "),e("h3",{attrs:{id:"1a-verify-xprv-environment-variables-are-active"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#1a-verify-xprv-environment-variables-are-active"}},[t._v("#")]),t._v(" 1a: Verify XPRV environment variables are Active")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("env | grep XPRV")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/ZerGPbO.gif",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-2-generate-xpubs-extended-public-keys-save-to-environment-variables"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-2-generate-xpubs-extended-public-keys-save-to-environment-variables"}},[t._v("#")]),t._v(" Step 2: Generate XPUBs (Extended Public Keys) & Save to environment variables")]),t._v(" "),e("blockquote",[e("p",[t._v("Generate the three individual Public Keys aka XPUBs using our Private key and descriptor path.")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export XPUB_00=$(bdk-cli key derive --xprv $XPRV_00 --path "m/84\'/1\'/0\'/0" | jq -r ".xpub")')])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export XPUB_01=$(bdk-cli key derive --xprv $XPRV_01 --path "m/84\'/1\'/0\'/0" | jq -r ".xpub")')])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export XPUB_02=$(bdk-cli key derive --xprv $XPRV_02 --path "m/84\'/1\'/0\'/0" | jq -r ".xpub")')])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/xT3KRh4.gif",alt:""}})]),t._v(" "),e("h3",{attrs:{id:"2a-verify-xpub-environment-variables"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#2a-verify-xpub-environment-variables"}},[t._v("#")]),t._v(" 2a: Verify XPUB environment variables")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("env | grep XPUB")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/SzAip9E.gif",alt:""}})]),t._v(" "),e("hr"),t._v(" "),e("h2",{attrs:{id:"step-3-create-single-wallet-descriptors"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-3-create-single-wallet-descriptors"}},[t._v("#")]),t._v(" Step 3: Create Single-Wallet Descriptors")]),t._v(" "),e("blockquote",[e("p",[t._v("Create the wallet Descriptor for each wallet")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export DESCRIPTOR_00="$XPRV_00/84h/1h/0h/0/*"')])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export DESCRIPTOR_01="$XPRV_01/84h/1h/0h/0/*"')])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export DESCRIPTOR_02="$XPRV_02/84h/1h/0h/0/*"')])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/mFrWt6b.png",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-4-create-multi-sig-descriptor-wallets"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-4-create-multi-sig-descriptor-wallets"}},[t._v("#")]),t._v(" Step 4: Create Multi-Sig-Descriptor Wallets")]),t._v(" "),e("blockquote",[e("p",[t._v("This is how you create the 2-of-3 multi-sig output descriptor. You will need (one PrivateKey and two Xpubs) It consists of using the "),e("code",[t._v("compiler")]),t._v(" function to parse "),e("code",[t._v("policy")]),t._v(" to "),e("code",[t._v("mini-script")]),t._v(" .")])]),t._v(" "),e("ul",[e("li",[t._v("When creating the descriptor the order matters so be aware of that when following tutorial if you are for any reason changing the order of the policy.")])]),t._v(" "),e("h4",{attrs:{id:"multi-sig-wallet-0"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#multi-sig-wallet-0"}},[t._v("#")]),t._v(" Multi-Sig-Wallet 0")]),t._v(" "),e("ul",[e("li",[t._v("[ ] ▶️ "),e("code",[t._v("export MULTI_DESCRIPTOR_00=$(bdk-cli compile \"thresh(2,pk($DESCRIPTOR_00),pk($XPUB_01),pk($XPUB_02))\" | jq -r '.descriptor')")])])]),t._v(" "),e("h4",{attrs:{id:"multi-sig-wallet-1"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#multi-sig-wallet-1"}},[t._v("#")]),t._v(" Multi-Sig-Wallet 1")]),t._v(" "),e("ul",[e("li",[t._v("[ ] ▶️ "),e("code",[t._v("export MULTI_DESCRIPTOR_01=$(bdk-cli compile \"thresh(2,pk($XPUB_00),pk($DESCRIPTOR_01),pk($XPUB_02))\" | jq -r '.descriptor')")])])]),t._v(" "),e("h4",{attrs:{id:"multi-sig-wallet-2"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#multi-sig-wallet-2"}},[t._v("#")]),t._v(" Multi-Sig-Wallet 2")]),t._v(" "),e("ul",[e("li",[t._v("[ ] ▶️ "),e("code",[t._v("export MULTI_DESCRIPTOR_02=$(bdk-cli compile \"thresh(2,pk($XPUB_00),pk($XPUB_01),pk($DESCRIPTOR_02))\" | jq -r '.descriptor')")])])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/Yb8RmFS.gif",alt:""}})]),t._v(" "),e("h4",{attrs:{id:"multi-sig-2-of-3-policy-gets-compiled-to-miniscript"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#multi-sig-2-of-3-policy-gets-compiled-to-miniscript"}},[t._v("#")]),t._v(" multi-sig 2 of 3 policy gets compiled to miniscript")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# policy")]),t._v("\nthresh"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",pk"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("XPRV_A"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(",pk"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("XPUB_B"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(",pk"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("XPUB_C"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("))")]),t._v(" \n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# miniscript")]),t._v("\nwsh"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("multi"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",XPRV_KEY,PUBKEY_B,XPUB_C"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("))")]),t._v("\n")])])]),e("hr"),t._v(" "),e("h3",{attrs:{id:"4a-verify-multi-sig-descriptor-environment-variables-are-active"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#4a-verify-multi-sig-descriptor-environment-variables-are-active"}},[t._v("#")]),t._v(" 4a: Verify Multi-Sig-Descriptor environment variables are active")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("env | grep MULTI")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/aAgtlsi.gif",alt:""}})]),t._v(" "),e("hr"),t._v(" "),e("h2",{attrs:{id:"step-5-generate-receive-address-by-using-multi-sig-descriptor-wallets"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-5-generate-receive-address-by-using-multi-sig-descriptor-wallets"}},[t._v("#")]),t._v(" Step 5: Generate Receive Address by using Multi-Sig-Descriptor Wallets")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 get_new_address")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 get_new_address")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd02 --descriptor $MULTI_DESCRIPTOR_02 get_new_address")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/w1fxPSn.gif",alt:""}})]),t._v(" "),e("p",[t._v("🔴 Did you generate the same address for all three? Good! Else, something might be incorrect.")]),t._v(" "),e("h2",{attrs:{id:"step-6-send-testnet-bitcoin-to-the-newly-created-receive-address"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-6-send-testnet-bitcoin-to-the-newly-created-receive-address"}},[t._v("#")]),t._v(" Step 6: Send Testnet Bitcoin to the newly created receive-address")]),t._v(" "),e("p",[e("a",{attrs:{href:"https://testnet-faucet.mempool.co",target:"_blank",rel:"noopener noreferrer"}},[t._v("Bitcoin Testnet Faucet link:1"),e("OutboundLink")],1),t._v(" "),e("a",{attrs:{href:"https://bitcoinfaucet.uo1.net",target:"_blank",rel:"noopener noreferrer"}},[t._v("Bitcoin Testnet Faucet link:2"),e("OutboundLink")],1)]),t._v(" "),e("h2",{attrs:{id:"step-7-sync-one-of-the-multi-sig-wallets"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-7-sync-one-of-the-multi-sig-wallets"}},[t._v("#")]),t._v(" Step 7: Sync one of the Multi-Sig Wallets")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sync")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/GuefgeI.gif",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-8-check-balance-multi-sig-wallets"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-8-check-balance-multi-sig-wallets"}},[t._v("#")]),t._v(" Step 8: Check Balance Multi-Sig Wallets")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 get_balance")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/zNciCqF.gif",alt:""}})]),t._v(" "),e("ul",[e("li",[t._v("Every wallet has access to sync and view balance.")])]),t._v(" "),e("h2",{attrs:{id:"step-9-check-multi-sig-policies-on-descriptor-wallet"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-9-check-multi-sig-policies-on-descriptor-wallet"}},[t._v("#")]),t._v(" Step 9: Check Multi-Sig Policies on Descriptor Wallet")]),t._v(" "),e("p",[t._v("▶️"),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 policies")])]),t._v(" "),e("p",[t._v("The output below confirms the command was successful.")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"external"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"contribution"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"conditions"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"items"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"m"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"n"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sorted"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" false,\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"type"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"PARTIAL"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"id"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"seaxtqqn"')]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"keys"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fingerprint"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"7cdf2d46"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fingerprint"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fc7870cd"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fingerprint"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"26b03333"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"satisfaction"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"items"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"m"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"n"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sorted"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" false,\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"type"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"PARTIAL"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"threshold"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"type"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"MULTISIG"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"internal"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" null\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\n")])])]),e("h3",{attrs:{id:"spendingpolicyrequired-for-complex-descriptors"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#spendingpolicyrequired-for-complex-descriptors"}},[t._v("#")]),t._v(" SpendingPolicyRequired for complex descriptors")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--external_policy")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"{'),e("span",{pre:!0,attrs:{class:"token entity",title:'\\"'}},[t._v('\\"')]),t._v("seaxtqqn"),e("span",{pre:!0,attrs:{class:"token entity",title:'\\"'}},[t._v('\\"')]),t._v(': [0,1]}"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("-rootnode-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("children "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#0 and #1 of root node>")]),t._v("\n")])])]),e("blockquote",[e("p",[t._v("Save the \"id\": We will need to use this ''id'' later.")])]),t._v(" "),e("p",[t._v("More info on "),e("a",{attrs:{href:"https://bitcoindevkit.org/bdk-cli/interface/",target:"_blank",rel:"noopener noreferrer"}},[t._v("external policies here"),e("OutboundLink")],1)]),t._v(" "),e("h2",{attrs:{id:"step-10-create-a-transaction-psbt"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-10-create-a-transaction-psbt"}},[t._v("#")]),t._v(" Step 10: Create a Transaction (PSBT)")]),t._v(" "),e("ul",[e("li",[t._v("1st Create a PSBT using the first wallet")]),t._v(" "),e("li",[t._v("2nd Sign the PSBT with the first wallet")]),t._v(" "),e("li",[t._v("3rd Sign PSBT with the second wallet")]),t._v(" "),e("li",[t._v("Broadcast PSBT")])]),t._v(" "),e("h3",{attrs:{id:"export-unsigned-psbt-to-environment-variable"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#export-unsigned-psbt-to-environment-variable"}},[t._v("#")]),t._v(" Export UNSIGNED_PSBT to environment variable")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export UNSIGNED_PSBT=$(bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 create_tx --send_all --to mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt:0 --external_policy "{\\"CHANGE_ID_HERE\\": [0,1]}" | jq -r \'.psbt\')')])]),t._v(" "),e("h3",{attrs:{id:"verify-unsigned-psbt-environment-variable"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#verify-unsigned-psbt-environment-variable"}},[t._v("#")]),t._v(" Verify UNSIGNED_PSBT environment variable")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("env | grep UNSIGNED")]),t._v(" "),e("img",{attrs:{src:"https://i.imgur.com/djHaRDq.gif",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-11-sign-the-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-11-sign-the-transaction"}},[t._v("#")]),t._v(" Step 11: SIGN the Transaction")]),t._v(" "),e("h3",{attrs:{id:"1st-wallet-signs-the-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#1st-wallet-signs-the-transaction"}},[t._v("#")]),t._v(" 1st Wallet Signs the transaction")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sign --psbt $UNSIGNED_PSBT")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export ONESIG_PSBT=$(bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sign --psbt $UNSIGNED_PSBT | jq -r '.psbt')")])]),t._v(" "),e("p",[t._v("▶️"),e("code",[t._v("env | grep ONESIG")])]),t._v(" "),e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v('{\n "is_finalized": false,\n "psbt": "cHNidP8BAFUBAAAAAdYCtva/7Rkt+fgFu3mxAdaPh4uTbgBL3HmYZgcEKWygAAAAAAD/////AQqGAQAAAAAAGXapFDRKD0jKFQ7CuQOBdmC5tosTpnAmiKwAAAAAAAEA6gIAAAAAAQFLyGFJFK884DGBM1WgskRZ6gKp/7oZ+Z30u0+wF3pZYAEAAAAA/v///wKghgEAAAAAACIAINHcOQLE6GpJ3J+FOzn/be+HApxW8sZtGqfA3TBW+NYX91hoOAAAAAAWABTPQDZx2wYYIn+ug2pZBmWBn0Tu/gJHMEQCIHu6GmRMDgPZyTx+klFMA9VujR3qDA/Y08kSkRvOaChjAiBAtExtGAYLuQ/DDJzCqLlNZ1bMB3MV+nxsLfTdI9YcYwEhA0b8lz+kt0xHfR/tjUKOc2Nt2L61pDd5vJ/lsKi8pw9MmFUjAAEBK6CGAQAAAAAAIgAg0dw5AsToakncn4U7Of9t74cCnFbyxm0ap8DdMFb41hciAgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDUgwRQIhAJdILr7G3UzYylyr2fA13MFsz/jG4+iZlKeEkX79d082AiA99UF0/uFyXBVNUmuGaxdHL7wlhzqfbgGLMREN0z/O6QEBBWlSIQIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDSEDzsDXexRPSxeXiLJoS0i2fQlOoOGHmo+Dhaeaq3oHV6YhAjGKA2Dqg+QeMICBAifYslQF2WrehLEQ0iEOpp/+eQ0NU64iBgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDRh83y1GVAAAgAEAAIAAAACAAAAAAAAAAAAiBgIxigNg6oPkHjCAgQIn2LJUBdlq3oSxENIhDqaf/nkNDRgmsDMzVAAAgAEAAIAAAACAAAAAAAAAAAAiBgPOwNd7FE9LF5eIsmhLSLZ9CU6g4Yeaj4OFp5qregdXphj8eHDNVAAAgAEAAIAAAACAAAAAAAAAAAAAAA=="\n}\n')])])]),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/0w4sK5y.gif",alt:""}})]),t._v(" "),e("h3",{attrs:{id:"2nd-wallet-signs-the-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#2nd-wallet-signs-the-transaction"}},[t._v("#")]),t._v(" 2nd Wallet Signs the transaction")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 sign --psbt $ONESIG_PSBT")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export SECONDSIG_PSBT=$(bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 sign --psbt $ONESIG_PSBT | jq -r '.psbt')")])]),t._v(" "),e("p",[t._v("▶️"),e("code",[t._v("env | grep SECONDSIG")])]),t._v(" "),e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v('{\n "is_finalized": true,\n "psbt": "cHNidP8BAFUBAAAAAdYCtva/7Rkt+fgFu3mxAdaPh4uTbgBL3HmYZgcEKWygAAAAAAD/////AQqGAQAAAAAAGXapFDRKD0jKFQ7CuQOBdmC5tosTpnAmiKwAAAAAAAEA6gIAAAAAAQFLyGFJFK884DGBM1WgskRZ6gKp/7oZ+Z30u0+wF3pZYAEAAAAA/v///wKghgEAAAAAACIAINHcOQLE6GpJ3J+FOzn/be+HApxW8sZtGqfA3TBW+NYX91hoOAAAAAAWABTPQDZx2wYYIn+ug2pZBmWBn0Tu/gJHMEQCIHu6GmRMDgPZyTx+klFMA9VujR3qDA/Y08kSkRvOaChjAiBAtExtGAYLuQ/DDJzCqLlNZ1bMB3MV+nxsLfTdI9YcYwEhA0b8lz+kt0xHfR/tjUKOc2Nt2L61pDd5vJ/lsKi8pw9MmFUjAAEBK6CGAQAAAAAAIgAg0dw5AsToakncn4U7Of9t74cCnFbyxm0ap8DdMFb41hciAgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDUgwRQIhAJdILr7G3UzYylyr2fA13MFsz/jG4+iZlKeEkX79d082AiA99UF0/uFyXBVNUmuGaxdHL7wlhzqfbgGLMREN0z/O6QEiAgPOwNd7FE9LF5eIsmhLSLZ9CU6g4Yeaj4OFp5qregdXpkgwRQIhAO2aRERcublhAzToshkZRMg2I8GaE7mM2ECr0vYyuscmAiB5KK4ETlvrLqL0QbcRbGqrSwIa9lVuOqP3f5qCnGRMaQEBBWlSIQIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDSEDzsDXexRPSxeXiLJoS0i2fQlOoOGHmo+Dhaeaq3oHV6YhAjGKA2Dqg+QeMICBAifYslQF2WrehLEQ0iEOpp/+eQ0NU64iBgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDRh83y1GVAAAgAEAAIAAAACAAAAAAAAAAAAiBgIxigNg6oPkHjCAgQIn2LJUBdlq3oSxENIhDqaf/nkNDRgmsDMzVAAAgAEAAIAAAACAAAAAAAAAAAAiBgPOwNd7FE9LF5eIsmhLSLZ9CU6g4Yeaj4OFp5qregdXphj8eHDNVAAAgAEAAIAAAACAAAAAAAAAAAABBwABCP3+AAQASDBFAiEAl0guvsbdTNjKXKvZ8DXcwWzP+Mbj6JmUp4SRfv13TzYCID31QXT+4XJcFU1Sa4ZrF0cvvCWHOp9uAYsxEQ3TP87pAUgwRQIhAO2aRERcublhAzToshkZRMg2I8GaE7mM2ECr0vYyuscmAiB5KK4ETlvrLqL0QbcRbGqrSwIa9lVuOqP3f5qCnGRMaQFpUiECI1AiHZ8q+qw7bjYVTbeGQQ3L2C2sH6CW82z8sXP1jQ0hA87A13sUT0sXl4iyaEtItn0JTqDhh5qPg4Wnmqt6B1emIQIxigNg6oPkHjCAgQIn2LJUBdlq3oSxENIhDqaf/nkNDVOuAAA="\n}\n')])])]),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/OdLHnJ3.gif",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-12-broadcast-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-12-broadcast-transaction"}},[t._v("#")]),t._v(" Step 12: Broadcast Transaction")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 broadcast --psbt $SECONDSIG_PSBT")])]),t._v(" "),e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v('{\n "txid": "61da2451874a483aa8d1d0787c7680d157639f284840de8885098cac43f6cc2f"\n}\n')])])]),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/M7s0Fd6.gif",alt:""}})]),t._v(" "),e("h3",{attrs:{id:"verify-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#verify-transaction"}},[t._v("#")]),t._v(" Verify Transaction")]),t._v(" "),e("p",[t._v("Verify transcation in the memory pool on testnet "),e("a",{attrs:{href:"https://mempool.space/testnet",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mempool-testnet!"),e("OutboundLink")],1)])])}),[],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[40],{388:function(t,e,s){"use strict";s.r(e);var a=s(7),r=Object(a.a)({},(function(){var t=this,e=t._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h2",{attrs:{id:"2-of-3-multi-signature-descriptor-wallet-using-bdk-cli"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#2-of-3-multi-signature-descriptor-wallet-using-bdk-cli"}},[t._v("#")]),t._v(" 2-of-3 Multi-Signature Descriptor Wallet using bdk-cli")]),t._v(" "),e("h2",{attrs:{id:"overview-of-the-tutorial"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#overview-of-the-tutorial"}},[t._v("#")]),t._v(" Overview of the tutorial")]),t._v(" "),e("ul",[e("li",[t._v("The purpose of this tutorial is to continue learning "),e("code",[t._v("bdk-cli")]),t._v(" as our tool to manage a 2 of 3 multi-signature wallet.")]),t._v(" "),e("li",[t._v("Generate a receive address with a spending Policy of 2 out of 3 escrow aka multi-signature.")]),t._v(" "),e("li",[t._v("Intro to more complex but standard policies to create custom encumberances aka custom spending conditions for transactions.")])]),t._v(" "),e("p",[t._v("Note that to complete this tutorial, you'll need to enable the "),e("code",[t._v("compiler")]),t._v(" and "),e("code",[t._v("electrum")]),t._v(" flags when installing or building bdk-cli, for example by installing using:")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("cargo")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" bdk-cli "),e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--features")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("compiler,electrum\n")])])]),e("h2",{attrs:{id:"step-1-generate-the-xprvs-extended-keys-and-save-to-environment-variables"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-1-generate-the-xprvs-extended-keys-and-save-to-environment-variables"}},[t._v("#")]),t._v(" Step 1: Generate the XPRVs (Extended-Keys) and Save to environment variables")]),t._v(" "),e("blockquote",[e("p",[t._v("Create three private keys and each in their own environment variable")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export XPRV_00=$(bdk-cli key generate | jq -r '.xprv')")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export XPRV_01=$(bdk-cli key generate | jq -r '.xprv')")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export XPRV_02=$(bdk-cli key generate | jq -r '.xprv')")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/FwgUdwK.gif",alt:""}})]),t._v(" "),e("h3",{attrs:{id:"1a-verify-xprv-environment-variables-are-active"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#1a-verify-xprv-environment-variables-are-active"}},[t._v("#")]),t._v(" 1a: Verify XPRV environment variables are Active")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("env | grep XPRV")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/ZerGPbO.gif",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-2-generate-xpubs-extended-public-keys-save-to-environment-variables"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-2-generate-xpubs-extended-public-keys-save-to-environment-variables"}},[t._v("#")]),t._v(" Step 2: Generate XPUBs (Extended Public Keys) & Save to environment variables")]),t._v(" "),e("blockquote",[e("p",[t._v("Generate the three individual Public Keys aka XPUBs using our Private key and descriptor path.")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export XPUB_00=$(bdk-cli key derive --xprv $XPRV_00 --path "m/84\'/1\'/0\'/0" | jq -r ".xpub")')])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export XPUB_01=$(bdk-cli key derive --xprv $XPRV_01 --path "m/84\'/1\'/0\'/0" | jq -r ".xpub")')])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export XPUB_02=$(bdk-cli key derive --xprv $XPRV_02 --path "m/84\'/1\'/0\'/0" | jq -r ".xpub")')])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/xT3KRh4.gif",alt:""}})]),t._v(" "),e("h3",{attrs:{id:"2a-verify-xpub-environment-variables"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#2a-verify-xpub-environment-variables"}},[t._v("#")]),t._v(" 2a: Verify XPUB environment variables")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("env | grep XPUB")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/SzAip9E.gif",alt:""}})]),t._v(" "),e("hr"),t._v(" "),e("h2",{attrs:{id:"step-3-create-single-wallet-descriptors"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-3-create-single-wallet-descriptors"}},[t._v("#")]),t._v(" Step 3: Create Single-Wallet Descriptors")]),t._v(" "),e("blockquote",[e("p",[t._v("Create the wallet Descriptor for each wallet")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export DESCRIPTOR_00="$XPRV_00/84h/1h/0h/0/*"')])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export DESCRIPTOR_01="$XPRV_01/84h/1h/0h/0/*"')])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export DESCRIPTOR_02="$XPRV_02/84h/1h/0h/0/*"')])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/mFrWt6b.png",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-4-create-multi-sig-descriptor-wallets"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-4-create-multi-sig-descriptor-wallets"}},[t._v("#")]),t._v(" Step 4: Create Multi-Sig-Descriptor Wallets")]),t._v(" "),e("blockquote",[e("p",[t._v("This is how you create the 2-of-3 multi-sig output descriptor. You will need (one PrivateKey and two Xpubs) It consists of using the "),e("code",[t._v("compiler")]),t._v(" function to parse "),e("code",[t._v("policy")]),t._v(" to "),e("code",[t._v("mini-script")]),t._v(" .")])]),t._v(" "),e("ul",[e("li",[t._v("When creating the descriptor the order matters so be aware of that when following tutorial if you are for any reason changing the order of the policy.")])]),t._v(" "),e("h4",{attrs:{id:"multi-sig-wallet-0"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#multi-sig-wallet-0"}},[t._v("#")]),t._v(" Multi-Sig-Wallet 0")]),t._v(" "),e("ul",[e("li",[t._v("[ ] ▶️ "),e("code",[t._v("export MULTI_DESCRIPTOR_00=$(bdk-cli compile \"thresh(2,pk($DESCRIPTOR_00),pk($XPUB_01),pk($XPUB_02))\" | jq -r '.descriptor')")])])]),t._v(" "),e("h4",{attrs:{id:"multi-sig-wallet-1"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#multi-sig-wallet-1"}},[t._v("#")]),t._v(" Multi-Sig-Wallet 1")]),t._v(" "),e("ul",[e("li",[t._v("[ ] ▶️ "),e("code",[t._v("export MULTI_DESCRIPTOR_01=$(bdk-cli compile \"thresh(2,pk($XPUB_00),pk($DESCRIPTOR_01),pk($XPUB_02))\" | jq -r '.descriptor')")])])]),t._v(" "),e("h4",{attrs:{id:"multi-sig-wallet-2"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#multi-sig-wallet-2"}},[t._v("#")]),t._v(" Multi-Sig-Wallet 2")]),t._v(" "),e("ul",[e("li",[t._v("[ ] ▶️ "),e("code",[t._v("export MULTI_DESCRIPTOR_02=$(bdk-cli compile \"thresh(2,pk($XPUB_00),pk($XPUB_01),pk($DESCRIPTOR_02))\" | jq -r '.descriptor')")])])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/Yb8RmFS.gif",alt:""}})]),t._v(" "),e("h4",{attrs:{id:"multi-sig-2-of-3-policy-gets-compiled-to-miniscript"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#multi-sig-2-of-3-policy-gets-compiled-to-miniscript"}},[t._v("#")]),t._v(" multi-sig 2 of 3 policy gets compiled to miniscript")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# policy")]),t._v("\nthresh"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",pk"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("XPRV_A"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(",pk"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("XPUB_B"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(",pk"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("XPUB_C"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("))")]),t._v(" \n\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# miniscript")]),t._v("\nwsh"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("multi"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",XPRV_KEY,PUBKEY_B,XPUB_C"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("))")]),t._v("\n")])])]),e("hr"),t._v(" "),e("h3",{attrs:{id:"4a-verify-multi-sig-descriptor-environment-variables-are-active"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#4a-verify-multi-sig-descriptor-environment-variables-are-active"}},[t._v("#")]),t._v(" 4a: Verify Multi-Sig-Descriptor environment variables are active")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("env | grep MULTI")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/aAgtlsi.gif",alt:""}})]),t._v(" "),e("hr"),t._v(" "),e("h2",{attrs:{id:"step-5-generate-receive-address-by-using-multi-sig-descriptor-wallets"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-5-generate-receive-address-by-using-multi-sig-descriptor-wallets"}},[t._v("#")]),t._v(" Step 5: Generate Receive Address by using Multi-Sig-Descriptor Wallets")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 get_new_address")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 get_new_address")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd02 --descriptor $MULTI_DESCRIPTOR_02 get_new_address")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/w1fxPSn.gif",alt:""}})]),t._v(" "),e("p",[t._v("🔴 Did you generate the same address for all three? Good! Else, something might be incorrect.")]),t._v(" "),e("h2",{attrs:{id:"step-6-send-testnet-bitcoin-to-the-newly-created-receive-address"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-6-send-testnet-bitcoin-to-the-newly-created-receive-address"}},[t._v("#")]),t._v(" Step 6: Send Testnet Bitcoin to the newly created receive-address")]),t._v(" "),e("p",[e("a",{attrs:{href:"https://testnet-faucet.mempool.co",target:"_blank",rel:"noopener noreferrer"}},[t._v("Bitcoin Testnet Faucet link:1"),e("OutboundLink")],1),t._v(" "),e("a",{attrs:{href:"https://bitcoinfaucet.uo1.net",target:"_blank",rel:"noopener noreferrer"}},[t._v("Bitcoin Testnet Faucet link:2"),e("OutboundLink")],1)]),t._v(" "),e("h2",{attrs:{id:"step-7-sync-one-of-the-multi-sig-wallets"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-7-sync-one-of-the-multi-sig-wallets"}},[t._v("#")]),t._v(" Step 7: Sync one of the Multi-Sig Wallets")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sync")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/GuefgeI.gif",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-8-check-balance-multi-sig-wallets"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-8-check-balance-multi-sig-wallets"}},[t._v("#")]),t._v(" Step 8: Check Balance Multi-Sig Wallets")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 get_balance")])]),t._v(" "),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/zNciCqF.gif",alt:""}})]),t._v(" "),e("ul",[e("li",[t._v("Every wallet has access to sync and view balance.")])]),t._v(" "),e("h2",{attrs:{id:"step-9-check-multi-sig-policies-on-descriptor-wallet"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-9-check-multi-sig-policies-on-descriptor-wallet"}},[t._v("#")]),t._v(" Step 9: Check Multi-Sig Policies on Descriptor Wallet")]),t._v(" "),e("p",[t._v("▶️"),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 policies")])]),t._v(" "),e("p",[t._v("The output below confirms the command was successful.")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"external"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"contribution"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"conditions"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"items"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"m"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"n"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sorted"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" false,\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"type"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"PARTIAL"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"id"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"seaxtqqn"')]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"keys"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fingerprint"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"7cdf2d46"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fingerprint"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fc7870cd"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fingerprint"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"26b03333"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"satisfaction"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"items"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"m"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"n"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sorted"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" false,\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"type"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"PARTIAL"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"threshold"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"type"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"MULTISIG"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(",\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"internal"')]),e("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),t._v(" null\n"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\n")])])]),e("h3",{attrs:{id:"spendingpolicyrequired-for-complex-descriptors"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#spendingpolicyrequired-for-complex-descriptors"}},[t._v("#")]),t._v(" SpendingPolicyRequired for complex descriptors")]),t._v(" "),e("div",{staticClass:"language-shell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-shell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token parameter variable"}},[t._v("--external_policy")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"{'),e("span",{pre:!0,attrs:{class:"token entity",title:'\\"'}},[t._v('\\"')]),t._v("seaxtqqn"),e("span",{pre:!0,attrs:{class:"token entity",title:'\\"'}},[t._v('\\"')]),t._v(': [0,1]}"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("-rootnode-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("children "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#0 and #1 of root node>")]),t._v("\n")])])]),e("blockquote",[e("p",[t._v("Save the \"id\": We will need to use this ''id'' later.")])]),t._v(" "),e("p",[t._v("More info on "),e("a",{attrs:{href:"https://bitcoindevkit.org/bdk-cli/interface/",target:"_blank",rel:"noopener noreferrer"}},[t._v("external policies here"),e("OutboundLink")],1)]),t._v(" "),e("h2",{attrs:{id:"step-10-create-a-transaction-psbt"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-10-create-a-transaction-psbt"}},[t._v("#")]),t._v(" Step 10: Create a Transaction (PSBT)")]),t._v(" "),e("ul",[e("li",[t._v("1st Create a PSBT using the first wallet")]),t._v(" "),e("li",[t._v("2nd Sign the PSBT with the first wallet")]),t._v(" "),e("li",[t._v("3rd Sign PSBT with the second wallet")]),t._v(" "),e("li",[t._v("Broadcast PSBT")])]),t._v(" "),e("h3",{attrs:{id:"export-unsigned-psbt-to-environment-variable"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#export-unsigned-psbt-to-environment-variable"}},[t._v("#")]),t._v(" Export UNSIGNED_PSBT to environment variable")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v('export UNSIGNED_PSBT=$(bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 create_tx --send_all --to mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt:0 --external_policy "{\\"CHANGE_ID_HERE\\": [0,1]}" | jq -r \'.psbt\')')])]),t._v(" "),e("h3",{attrs:{id:"verify-unsigned-psbt-environment-variable"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#verify-unsigned-psbt-environment-variable"}},[t._v("#")]),t._v(" Verify UNSIGNED_PSBT environment variable")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("env | grep UNSIGNED")]),t._v(" "),e("img",{attrs:{src:"https://i.imgur.com/djHaRDq.gif",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-11-sign-the-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-11-sign-the-transaction"}},[t._v("#")]),t._v(" Step 11: SIGN the Transaction")]),t._v(" "),e("h3",{attrs:{id:"1st-wallet-signs-the-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#1st-wallet-signs-the-transaction"}},[t._v("#")]),t._v(" 1st Wallet Signs the transaction")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sign --psbt $UNSIGNED_PSBT")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export ONESIG_PSBT=$(bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sign --psbt $UNSIGNED_PSBT | jq -r '.psbt')")])]),t._v(" "),e("p",[t._v("▶️"),e("code",[t._v("env | grep ONESIG")])]),t._v(" "),e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v('{\n "is_finalized": false,\n "psbt": "cHNidP8BAFUBAAAAAdYCtva/7Rkt+fgFu3mxAdaPh4uTbgBL3HmYZgcEKWygAAAAAAD/////AQqGAQAAAAAAGXapFDRKD0jKFQ7CuQOBdmC5tosTpnAmiKwAAAAAAAEA6gIAAAAAAQFLyGFJFK884DGBM1WgskRZ6gKp/7oZ+Z30u0+wF3pZYAEAAAAA/v///wKghgEAAAAAACIAINHcOQLE6GpJ3J+FOzn/be+HApxW8sZtGqfA3TBW+NYX91hoOAAAAAAWABTPQDZx2wYYIn+ug2pZBmWBn0Tu/gJHMEQCIHu6GmRMDgPZyTx+klFMA9VujR3qDA/Y08kSkRvOaChjAiBAtExtGAYLuQ/DDJzCqLlNZ1bMB3MV+nxsLfTdI9YcYwEhA0b8lz+kt0xHfR/tjUKOc2Nt2L61pDd5vJ/lsKi8pw9MmFUjAAEBK6CGAQAAAAAAIgAg0dw5AsToakncn4U7Of9t74cCnFbyxm0ap8DdMFb41hciAgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDUgwRQIhAJdILr7G3UzYylyr2fA13MFsz/jG4+iZlKeEkX79d082AiA99UF0/uFyXBVNUmuGaxdHL7wlhzqfbgGLMREN0z/O6QEBBWlSIQIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDSEDzsDXexRPSxeXiLJoS0i2fQlOoOGHmo+Dhaeaq3oHV6YhAjGKA2Dqg+QeMICBAifYslQF2WrehLEQ0iEOpp/+eQ0NU64iBgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDRh83y1GVAAAgAEAAIAAAACAAAAAAAAAAAAiBgIxigNg6oPkHjCAgQIn2LJUBdlq3oSxENIhDqaf/nkNDRgmsDMzVAAAgAEAAIAAAACAAAAAAAAAAAAiBgPOwNd7FE9LF5eIsmhLSLZ9CU6g4Yeaj4OFp5qregdXphj8eHDNVAAAgAEAAIAAAACAAAAAAAAAAAAAAA=="\n}\n')])])]),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/0w4sK5y.gif",alt:""}})]),t._v(" "),e("h3",{attrs:{id:"2nd-wallet-signs-the-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#2nd-wallet-signs-the-transaction"}},[t._v("#")]),t._v(" 2nd Wallet Signs the transaction")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 sign --psbt $ONESIG_PSBT")])]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("export SECONDSIG_PSBT=$(bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 sign --psbt $ONESIG_PSBT | jq -r '.psbt')")])]),t._v(" "),e("p",[t._v("▶️"),e("code",[t._v("env | grep SECONDSIG")])]),t._v(" "),e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v('{\n "is_finalized": true,\n "psbt": "cHNidP8BAFUBAAAAAdYCtva/7Rkt+fgFu3mxAdaPh4uTbgBL3HmYZgcEKWygAAAAAAD/////AQqGAQAAAAAAGXapFDRKD0jKFQ7CuQOBdmC5tosTpnAmiKwAAAAAAAEA6gIAAAAAAQFLyGFJFK884DGBM1WgskRZ6gKp/7oZ+Z30u0+wF3pZYAEAAAAA/v///wKghgEAAAAAACIAINHcOQLE6GpJ3J+FOzn/be+HApxW8sZtGqfA3TBW+NYX91hoOAAAAAAWABTPQDZx2wYYIn+ug2pZBmWBn0Tu/gJHMEQCIHu6GmRMDgPZyTx+klFMA9VujR3qDA/Y08kSkRvOaChjAiBAtExtGAYLuQ/DDJzCqLlNZ1bMB3MV+nxsLfTdI9YcYwEhA0b8lz+kt0xHfR/tjUKOc2Nt2L61pDd5vJ/lsKi8pw9MmFUjAAEBK6CGAQAAAAAAIgAg0dw5AsToakncn4U7Of9t74cCnFbyxm0ap8DdMFb41hciAgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDUgwRQIhAJdILr7G3UzYylyr2fA13MFsz/jG4+iZlKeEkX79d082AiA99UF0/uFyXBVNUmuGaxdHL7wlhzqfbgGLMREN0z/O6QEiAgPOwNd7FE9LF5eIsmhLSLZ9CU6g4Yeaj4OFp5qregdXpkgwRQIhAO2aRERcublhAzToshkZRMg2I8GaE7mM2ECr0vYyuscmAiB5KK4ETlvrLqL0QbcRbGqrSwIa9lVuOqP3f5qCnGRMaQEBBWlSIQIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDSEDzsDXexRPSxeXiLJoS0i2fQlOoOGHmo+Dhaeaq3oHV6YhAjGKA2Dqg+QeMICBAifYslQF2WrehLEQ0iEOpp/+eQ0NU64iBgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDRh83y1GVAAAgAEAAIAAAACAAAAAAAAAAAAiBgIxigNg6oPkHjCAgQIn2LJUBdlq3oSxENIhDqaf/nkNDRgmsDMzVAAAgAEAAIAAAACAAAAAAAAAAAAiBgPOwNd7FE9LF5eIsmhLSLZ9CU6g4Yeaj4OFp5qregdXphj8eHDNVAAAgAEAAIAAAACAAAAAAAAAAAABBwABCP3+AAQASDBFAiEAl0guvsbdTNjKXKvZ8DXcwWzP+Mbj6JmUp4SRfv13TzYCID31QXT+4XJcFU1Sa4ZrF0cvvCWHOp9uAYsxEQ3TP87pAUgwRQIhAO2aRERcublhAzToshkZRMg2I8GaE7mM2ECr0vYyuscmAiB5KK4ETlvrLqL0QbcRbGqrSwIa9lVuOqP3f5qCnGRMaQFpUiECI1AiHZ8q+qw7bjYVTbeGQQ3L2C2sH6CW82z8sXP1jQ0hA87A13sUT0sXl4iyaEtItn0JTqDhh5qPg4Wnmqt6B1emIQIxigNg6oPkHjCAgQIn2LJUBdlq3oSxENIhDqaf/nkNDVOuAAA="\n}\n')])])]),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/OdLHnJ3.gif",alt:""}})]),t._v(" "),e("h2",{attrs:{id:"step-12-broadcast-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#step-12-broadcast-transaction"}},[t._v("#")]),t._v(" Step 12: Broadcast Transaction")]),t._v(" "),e("p",[t._v("▶️ "),e("code",[t._v("bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 broadcast --psbt $SECONDSIG_PSBT")])]),t._v(" "),e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v('{\n "txid": "61da2451874a483aa8d1d0787c7680d157639f284840de8885098cac43f6cc2f"\n}\n')])])]),e("figure",[e("img",{attrs:{src:"https://i.imgur.com/M7s0Fd6.gif",alt:""}})]),t._v(" "),e("h3",{attrs:{id:"verify-transaction"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#verify-transaction"}},[t._v("#")]),t._v(" Verify Transaction")]),t._v(" "),e("p",[t._v("Verify transcation in the memory pool on testnet "),e("a",{attrs:{href:"https://mempool.space/testnet",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mempool-testnet!"),e("OutboundLink")],1)])])}),[],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/46.5deb1292.js b/assets/js/46.5ebccb0a.js similarity index 99% rename from assets/js/46.5deb1292.js rename to assets/js/46.5ebccb0a.js index 3920b4ef57..693f2472cc 100644 --- a/assets/js/46.5deb1292.js +++ b/assets/js/46.5ebccb0a.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[46],{394:function(e,t,r){"use strict";r.r(t);var a=r(7),s=Object(a.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h2",{attrs:{id:"introduction"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#introduction"}},[e._v("#")]),e._v(" Introduction")]),e._v(" "),t("p",[e._v("I have tried to setup a 2 of 2 multi signature infrastructure with two\ndifferent wallets, which know nothing about each other, but are compliant with\ntwo very important protocols: "),t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors"),t("OutboundLink")],1),e._v(" and "),t("a",{attrs:{href:"https://en.bitcoin.it/wiki/BIP_0174",target:"_blank",rel:"noopener noreferrer"}},[e._v("Partially Signed\nBitcoin Transactions"),t("OutboundLink")],1),e._v(" described in BIP 174.")]),e._v(" "),t("p",[e._v("Before these two protocols came into existence, making a multi signature setup\nand spending from it was possible only if the involved parties were using the\nsame wallet (eg. Electrum Desktop Wallet). This limitation was due to the fact\nthat the two parties had to agree:")]),e._v(" "),t("ul",[t("li",[e._v("on the particular type of script and address to use")]),e._v(" "),t("li",[e._v("on the way the transaction would be shared composed and signed with all the\ninvolved parties.")])]),e._v(" "),t("p",[t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors"),t("OutboundLink")],1),e._v(" are a way to express which kind scriptPubKey and\naddresses to produce with a key or a series of keys.")]),e._v(" "),t("p",[t("a",{attrs:{href:"https://en.bitcoin.it/wiki/BIP_0174",target:"_blank",rel:"noopener noreferrer"}},[e._v("PSBT"),t("OutboundLink")],1),e._v(" is instead the standard protocol used to create a transaction and to enrich\nit with the necessary signatures and other components, to make it valid and complete.")]),e._v(" "),t("p",[e._v("Together they provide a common ground to create and use a multi signature\ninfrastructure in a heterogeneous environment, and this is what I have put\nto test.")]),e._v(" "),t("h2",{attrs:{id:"the-use-case"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-use-case"}},[e._v("#")]),e._v(" The use case")]),e._v(" "),t("p",[e._v("Imagine Alice and Bob owning a company and being willing to put the corporate cash\nin a 2of2 multi signature setup, so that each one of them have to agree and sign each\ntransaction.")]),e._v(" "),t("h2",{attrs:{id:"the-role-of-descriptors"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-role-of-descriptors"}},[e._v("#")]),e._v(" The role of Descriptors")]),e._v(" "),t("p",[e._v("If Alice and Bob cannot agree on the software to use, to monitor the same financial\nsituation, the two software must control and produce exactly the same series\nof multisignature addresses.")]),e._v(" "),t("p",[e._v("To make two different software produce the same addresses in a deterministic way\nwe must ensure that they:")]),e._v(" "),t("ul",[t("li",[e._v("produce the same pair of public keys")]),e._v(" "),t("li",[e._v("combine them in the same order")]),e._v(" "),t("li",[e._v("put them inside the same scriptPubKey to produce the same address")])]),e._v(" "),t("p",[e._v("Here is where the "),t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors"),t("OutboundLink")],1),e._v(" come into play. They describe:")]),e._v(" "),t("ul",[t("li",[e._v("the sequence of public keys each extended key (xpub) will produce")]),e._v(" "),t("li",[e._v("the sequence in which the new public keys of various parties will enter into\nthe script")]),e._v(" "),t("li",[e._v("the type of script the wallet will prepare with that group keys and so the type\nof address the group of keys will produce.")])]),e._v(" "),t("p",[t("strong",[e._v("By sharing the same Descriptor, every compliant wallet will derive\ndeterministically the same series of multisig addresses")]),e._v(".")]),e._v(" "),t("p",[e._v("Imagine Alice using Bitcoin Core (from now on "),t("a",{attrs:{href:"https://bitcoincore.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v('"Core"'),t("OutboundLink")],1),e._v(') as a\nWallet and Bob using a "Last generation" wallet, Bitcoin Development Kit\n(from now on '),t("a",{attrs:{href:"https://bitcoindevkit.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v('"BDK"'),t("OutboundLink")],1),e._v("), which uses descriptors and miniscript natively.")]),e._v(" "),t("p",[e._v("Each of these two software wallets should be able to:")]),e._v(" "),t("ul",[t("li",[e._v("Create a new address which is seen as belonging to the multi signature\nwallet in both software")]),e._v(" "),t("li",[e._v("Express the consent of each party by partially signing the transaction in a way\nthe other wallet can understand and complete it with its own signature.")])]),e._v(" "),t("p",[e._v("The infrastructure of multiple Extended keys combined toghether to produce\nmultiple multisignature addresses is often referred as\n"),t("em",[t("a",{attrs:{href:"https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki",target:"_blank",rel:"noopener noreferrer"}},[e._v("Hierarchical Deterministic"),t("OutboundLink")],1),e._v(" multi signature wallet or HDM")]),e._v(".")]),e._v(" "),t("p",[e._v("What follows are the steps to create the HDM usable both in Core and\nin BDK.")]),e._v(" "),t("p",[t("em",[e._v("Note: In Core, "),t("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/pull/16528",target:"_blank",rel:"noopener noreferrer"}},[e._v("Descriptor wallets"),t("OutboundLink")],1),e._v(" are still experimental and in general,\nboth wallets should be tested for descriptor capabilities only in testnet.")])]),e._v(" "),t("h2",{attrs:{id:"our-playground"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#our-playground"}},[e._v("#")]),e._v(" Our playground")]),e._v(" "),t("p",[e._v("We will build a 2of2 key set up that will be used cooperatively by Bitcoin Core\nand Bitcoin Development Kit.\nThe steps Alice and Bob will do are:")]),e._v(" "),t("ol",[t("li",[e._v("creation of the seed and the derived Extended Master Public and send it to\nthe other party")]),e._v(" "),t("li",[e._v("Create the multi signature descriptor for each wallet")]),e._v(" "),t("li",[e._v("Use each other's software to receive testnet coins from a faucet")]),e._v(" "),t("li",[e._v("return part of the coins to the faucet signing the transaction with both\nwallets.")])]),e._v(" "),t("p",[e._v("We need:")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://bitcoindevkit.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Bitcoin Dev Kit"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://bitcoincore.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Bitcoin Core"),t("OutboundLink")],1),e._v(" (v0.21.0 or later)")])]),e._v(" "),t("h3",{attrs:{id:"1-creating-the-seeds-and-the-derived-extended-public-keys"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#1-creating-the-seeds-and-the-derived-extended-public-keys"}},[e._v("#")]),e._v(" 1. Creating the seeds and the derived Extended Public keys")]),e._v(" "),t("h4",{attrs:{id:"seeds-and-extended-master-public"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#seeds-and-extended-master-public"}},[e._v("#")]),e._v(" Seeds and Extended Master Public")]),e._v(" "),t("p",[e._v("We build an Extended Private Master Key for both wallet and derive a BIP84\nExtended Master Public for Bitcoin Core and then for BDK.")]),e._v(" "),t("p",[e._v("For Bitcoin Core (Alice):")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("# new Extended wallet data\nexport core_key=$(bdk-cli key generate)\n\n# New Extended Master Private\n\nexport core_xprv=$(echo $core_key | jq -r '.xprv')\n\n# Now I derive the xpubs (one for receiving and one for the change)\n# together with informations about the derivation path to be communicated\n# to BDK wallet's owner (Bob).\n\nexport core_xpub_84_for_rec_desc=$(bdk-cli key derive --path m/84h/0h/0h/0 --xprv $core_xprv | jq -r '.xpub')\nexport core_xpub_84_for_chg_desc=$(bdk-cli key derive --path m/84h/0h/0h/1 --xprv $core_xprv | jq -r '.xpub')\n")])])]),t("p",[e._v("For BDK (Bob) we do the same:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("# new Extended wallet data\n\nexport BDK_key=$(bdk-cli key generate)\n\n# New Extended Master Private\n\nexport BDK_xprv=$(echo $BDK_key | jq -r '.xprv')\n\n# Now I build the derived xpubs to be communicated (to Alice).\n\nexport BDK_xpub_84_for_rec_desc=$(bdk-cli key derive --path m/84h/0h/0h/0 --xprv $BDK_xprv | jq -r '.xpub')\nexport BDK_xpub_84_for_chg_desc=$(bdk-cli key derive --path m/84h/0h/0h/1 --xprv $BDK_xprv | jq -r '.xpub')\n")])])]),t("h3",{attrs:{id:"2-creation-of-the-multi-signature-descriptor-for-each-wallet"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#2-creation-of-the-multi-signature-descriptor-for-each-wallet"}},[e._v("#")]),e._v(" 2. Creation of the multi signature descriptor for each wallet")]),e._v(" "),t("p",[e._v("To build a multisig wallet, each wallet owner must compose the descriptor\nadding:")]),e._v(" "),t("ul",[t("li",[e._v("his derived extended "),t("strong",[e._v("private")]),e._v(" key AND")]),e._v(" "),t("li",[e._v("all the extended "),t("strong",[e._v("public")]),e._v(" keys of the other wallets involved in the\nmulti signature setup")])]),e._v(" "),t("p",[t("em",[e._v("The different nature of the two keys (one is private and one is public) is\ndue to the fact that each wallet, to be able to partially sign the transaction,\n"),t("strong",[e._v("must manage the private key of the wallet's owner")])]),e._v(" AND have the other\nparty's public key. Otherwise, if we put both public keys, we would obtain\na watch-only wallet unable to sign the transactions. If we\nhad both extended private keys inside the descriptor, we would allow each party\nto finalize the transactions autonomously.")]),e._v(" "),t("h4",{attrs:{id:"in-bitcoin-core"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#in-bitcoin-core"}},[e._v("#")]),e._v(" In Bitcoin Core:")]),e._v(" "),t("p",[e._v("In our case, the multi signature descriptor for Bitcoin Core will be composed\nwith:")]),e._v(" "),t("ul",[t("li",[e._v("The BIP84 derived Extended "),t("strong",[e._v("Public")]),e._v(" Key from BDK")]),e._v(" "),t("li",[e._v("The BIP84 derived Extended "),t("strong",[e._v("Private")]),e._v(" Key from Core.")])]),e._v(" "),t("p",[e._v("BDK wallet's owner will send to Core's owner the derived xpub for this purpose.\nThis is how the Core's multisig descriptor will be created and put into an\nenvironment variable:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export core_rec_desc=\"wsh(multi(2,$BDK_xpub_84_for_rec_desc,$core_xprv/84'/0'/0'/0/*))\"\n")])])]),t("p",[e._v("Where of course "),t("code",[e._v("$BDK_xpub_84_for_rec_desc")]),e._v("is the derived master public created\nin BDK and received by Core's owner.")]),e._v(" "),t("p",[e._v("The meaning of what is before and after is illustrated in the doc that explain\nthe use of "),t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors in Bitcoin Core"),t("OutboundLink")],1),e._v(".")]),e._v(" "),t("p",[e._v("We add the necessary checksum using the specific "),t("code",[e._v("bitcoin-cli")]),e._v(" call.")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export core_rec_desc_chksum=$core_rec_desc#$(bitcoin-cli -testnet getdescriptorinfo $core_rec_desc | jq -r '.checksum')\n")])])]),t("p",[e._v("We repeat the same to build the descriptor to receive the change.")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export core_chg_desc=\"wsh(multi(2,$BDK_xpub_84_for_chg_desc,$core_xprv/84'/0'/0'/1/*))\"\nexport core_chg_desc_chksum=$core_chg_desc#$(bitcoin-cli -testnet getdescriptorinfo $core_chg_desc|jq -r '.checksum')\n")])])]),t("h4",{attrs:{id:"in-bdk"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#in-bdk"}},[e._v("#")]),e._v(" In BDK:")]),e._v(" "),t("p",[e._v("For BDK we set the derivation for receiving addresses and change addresses\nin the command line (maybe setting an alias)")]),e._v(" "),t("p",[e._v("Building the descriptor:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export BDK_rec_desc=\"wsh(multi(2,$BDK_xprv/84'/0'/0'/0/*,$core_xpub_84_for_rec_desc))\"`\n")])])]),t("p",[e._v("Please note that the order of the extended key in the descriptor MUST be the\nsame in the 2 wallets.")]),e._v(" "),t("p",[t("em",[e._v("We have chosen to put BDK first and in each software wallet, the public key\nderived from BDK will always come first. In alternative, we could have chosen to\nproduce the descriptor, "),t("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/pull/17056?ref=tokendaily",target:"_blank",rel:"noopener noreferrer"}},[e._v("chosing a "),t("code",[e._v("soretedmulti")]),e._v(" multisignature setup"),t("OutboundLink")],1)]),e._v(".")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export BDK_rec_desc_chksum=$BDK_rec_desc#$(bitcoin-cli -testnet getdescriptorinfo $BDK_rec_desc | jq -r '.checksum')\nexport BDK_chg_desc=\"wsh(multi(2,$BDK_xprv/84'/0'/0'/1/*,$core_xpub_84_for_chg_desc))\"\nexport BDK_chg_desc_chksum=$BDK_chg_desc#$(bitcoin-cli -testnet getdescriptorinfo $BDK_chg_desc | jq -r '.checksum')\n")])])]),t("p",[e._v("To take a look at the variables we have produced so far:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("env | grep 'core_'\nenv | grep 'BDK_'\n")])])]),t("p",[e._v("Now we will use the multisig descriptor wallet to receive testnet coins with\nAlice and Bob's software")]),e._v(" "),t("h3",{attrs:{id:"3-use-each-others-software-to-receive-testnet-coins-from-a-faucet"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#3-use-each-others-software-to-receive-testnet-coins-from-a-faucet"}},[e._v("#")]),e._v(" 3. Use each other's software to receive testnet coins from a faucet")]),e._v(" "),t("h4",{attrs:{id:"in-bitcoin-core-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#in-bitcoin-core-2"}},[e._v("#")]),e._v(" In Bitcoin Core")]),e._v(" "),t("p",[e._v('Alice must create an empty, experimental new "descriptors wallet" in Core and\nto import the multisig Output Descriptor.')]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('bitcoin-cli -testnet createwallet "multisig2of2withBDK" false true "" false true false\n')])])]),t("p",[e._v("The flag are to:")]),e._v(" "),t("ul",[t("li",[e._v("use the private keys")]),e._v(" "),t("li",[e._v("make it empty")]),e._v(" "),t("li",[e._v("no password provided to the wallet")]),e._v(" "),t("li",[e._v("reusing of addresses not allowed")]),e._v(" "),t("li",[e._v('"new experimental descriptors wallet"')]),e._v(" "),t("li",[e._v("don't load it on start up")])]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('bitcoin-cli -testnet -rpcwallet=multisig2of2withBDK importdescriptors "[{\\"desc\\":\\"$core_rec_desc_chksum\\",\\"timestamp\\":\\"now\\",\\"active\\":true,\\"internal\\":false},{\\"desc\\":\\"$core_chg_desc_chksum\\",\\"timestamp\\":\\"now\\",\\"active\\":true,\\"internal\\":true}]"\n')])])]),t("p",[e._v("Now Alice asks for her first receiving multisignature address.")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export first_address=$(bitcoin-cli -testnet -rpcwallet=multisig2of2withBDK getnewaddress)\necho $first_address\n")])])]),t("h4",{attrs:{id:"bdk"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#bdk"}},[e._v("#")]),e._v(" BDK")]),e._v(" "),t("p",[e._v("In BDK Bob can specify directly the descriptors on the command line to produce\nthe multisig address, because BDK is descriptors aware natively.")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('repl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint get_new_address`\n')])])]),t("p",[e._v('Et voilà: if we have done everything correctly, the newly created address in\nCore is the same of the newly created address in BDK. this is part of the\n"miracle" of descriptors\' interoperability.')]),e._v(" "),t("h4",{attrs:{id:"we-ask-for-testnet-coins-giving-the-first-created-address"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#we-ask-for-testnet-coins-giving-the-first-created-address"}},[e._v("#")]),e._v(" We ask for testnet coins giving the first created address.")]),e._v(" "),t("p",[e._v('To find testnet coins for free, you can just google "testnet faucet" and you\nshould find some satoshis to play with. Just give to the site your first\ngenerated address and, in twenty minutes, you will find the satoshis in\nyour balance both in Core and in BDK.')]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('# to check it in Core:\n\nbitcoin-cli -testnet -rpcwallet=multisig2of2withBDK getbalance\n\n# In BDK:\n\n# Sync with the blockchain\nrepl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint sync\n# Get the balance\nrepl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint get_balance\n')])])]),t("p",[e._v("Some testnet faucets have an address to send back the unused satoshi after\nthe use. Take note of that because we will use it in the next step.")]),e._v(" "),t("h3",{attrs:{id:"4-we-return-part-of-the-satoshis-received-back-to-the-faucet"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#4-we-return-part-of-the-satoshis-received-back-to-the-faucet"}},[e._v("#")]),e._v(" 4. we return part of the satoshis received back to the faucet")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('export psbt=$(bitcoin-cli -testnet -rpcwallet=multisig2of2withBDK walletcreatefundedpsbt "[]" "[{\\"tb1qrcesfj9f2d7x40xs6ztnlrcgxhh6vsw8658hjdhdy6qgkf6nfrds9rp79a\\":0.000012}]" | jq -r \'.psbt\')\n\nexport psbt=$(bitcoin-cli -testnet -rpcwallet=multisig2of2withBDK walletprocesspsbt $psbt | jq -r \'.psbt\')\n{\n "psbt": "cHNidP8BAIkCAAAAATj90EC+NAuXj7y6SseZJucoJM6sGnUcVm9koTveZECTAAAAAAD+////AmACAAAAAAAAIgAg98ol9j4AalD71E0mV5QV0uM6/vCT+pi2twxr/zrvLROwBAAAAAAAACIAIB4zBMipU3xqvNDQlz+PCDXvpkHH1Q95Nu0mgIsnU0jbAAAAAAABAIkCAAAAAQS+ObgGG6UwtvaO3KYph2E3/ws7Q83RbmR3rxC0fKYSAQAAAAD+////AtAHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNDAHQAAAAAAACIAIBQpiDTgPIMt0ld8cmuYqlY+EIPjvrmMqZruDhs61hQNAAAAAAEBK9AHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNAiAgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAAQEFR1IhArn3tec7n7318rnWqf0dIIwtLtfxo6Zt0HV70UvZYaWvIQNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfVKuIgYCufe15zufvfXyudap/R0gjC0u1/Gjpm3QdXvRS9lhpa8YNEw2cFQAAIAAAACAAAAAgAAAAAAAAAAAIgYDbdI+wHtIgO6pS6Ja7jai1pDwPeidKoDCytTO1jhjAH0YO/laXFQAAIAAAACAAAAAgAAAAAAAAAAAAAEBR1IhAqccvA3rL13D1K4GeWjcahDsO3P8oaVNBttk4MlCKXIcIQLHKhjmPuCQjyS77ZfaMN2tdgNKcf/+57VXGZhz/UWTl1KuIgICpxy8DesvXcPUrgZ5aNxqEOw7c/yhpU0G22TgyUIpchwYNEw2cFQAAIAAAACAAAAAgAEAAAADAAAAIgICxyoY5j7gkI8ku+2X2jDdrXYDSnH//ue1VxmYc/1Fk5cYO/laXFQAAIAAAACAAAAAgAEAAAADAAAAAAA=",\n "complete": false\n}\n')])])]),t("p",[e._v("Exactly! Note the "),t("code",[e._v('"complete": false')]),e._v(". We have processed the transaction with\nCore but we miss one of the necessary key of the multisig 2of2 setup (The one\ncontained inside BDK).")]),e._v(" "),t("p",[t("code",[e._v("tb1qrcesfj9f2d7x40xs6ztnlrcgxhh6vsw8658hjdhdy6qgkf6nfrds9rp79a")]),e._v(" is the address\nwe got from the faucet site to return the satoshis.")]),e._v(" "),t("p",[e._v("The "),t("a",{attrs:{href:"https://en.bitcoin.it/wiki/BIP_0174",target:"_blank",rel:"noopener noreferrer"}},[e._v("PSBT"),t("OutboundLink")],1),e._v(" is sent over to the BDK wallet owner who tries to sign the\ntransaction:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('repl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint sign --psbt $psbt\n{\n "is_finalized": true,\n "psbt": "cHNidP8BAIkCAAAAATj90EC+NAuXj7y6SseZJucoJM6sGnUcVm9koTveZECTAAAAAAD+////AmACAAAAAAAAIgAg98ol9j4AalD71E0mV5QV0uM6/vCT+pi2twxr/zrvLROwBAAAAAAAACIAIB4zBMipU3xqvNDQlz+PCDXvpkHH1Q95Nu0mgIsnU0jbAAAAAAABAIkCAAAAAQS+ObgGG6UwtvaO3KYph2E3/ws7Q83RbmR3rxC0fKYSAQAAAAD+////AtAHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNDAHQAAAAAAACIAIBQpiDTgPIMt0ld8cmuYqlY+EIPjvrmMqZruDhs61hQNAAAAAAEBK9AHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNAiAgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAASICArn3tec7n7318rnWqf0dIIwtLtfxo6Zt0HV70UvZYaWvRzBEAiBkVDLgVEwvENnLx+04o7gGpGjFDBwAXTJmf8Yvo35oygIgbuBkHsvPC9jmZcMZ9P+Pwp01yxSaWo+5feyPmd3ai1kBAQVHUiECufe15zufvfXyudap/R0gjC0u1/Gjpm3QdXvRS9lhpa8hA23SPsB7SIDuqUuiWu42otaQ8D3onSqAwsrUztY4YwB9Uq4iBgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfRg7+VpcVAAAgAAAAIAAAACAAAAAAAAAAAAiBgK597XnO5+99fK51qn9HSCMLS7X8aOmbdB1e9FL2WGlrxg0TDZwVAAAgAAAAIAAAACAAAAAAAAAAAABBwABCNoEAEcwRAIgZFQy4FRMLxDZy8ftOKO4BqRoxQwcAF0yZn/GL6N+aMoCIG7gZB7LzwvY5mXDGfT/j8KdNcsUmlqPuX3sj5nd2otZAUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAAUdSIQK597XnO5+99fK51qn9HSCMLS7X8aOmbdB1e9FL2WGlryEDbdI+wHtIgO6pS6Ja7jai1pDwPeidKoDCytTO1jhjAH1SrgABAUdSIQKnHLwN6y9dw9SuBnlo3GoQ7Dtz/KGlTQbbZODJQilyHCECxyoY5j7gkI8ku+2X2jDdrXYDSnH//ue1VxmYc/1Fk5dSriICAqccvA3rL13D1K4GeWjcahDsO3P8oaVNBttk4MlCKXIcGDRMNnBUAACAAAAAgAAAAIABAAAAAwAAACICAscqGOY+4JCPJLvtl9ow3a12A0px//7ntVcZmHP9RZOXGDv5WlxUAACAAAAAgAAAAIABAAAAAwAAAAAA"\n}\n')])])]),t("p",[e._v('The signature has succeded (note the "is_finalized": true,) and now we can\nbroadcast the transction.')]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('repl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint broadcast --psbt "cHNidP8BAIkCAAAAATj90EC+NAuXj7y6SseZJucoJM6sGnUcVm9koTveZECTAAAAAAD+////AmACAAAAAAAAIgAg98ol9j4AalD71E0mV5QV0uM6/vCT+pi2twxr/zrvLROwBAAAAAAAACIAIB4zBMipU3xqvNDQlz+PCDXvpkHH1Q95Nu0mgIsnU0jbAAAAAAABAIkCAAAAAQS+ObgGG6UwtvaO3KYph2E3/ws7Q83RbmR3rxC0fKYSAQAAAAD+////AtAHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNDAHQAAAAAAACIAIBQpiDTgPIMt0ld8cmuYqlY+EIPjvrmMqZruDhs61hQNAAAAAAEBK9AHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNAiAgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAASICArn3tec7n7318rnWqf0dIIwtLtfxo6Zt0HV70UvZYaWvRzBEAiBkVDLgVEwvENnLx+04o7gGpGjFDBwAXTJmf8Yvo35oygIgbuBkHsvPC9jmZcMZ9P+Pwp01yxSaWo+5feyPmd3ai1kBAQVHUiECufe15zufvfXyudap/R0gjC0u1/Gjpm3QdXvRS9lhpa8hA23SPsB7SIDuqUuiWu42otaQ8D3onSqAwsrUztY4YwB9Uq4iBgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfRg7+VpcVAAAgAAAAIAAAACAAAAAAAAAAAAiBgK597XnO5+99fK51qn9HSCMLS7X8aOmbdB1e9FL2WGlrxg0TDZwVAAAgAAAAIAAAACAAAAAAAAAAAABBwABCNoEAEcwRAIgZFQy4FRMLxDZy8ftOKO4BqRoxQwcAF0yZn/GL6N+aMoCIG7gZB7LzwvY5mXDGfT/j8KdNcsUmlqPuX3sj5nd2otZAUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAAUdSIQK597XnO5+99fK51qn9HSCMLS7X8aOmbdB1e9FL2WGlryEDbdI+wHtIgO6pS6Ja7jai1pDwPeidKoDCytTO1jhjAH1SrgABAUdSIQKnHLwN6y9dw9SuBnlo3GoQ7Dtz/KGlTQbbZODJQilyHCECxyoY5j7gkI8ku+2X2jDdrXYDSnH//ue1VxmYc/1Fk5dSriICAqccvA3rL13D1K4GeWjcahDsO3P8oaVNBttk4MlCKXIcGDRMNnBUAACAAAAAgAAAAIABAAAAAwAAACICAscqGOY+4JCPJLvtl9ow3a12A0px//7ntVcZmHP9RZOXGDv5WlxUAACAAAAAgAAAAIABAAAAAwAAAAAA"\n{\n "txid": "a0b082e3b0579822d4a0b0fa95a4c4662f6b128ffd43fdcfe53c37473ce85dee"\n}\n')])])]),t("h2",{attrs:{id:"conclusion"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#conclusion"}},[e._v("#")]),e._v(" Conclusion")]),e._v(" "),t("p",[e._v("We have built an HDM and we have used it with two indipendent wallets, which\nare compatible with "),t("a",{attrs:{href:"https://en.bitcoin.it/wiki/BIP_0174",target:"_blank",rel:"noopener noreferrer"}},[e._v("BIP 174"),t("OutboundLink")],1),e._v(" and "),t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors"),t("OutboundLink")],1),e._v(". Hopefully we\nwill see many other compatible wallets beyound "),t("a",{attrs:{href:"https://bitcoincore.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Bitcoin Core"),t("OutboundLink")],1),e._v(" and "),t("a",{attrs:{href:"https://bitcoindevkit.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("BDK"),t("OutboundLink")],1),e._v(",\nwith which we will be able to easily set up multi signature schemes.")])])}),[],!1,null,null,null);t.default=s.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[46],{401:function(e,t,r){"use strict";r.r(t);var a=r(7),s=Object(a.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h2",{attrs:{id:"introduction"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#introduction"}},[e._v("#")]),e._v(" Introduction")]),e._v(" "),t("p",[e._v("I have tried to setup a 2 of 2 multi signature infrastructure with two\ndifferent wallets, which know nothing about each other, but are compliant with\ntwo very important protocols: "),t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors"),t("OutboundLink")],1),e._v(" and "),t("a",{attrs:{href:"https://en.bitcoin.it/wiki/BIP_0174",target:"_blank",rel:"noopener noreferrer"}},[e._v("Partially Signed\nBitcoin Transactions"),t("OutboundLink")],1),e._v(" described in BIP 174.")]),e._v(" "),t("p",[e._v("Before these two protocols came into existence, making a multi signature setup\nand spending from it was possible only if the involved parties were using the\nsame wallet (eg. Electrum Desktop Wallet). This limitation was due to the fact\nthat the two parties had to agree:")]),e._v(" "),t("ul",[t("li",[e._v("on the particular type of script and address to use")]),e._v(" "),t("li",[e._v("on the way the transaction would be shared composed and signed with all the\ninvolved parties.")])]),e._v(" "),t("p",[t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors"),t("OutboundLink")],1),e._v(" are a way to express which kind scriptPubKey and\naddresses to produce with a key or a series of keys.")]),e._v(" "),t("p",[t("a",{attrs:{href:"https://en.bitcoin.it/wiki/BIP_0174",target:"_blank",rel:"noopener noreferrer"}},[e._v("PSBT"),t("OutboundLink")],1),e._v(" is instead the standard protocol used to create a transaction and to enrich\nit with the necessary signatures and other components, to make it valid and complete.")]),e._v(" "),t("p",[e._v("Together they provide a common ground to create and use a multi signature\ninfrastructure in a heterogeneous environment, and this is what I have put\nto test.")]),e._v(" "),t("h2",{attrs:{id:"the-use-case"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-use-case"}},[e._v("#")]),e._v(" The use case")]),e._v(" "),t("p",[e._v("Imagine Alice and Bob owning a company and being willing to put the corporate cash\nin a 2of2 multi signature setup, so that each one of them have to agree and sign each\ntransaction.")]),e._v(" "),t("h2",{attrs:{id:"the-role-of-descriptors"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-role-of-descriptors"}},[e._v("#")]),e._v(" The role of Descriptors")]),e._v(" "),t("p",[e._v("If Alice and Bob cannot agree on the software to use, to monitor the same financial\nsituation, the two software must control and produce exactly the same series\nof multisignature addresses.")]),e._v(" "),t("p",[e._v("To make two different software produce the same addresses in a deterministic way\nwe must ensure that they:")]),e._v(" "),t("ul",[t("li",[e._v("produce the same pair of public keys")]),e._v(" "),t("li",[e._v("combine them in the same order")]),e._v(" "),t("li",[e._v("put them inside the same scriptPubKey to produce the same address")])]),e._v(" "),t("p",[e._v("Here is where the "),t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors"),t("OutboundLink")],1),e._v(" come into play. They describe:")]),e._v(" "),t("ul",[t("li",[e._v("the sequence of public keys each extended key (xpub) will produce")]),e._v(" "),t("li",[e._v("the sequence in which the new public keys of various parties will enter into\nthe script")]),e._v(" "),t("li",[e._v("the type of script the wallet will prepare with that group keys and so the type\nof address the group of keys will produce.")])]),e._v(" "),t("p",[t("strong",[e._v("By sharing the same Descriptor, every compliant wallet will derive\ndeterministically the same series of multisig addresses")]),e._v(".")]),e._v(" "),t("p",[e._v("Imagine Alice using Bitcoin Core (from now on "),t("a",{attrs:{href:"https://bitcoincore.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v('"Core"'),t("OutboundLink")],1),e._v(') as a\nWallet and Bob using a "Last generation" wallet, Bitcoin Development Kit\n(from now on '),t("a",{attrs:{href:"https://bitcoindevkit.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v('"BDK"'),t("OutboundLink")],1),e._v("), which uses descriptors and miniscript natively.")]),e._v(" "),t("p",[e._v("Each of these two software wallets should be able to:")]),e._v(" "),t("ul",[t("li",[e._v("Create a new address which is seen as belonging to the multi signature\nwallet in both software")]),e._v(" "),t("li",[e._v("Express the consent of each party by partially signing the transaction in a way\nthe other wallet can understand and complete it with its own signature.")])]),e._v(" "),t("p",[e._v("The infrastructure of multiple Extended keys combined toghether to produce\nmultiple multisignature addresses is often referred as\n"),t("em",[t("a",{attrs:{href:"https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki",target:"_blank",rel:"noopener noreferrer"}},[e._v("Hierarchical Deterministic"),t("OutboundLink")],1),e._v(" multi signature wallet or HDM")]),e._v(".")]),e._v(" "),t("p",[e._v("What follows are the steps to create the HDM usable both in Core and\nin BDK.")]),e._v(" "),t("p",[t("em",[e._v("Note: In Core, "),t("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/pull/16528",target:"_blank",rel:"noopener noreferrer"}},[e._v("Descriptor wallets"),t("OutboundLink")],1),e._v(" are still experimental and in general,\nboth wallets should be tested for descriptor capabilities only in testnet.")])]),e._v(" "),t("h2",{attrs:{id:"our-playground"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#our-playground"}},[e._v("#")]),e._v(" Our playground")]),e._v(" "),t("p",[e._v("We will build a 2of2 key set up that will be used cooperatively by Bitcoin Core\nand Bitcoin Development Kit.\nThe steps Alice and Bob will do are:")]),e._v(" "),t("ol",[t("li",[e._v("creation of the seed and the derived Extended Master Public and send it to\nthe other party")]),e._v(" "),t("li",[e._v("Create the multi signature descriptor for each wallet")]),e._v(" "),t("li",[e._v("Use each other's software to receive testnet coins from a faucet")]),e._v(" "),t("li",[e._v("return part of the coins to the faucet signing the transaction with both\nwallets.")])]),e._v(" "),t("p",[e._v("We need:")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://bitcoindevkit.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Bitcoin Dev Kit"),t("OutboundLink")],1)]),e._v(" "),t("li",[t("a",{attrs:{href:"https://bitcoincore.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Bitcoin Core"),t("OutboundLink")],1),e._v(" (v0.21.0 or later)")])]),e._v(" "),t("h3",{attrs:{id:"1-creating-the-seeds-and-the-derived-extended-public-keys"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#1-creating-the-seeds-and-the-derived-extended-public-keys"}},[e._v("#")]),e._v(" 1. Creating the seeds and the derived Extended Public keys")]),e._v(" "),t("h4",{attrs:{id:"seeds-and-extended-master-public"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#seeds-and-extended-master-public"}},[e._v("#")]),e._v(" Seeds and Extended Master Public")]),e._v(" "),t("p",[e._v("We build an Extended Private Master Key for both wallet and derive a BIP84\nExtended Master Public for Bitcoin Core and then for BDK.")]),e._v(" "),t("p",[e._v("For Bitcoin Core (Alice):")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("# new Extended wallet data\nexport core_key=$(bdk-cli key generate)\n\n# New Extended Master Private\n\nexport core_xprv=$(echo $core_key | jq -r '.xprv')\n\n# Now I derive the xpubs (one for receiving and one for the change)\n# together with informations about the derivation path to be communicated\n# to BDK wallet's owner (Bob).\n\nexport core_xpub_84_for_rec_desc=$(bdk-cli key derive --path m/84h/0h/0h/0 --xprv $core_xprv | jq -r '.xpub')\nexport core_xpub_84_for_chg_desc=$(bdk-cli key derive --path m/84h/0h/0h/1 --xprv $core_xprv | jq -r '.xpub')\n")])])]),t("p",[e._v("For BDK (Bob) we do the same:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("# new Extended wallet data\n\nexport BDK_key=$(bdk-cli key generate)\n\n# New Extended Master Private\n\nexport BDK_xprv=$(echo $BDK_key | jq -r '.xprv')\n\n# Now I build the derived xpubs to be communicated (to Alice).\n\nexport BDK_xpub_84_for_rec_desc=$(bdk-cli key derive --path m/84h/0h/0h/0 --xprv $BDK_xprv | jq -r '.xpub')\nexport BDK_xpub_84_for_chg_desc=$(bdk-cli key derive --path m/84h/0h/0h/1 --xprv $BDK_xprv | jq -r '.xpub')\n")])])]),t("h3",{attrs:{id:"2-creation-of-the-multi-signature-descriptor-for-each-wallet"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#2-creation-of-the-multi-signature-descriptor-for-each-wallet"}},[e._v("#")]),e._v(" 2. Creation of the multi signature descriptor for each wallet")]),e._v(" "),t("p",[e._v("To build a multisig wallet, each wallet owner must compose the descriptor\nadding:")]),e._v(" "),t("ul",[t("li",[e._v("his derived extended "),t("strong",[e._v("private")]),e._v(" key AND")]),e._v(" "),t("li",[e._v("all the extended "),t("strong",[e._v("public")]),e._v(" keys of the other wallets involved in the\nmulti signature setup")])]),e._v(" "),t("p",[t("em",[e._v("The different nature of the two keys (one is private and one is public) is\ndue to the fact that each wallet, to be able to partially sign the transaction,\n"),t("strong",[e._v("must manage the private key of the wallet's owner")])]),e._v(" AND have the other\nparty's public key. Otherwise, if we put both public keys, we would obtain\na watch-only wallet unable to sign the transactions. If we\nhad both extended private keys inside the descriptor, we would allow each party\nto finalize the transactions autonomously.")]),e._v(" "),t("h4",{attrs:{id:"in-bitcoin-core"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#in-bitcoin-core"}},[e._v("#")]),e._v(" In Bitcoin Core:")]),e._v(" "),t("p",[e._v("In our case, the multi signature descriptor for Bitcoin Core will be composed\nwith:")]),e._v(" "),t("ul",[t("li",[e._v("The BIP84 derived Extended "),t("strong",[e._v("Public")]),e._v(" Key from BDK")]),e._v(" "),t("li",[e._v("The BIP84 derived Extended "),t("strong",[e._v("Private")]),e._v(" Key from Core.")])]),e._v(" "),t("p",[e._v("BDK wallet's owner will send to Core's owner the derived xpub for this purpose.\nThis is how the Core's multisig descriptor will be created and put into an\nenvironment variable:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export core_rec_desc=\"wsh(multi(2,$BDK_xpub_84_for_rec_desc,$core_xprv/84'/0'/0'/0/*))\"\n")])])]),t("p",[e._v("Where of course "),t("code",[e._v("$BDK_xpub_84_for_rec_desc")]),e._v("is the derived master public created\nin BDK and received by Core's owner.")]),e._v(" "),t("p",[e._v("The meaning of what is before and after is illustrated in the doc that explain\nthe use of "),t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors in Bitcoin Core"),t("OutboundLink")],1),e._v(".")]),e._v(" "),t("p",[e._v("We add the necessary checksum using the specific "),t("code",[e._v("bitcoin-cli")]),e._v(" call.")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export core_rec_desc_chksum=$core_rec_desc#$(bitcoin-cli -testnet getdescriptorinfo $core_rec_desc | jq -r '.checksum')\n")])])]),t("p",[e._v("We repeat the same to build the descriptor to receive the change.")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export core_chg_desc=\"wsh(multi(2,$BDK_xpub_84_for_chg_desc,$core_xprv/84'/0'/0'/1/*))\"\nexport core_chg_desc_chksum=$core_chg_desc#$(bitcoin-cli -testnet getdescriptorinfo $core_chg_desc|jq -r '.checksum')\n")])])]),t("h4",{attrs:{id:"in-bdk"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#in-bdk"}},[e._v("#")]),e._v(" In BDK:")]),e._v(" "),t("p",[e._v("For BDK we set the derivation for receiving addresses and change addresses\nin the command line (maybe setting an alias)")]),e._v(" "),t("p",[e._v("Building the descriptor:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export BDK_rec_desc=\"wsh(multi(2,$BDK_xprv/84'/0'/0'/0/*,$core_xpub_84_for_rec_desc))\"`\n")])])]),t("p",[e._v("Please note that the order of the extended key in the descriptor MUST be the\nsame in the 2 wallets.")]),e._v(" "),t("p",[t("em",[e._v("We have chosen to put BDK first and in each software wallet, the public key\nderived from BDK will always come first. In alternative, we could have chosen to\nproduce the descriptor, "),t("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/pull/17056?ref=tokendaily",target:"_blank",rel:"noopener noreferrer"}},[e._v("chosing a "),t("code",[e._v("soretedmulti")]),e._v(" multisignature setup"),t("OutboundLink")],1)]),e._v(".")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export BDK_rec_desc_chksum=$BDK_rec_desc#$(bitcoin-cli -testnet getdescriptorinfo $BDK_rec_desc | jq -r '.checksum')\nexport BDK_chg_desc=\"wsh(multi(2,$BDK_xprv/84'/0'/0'/1/*,$core_xpub_84_for_chg_desc))\"\nexport BDK_chg_desc_chksum=$BDK_chg_desc#$(bitcoin-cli -testnet getdescriptorinfo $BDK_chg_desc | jq -r '.checksum')\n")])])]),t("p",[e._v("To take a look at the variables we have produced so far:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("env | grep 'core_'\nenv | grep 'BDK_'\n")])])]),t("p",[e._v("Now we will use the multisig descriptor wallet to receive testnet coins with\nAlice and Bob's software")]),e._v(" "),t("h3",{attrs:{id:"3-use-each-others-software-to-receive-testnet-coins-from-a-faucet"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#3-use-each-others-software-to-receive-testnet-coins-from-a-faucet"}},[e._v("#")]),e._v(" 3. Use each other's software to receive testnet coins from a faucet")]),e._v(" "),t("h4",{attrs:{id:"in-bitcoin-core-2"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#in-bitcoin-core-2"}},[e._v("#")]),e._v(" In Bitcoin Core")]),e._v(" "),t("p",[e._v('Alice must create an empty, experimental new "descriptors wallet" in Core and\nto import the multisig Output Descriptor.')]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('bitcoin-cli -testnet createwallet "multisig2of2withBDK" false true "" false true false\n')])])]),t("p",[e._v("The flag are to:")]),e._v(" "),t("ul",[t("li",[e._v("use the private keys")]),e._v(" "),t("li",[e._v("make it empty")]),e._v(" "),t("li",[e._v("no password provided to the wallet")]),e._v(" "),t("li",[e._v("reusing of addresses not allowed")]),e._v(" "),t("li",[e._v('"new experimental descriptors wallet"')]),e._v(" "),t("li",[e._v("don't load it on start up")])]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('bitcoin-cli -testnet -rpcwallet=multisig2of2withBDK importdescriptors "[{\\"desc\\":\\"$core_rec_desc_chksum\\",\\"timestamp\\":\\"now\\",\\"active\\":true,\\"internal\\":false},{\\"desc\\":\\"$core_chg_desc_chksum\\",\\"timestamp\\":\\"now\\",\\"active\\":true,\\"internal\\":true}]"\n')])])]),t("p",[e._v("Now Alice asks for her first receiving multisignature address.")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("export first_address=$(bitcoin-cli -testnet -rpcwallet=multisig2of2withBDK getnewaddress)\necho $first_address\n")])])]),t("h4",{attrs:{id:"bdk"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#bdk"}},[e._v("#")]),e._v(" BDK")]),e._v(" "),t("p",[e._v("In BDK Bob can specify directly the descriptors on the command line to produce\nthe multisig address, because BDK is descriptors aware natively.")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('repl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint get_new_address`\n')])])]),t("p",[e._v('Et voilà: if we have done everything correctly, the newly created address in\nCore is the same of the newly created address in BDK. this is part of the\n"miracle" of descriptors\' interoperability.')]),e._v(" "),t("h4",{attrs:{id:"we-ask-for-testnet-coins-giving-the-first-created-address"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#we-ask-for-testnet-coins-giving-the-first-created-address"}},[e._v("#")]),e._v(" We ask for testnet coins giving the first created address.")]),e._v(" "),t("p",[e._v('To find testnet coins for free, you can just google "testnet faucet" and you\nshould find some satoshis to play with. Just give to the site your first\ngenerated address and, in twenty minutes, you will find the satoshis in\nyour balance both in Core and in BDK.')]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('# to check it in Core:\n\nbitcoin-cli -testnet -rpcwallet=multisig2of2withBDK getbalance\n\n# In BDK:\n\n# Sync with the blockchain\nrepl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint sync\n# Get the balance\nrepl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint get_balance\n')])])]),t("p",[e._v("Some testnet faucets have an address to send back the unused satoshi after\nthe use. Take note of that because we will use it in the next step.")]),e._v(" "),t("h3",{attrs:{id:"4-we-return-part-of-the-satoshis-received-back-to-the-faucet"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#4-we-return-part-of-the-satoshis-received-back-to-the-faucet"}},[e._v("#")]),e._v(" 4. we return part of the satoshis received back to the faucet")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('export psbt=$(bitcoin-cli -testnet -rpcwallet=multisig2of2withBDK walletcreatefundedpsbt "[]" "[{\\"tb1qrcesfj9f2d7x40xs6ztnlrcgxhh6vsw8658hjdhdy6qgkf6nfrds9rp79a\\":0.000012}]" | jq -r \'.psbt\')\n\nexport psbt=$(bitcoin-cli -testnet -rpcwallet=multisig2of2withBDK walletprocesspsbt $psbt | jq -r \'.psbt\')\n{\n "psbt": "cHNidP8BAIkCAAAAATj90EC+NAuXj7y6SseZJucoJM6sGnUcVm9koTveZECTAAAAAAD+////AmACAAAAAAAAIgAg98ol9j4AalD71E0mV5QV0uM6/vCT+pi2twxr/zrvLROwBAAAAAAAACIAIB4zBMipU3xqvNDQlz+PCDXvpkHH1Q95Nu0mgIsnU0jbAAAAAAABAIkCAAAAAQS+ObgGG6UwtvaO3KYph2E3/ws7Q83RbmR3rxC0fKYSAQAAAAD+////AtAHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNDAHQAAAAAAACIAIBQpiDTgPIMt0ld8cmuYqlY+EIPjvrmMqZruDhs61hQNAAAAAAEBK9AHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNAiAgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAAQEFR1IhArn3tec7n7318rnWqf0dIIwtLtfxo6Zt0HV70UvZYaWvIQNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfVKuIgYCufe15zufvfXyudap/R0gjC0u1/Gjpm3QdXvRS9lhpa8YNEw2cFQAAIAAAACAAAAAgAAAAAAAAAAAIgYDbdI+wHtIgO6pS6Ja7jai1pDwPeidKoDCytTO1jhjAH0YO/laXFQAAIAAAACAAAAAgAAAAAAAAAAAAAEBR1IhAqccvA3rL13D1K4GeWjcahDsO3P8oaVNBttk4MlCKXIcIQLHKhjmPuCQjyS77ZfaMN2tdgNKcf/+57VXGZhz/UWTl1KuIgICpxy8DesvXcPUrgZ5aNxqEOw7c/yhpU0G22TgyUIpchwYNEw2cFQAAIAAAACAAAAAgAEAAAADAAAAIgICxyoY5j7gkI8ku+2X2jDdrXYDSnH//ue1VxmYc/1Fk5cYO/laXFQAAIAAAACAAAAAgAEAAAADAAAAAAA=",\n "complete": false\n}\n')])])]),t("p",[e._v("Exactly! Note the "),t("code",[e._v('"complete": false')]),e._v(". We have processed the transaction with\nCore but we miss one of the necessary key of the multisig 2of2 setup (The one\ncontained inside BDK).")]),e._v(" "),t("p",[t("code",[e._v("tb1qrcesfj9f2d7x40xs6ztnlrcgxhh6vsw8658hjdhdy6qgkf6nfrds9rp79a")]),e._v(" is the address\nwe got from the faucet site to return the satoshis.")]),e._v(" "),t("p",[e._v("The "),t("a",{attrs:{href:"https://en.bitcoin.it/wiki/BIP_0174",target:"_blank",rel:"noopener noreferrer"}},[e._v("PSBT"),t("OutboundLink")],1),e._v(" is sent over to the BDK wallet owner who tries to sign the\ntransaction:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('repl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint sign --psbt $psbt\n{\n "is_finalized": true,\n "psbt": "cHNidP8BAIkCAAAAATj90EC+NAuXj7y6SseZJucoJM6sGnUcVm9koTveZECTAAAAAAD+////AmACAAAAAAAAIgAg98ol9j4AalD71E0mV5QV0uM6/vCT+pi2twxr/zrvLROwBAAAAAAAACIAIB4zBMipU3xqvNDQlz+PCDXvpkHH1Q95Nu0mgIsnU0jbAAAAAAABAIkCAAAAAQS+ObgGG6UwtvaO3KYph2E3/ws7Q83RbmR3rxC0fKYSAQAAAAD+////AtAHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNDAHQAAAAAAACIAIBQpiDTgPIMt0ld8cmuYqlY+EIPjvrmMqZruDhs61hQNAAAAAAEBK9AHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNAiAgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAASICArn3tec7n7318rnWqf0dIIwtLtfxo6Zt0HV70UvZYaWvRzBEAiBkVDLgVEwvENnLx+04o7gGpGjFDBwAXTJmf8Yvo35oygIgbuBkHsvPC9jmZcMZ9P+Pwp01yxSaWo+5feyPmd3ai1kBAQVHUiECufe15zufvfXyudap/R0gjC0u1/Gjpm3QdXvRS9lhpa8hA23SPsB7SIDuqUuiWu42otaQ8D3onSqAwsrUztY4YwB9Uq4iBgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfRg7+VpcVAAAgAAAAIAAAACAAAAAAAAAAAAiBgK597XnO5+99fK51qn9HSCMLS7X8aOmbdB1e9FL2WGlrxg0TDZwVAAAgAAAAIAAAACAAAAAAAAAAAABBwABCNoEAEcwRAIgZFQy4FRMLxDZy8ftOKO4BqRoxQwcAF0yZn/GL6N+aMoCIG7gZB7LzwvY5mXDGfT/j8KdNcsUmlqPuX3sj5nd2otZAUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAAUdSIQK597XnO5+99fK51qn9HSCMLS7X8aOmbdB1e9FL2WGlryEDbdI+wHtIgO6pS6Ja7jai1pDwPeidKoDCytTO1jhjAH1SrgABAUdSIQKnHLwN6y9dw9SuBnlo3GoQ7Dtz/KGlTQbbZODJQilyHCECxyoY5j7gkI8ku+2X2jDdrXYDSnH//ue1VxmYc/1Fk5dSriICAqccvA3rL13D1K4GeWjcahDsO3P8oaVNBttk4MlCKXIcGDRMNnBUAACAAAAAgAAAAIABAAAAAwAAACICAscqGOY+4JCPJLvtl9ow3a12A0px//7ntVcZmHP9RZOXGDv5WlxUAACAAAAAgAAAAIABAAAAAwAAAAAA"\n}\n')])])]),t("p",[e._v('The signature has succeded (note the "is_finalized": true,) and now we can\nbroadcast the transction.')]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v('repl -d "$BDK_rec_desc_chksum" -c "$BDK_chg_desc_chksum" -n testnet -w $BDK_fingerprint broadcast --psbt "cHNidP8BAIkCAAAAATj90EC+NAuXj7y6SseZJucoJM6sGnUcVm9koTveZECTAAAAAAD+////AmACAAAAAAAAIgAg98ol9j4AalD71E0mV5QV0uM6/vCT+pi2twxr/zrvLROwBAAAAAAAACIAIB4zBMipU3xqvNDQlz+PCDXvpkHH1Q95Nu0mgIsnU0jbAAAAAAABAIkCAAAAAQS+ObgGG6UwtvaO3KYph2E3/ws7Q83RbmR3rxC0fKYSAQAAAAD+////AtAHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNDAHQAAAAAAACIAIBQpiDTgPIMt0ld8cmuYqlY+EIPjvrmMqZruDhs61hQNAAAAAAEBK9AHAAAAAAAAIgAg6GXadcNj7k4yKUbnVlTLiedXQFXYdCBoNygop/PISNAiAgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAASICArn3tec7n7318rnWqf0dIIwtLtfxo6Zt0HV70UvZYaWvRzBEAiBkVDLgVEwvENnLx+04o7gGpGjFDBwAXTJmf8Yvo35oygIgbuBkHsvPC9jmZcMZ9P+Pwp01yxSaWo+5feyPmd3ai1kBAQVHUiECufe15zufvfXyudap/R0gjC0u1/Gjpm3QdXvRS9lhpa8hA23SPsB7SIDuqUuiWu42otaQ8D3onSqAwsrUztY4YwB9Uq4iBgNt0j7Ae0iA7qlLolruNqLWkPA96J0qgMLK1M7WOGMAfRg7+VpcVAAAgAAAAIAAAACAAAAAAAAAAAAiBgK597XnO5+99fK51qn9HSCMLS7X8aOmbdB1e9FL2WGlrxg0TDZwVAAAgAAAAIAAAACAAAAAAAAAAAABBwABCNoEAEcwRAIgZFQy4FRMLxDZy8ftOKO4BqRoxQwcAF0yZn/GL6N+aMoCIG7gZB7LzwvY5mXDGfT/j8KdNcsUmlqPuX3sj5nd2otZAUcwRAIgS6x0i1J1HRzllIPf4WlFY+Dl8kCCLK81TL2djZxTFXMCICJVBKkKNxu1w1mRVor6iFTSVXiJjmWwBXVeJLISvBwAAUdSIQK597XnO5+99fK51qn9HSCMLS7X8aOmbdB1e9FL2WGlryEDbdI+wHtIgO6pS6Ja7jai1pDwPeidKoDCytTO1jhjAH1SrgABAUdSIQKnHLwN6y9dw9SuBnlo3GoQ7Dtz/KGlTQbbZODJQilyHCECxyoY5j7gkI8ku+2X2jDdrXYDSnH//ue1VxmYc/1Fk5dSriICAqccvA3rL13D1K4GeWjcahDsO3P8oaVNBttk4MlCKXIcGDRMNnBUAACAAAAAgAAAAIABAAAAAwAAACICAscqGOY+4JCPJLvtl9ow3a12A0px//7ntVcZmHP9RZOXGDv5WlxUAACAAAAAgAAAAIABAAAAAwAAAAAA"\n{\n "txid": "a0b082e3b0579822d4a0b0fa95a4c4662f6b128ffd43fdcfe53c37473ce85dee"\n}\n')])])]),t("h2",{attrs:{id:"conclusion"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#conclusion"}},[e._v("#")]),e._v(" Conclusion")]),e._v(" "),t("p",[e._v("We have built an HDM and we have used it with two indipendent wallets, which\nare compatible with "),t("a",{attrs:{href:"https://en.bitcoin.it/wiki/BIP_0174",target:"_blank",rel:"noopener noreferrer"}},[e._v("BIP 174"),t("OutboundLink")],1),e._v(" and "),t("a",{attrs:{href:"https://bitcoinops.org/en/topics/output-script-descriptors/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Output Descriptors"),t("OutboundLink")],1),e._v(". Hopefully we\nwill see many other compatible wallets beyound "),t("a",{attrs:{href:"https://bitcoincore.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Bitcoin Core"),t("OutboundLink")],1),e._v(" and "),t("a",{attrs:{href:"https://bitcoindevkit.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("BDK"),t("OutboundLink")],1),e._v(",\nwith which we will be able to easily set up multi signature schemes.")])])}),[],!1,null,null,null);t.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/47.a9e58080.js b/assets/js/47.95bbbc17.js similarity index 99% rename from assets/js/47.a9e58080.js rename to assets/js/47.95bbbc17.js index bc732699c8..c1e622d567 100644 --- a/assets/js/47.a9e58080.js +++ b/assets/js/47.95bbbc17.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[47],{397:function(e,t,a){"use strict";a.r(t);var o=a(7),n=Object(o.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("p",[e._v("This post is part 1 of 3 of a series. ("),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-2/"}},[e._v("Part 2")]),e._v(", "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-3/"}},[e._v("Part 3")]),e._v(")")],1),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#introduction-what-is-fee-estimation"}},[e._v("Introduction: what is fee estimation?")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#the-problem"}},[e._v("The problem")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#the-challenges-and-the-solution"}},[e._v("The challenges and the solution")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#the-question"}},[e._v("The question")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#the-data-logger"}},[e._v("The data logger")])])])])]),e._v(" "),t("h2",{attrs:{id:"introduction-what-is-fee-estimation"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#introduction-what-is-fee-estimation"}},[e._v("#")]),e._v(" Introduction: what is fee estimation?")]),e._v(" "),t("p",[e._v("Fee estimation is the process of selecting the fee rate"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn1",id:"fnref1"}},[e._v("[1]")])]),e._v(" for a bitcoin transaction being created, according to two main factors:")]),e._v(" "),t("ul",[t("li",[e._v("The current congestion of the Bitcoin network.")]),e._v(" "),t("li",[e._v("The urgency, or lack thereof, for the transaction confirmation, i.e, its inclusion in a block.")])]),e._v(" "),t("p",[e._v("A fee rate should be adequate to the above factors: a fee too high would be a waste of money, because the same result could have been achieved with a lower expense. On the other hand, a fee rate too low would wait for a confirmation longer than planned or, even worse, the transaction could not be confirmed at all.")]),e._v(" "),t("h2",{attrs:{id:"the-problem"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-problem"}},[e._v("#")]),e._v(" The problem")]),e._v(" "),t("p",[e._v("Bitcoin Core offers fee estimation through the "),t("a",{attrs:{href:"https://bitcoincore.org/en/doc/0.20.0/rpc/util/estimatesmartfee/",target:"_blank",rel:"noopener noreferrer"}},[t("code",[e._v("estimatesmartfee")]),t("OutboundLink")],1),e._v(" RPC method, and there are also a lot of third-party "),t("a",{attrs:{href:"https://b10c.me/blog/003-a-list-of-public-bitcoin-feerate-estimation-apis/",target:"_blank",rel:"noopener noreferrer"}},[e._v("fee estimators"),t("OutboundLink")],1),e._v(" online, so do we need yet another estimator?")]),e._v(" "),t("p",[e._v("The model used by Bitcoin Core is not well suited for light-clients such as mobile wallets, even when running in pruned mode. Online estimators are lacking in terms of:")]),e._v(" "),t("ul",[t("li",[e._v("Privacy: Contacting the server leaks your IP (unless you are using Tor or a VPN), and the request timing may be used to correlate the request to a transaction broadcasted to the network soon thereafter.")]),e._v(" "),t("li",[e._v("Security: A malicious estimator could provide a high fee rate leading to a waste of money, or a low fee rate hampering the transaction confirmation.")])]),e._v(" "),t("p",[e._v("Replace By Fee (RBF) and Child Pays For Parent (CPFP) are techniques that can somewhat minimize the fee estimation problem, because one could simply underestimate the fee rate and then raise it when necessary, however:")]),e._v(" "),t("ul",[t("li",[e._v("RBF and CPFP may leak more information, such as patterns that may allow to detect the kind of wallet used, or which one of the transaction outputs is the change.")]),e._v(" "),t("li",[e._v('Requires additional interaction: the client must come back "online" to perform the fee bump. Sometimes this might be impractical or risky, for instance when using an offline signer or a multisignature with geographically distributed keys.')])]),e._v(" "),t("p",[e._v("Thus, this work is an effort to build a "),t("strong",[e._v("good fee estimator for purely peer to peer light clients")]),e._v(" such as "),t("a",{attrs:{href:"https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki",target:"_blank",rel:"noopener noreferrer"}},[e._v("neutrino"),t("OutboundLink")],1),e._v(" based ones, or at least determine whether the approach we take is infeasible and open the discussion\nfor other, better, models.")]),e._v(" "),t("p",[e._v("In the meantime, another sub-goal is pursued: attract the interest of data scientists; Indeed the initial step for this analysis consists in constructing a data set, which could also also help kickstart other studies on fee estimation or, more broadly, on the Bitcoin mempool.")]),e._v(" "),t("h4",{attrs:{id:"the-challenges-and-the-solution"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-challenges-and-the-solution"}},[e._v("#")]),e._v(" The challenges and the solution")]),e._v(" "),t("p",[e._v("The hardest part of doing fee estimation on a light client is the lack of information: for example, Bitcoin Core's "),t("code",[e._v("estimatesmartfee")]),e._v(" uses up to the last 1008 blocks and knows everything about the mempool"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn2",id:"fnref2"}},[e._v("[2]")])]),e._v(", such as the fee rate of every transaction it contains, but a light-client does not.")]),e._v(" "),t("p",[e._v("Also, there are other factors that may help doing fee estimation, such as the day of the week (the mempool usually empties during the "),t("a",{attrs:{href:"https://www.blockchainresearchlab.org/2020/03/30/a-week-with-bitcoin-transaction-timing-and-transaction-fees/",target:"_blank",rel:"noopener noreferrer"}},[e._v("weekend"),t("OutboundLink")],1),e._v(") or the time of the day to anticipate recurring daily events\n(such as the batch of "),t("a",{attrs:{href:"https://b10c.me/mempool-observations/2-bitmex-broadcast-13-utc/",target:"_blank",rel:"noopener noreferrer"}},[e._v("bitmex withdrawals"),t("OutboundLink")],1),e._v(").")]),e._v(" "),t("p",[e._v("The idea is to apply Machine Learning (ML) techniques"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn3",id:"fnref3"}},[e._v("[3]")])]),e._v(" to discover patterns over what a light-client knows and see if they are enough to achieve consistently good estimations.")]),e._v(" "),t("h4",{attrs:{id:"the-question"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-question"}},[e._v("#")]),e._v(" The question")]),e._v(" "),t("p",[e._v('We are going to use a DNN (Deep Neural Network), a ML technique in the supervised learning branch. The "ELI5" is: give a lot of example inputs and the desired output to a black box; if there are correlations between inputs and outputs,\nand there are enough examples, the black box will eventually start predicting the correct output even with inputs it has never seen before.')]),e._v(" "),t("p",[e._v("To define our inputs and outputs, we need to start from the question we want to answer. For a fee estimator this is:")]),e._v(" "),t("p",[t("em",[e._v('"Which minimum fee rate should I use if I want this transaction to be confirmed in at most '),t("code",[e._v("n")]),e._v(' blocks?"')])]),e._v(" "),t("p",[e._v("This can be translated to a table with many rows like:")]),e._v(" "),t("table",[t("thead",[t("tr",[t("th",[e._v("confirms_in")]),e._v(" "),t("th",[e._v("other_information")]),e._v(" "),t("th",[e._v("fee_rate")])])]),e._v(" "),t("tbody",[t("tr",[t("td",[e._v("1")]),e._v(" "),t("td",[e._v("...")]),e._v(" "),t("td",[e._v("100.34")])]),e._v(" "),t("tr",[t("td",[e._v("2")]),e._v(" "),t("td",[e._v("...")]),e._v(" "),t("td",[e._v("84.33")])]),e._v(" "),t("tr",[t("td",[e._v("10")]),e._v(" "),t("td",[e._v("...")]),e._v(" "),t("td",[e._v("44.44")])])])]),e._v(" "),t("p",[e._v("where the "),t("code",[e._v("fee_rate")]),e._v(' column is the output we want, also called the "'),t("em",[e._v("target")]),e._v('" or "'),t("em",[e._v("label")]),e._v('" in ML terminology, and the other columns are our inputs.')]),e._v(" "),t("p",[e._v("Can we build this table just by looking at the Bitcoin blockchain? Unfortunately, we can't:\nThe main thing that's missing is an indication of when the node first saw a transaction that has been later confirmed in a block. With that knowledge we can say that the fee rate of that transaction was the exact value required to confirm\nwithin the number of blocks it actually took to be confirmed. For instance, if we see transaction "),t("code",[e._v("t")]),e._v(" when the blockchain is at height "),t("code",[e._v("1000")]),e._v(" and then we notice that "),t("code",[e._v("t")]),e._v(" has been included in block "),t("code",[e._v("1006")]),e._v(", we can deduce that the\nfee rate paid by "),t("code",[e._v("t")]),e._v(" was the exact value required to get confirmed within the next "),t("code",[e._v("6")]),e._v(" blocks.")]),e._v(" "),t("p",[e._v("So to build our model, we first need to gather these data, and machine learning needs a "),t("em",[e._v("lot")]),e._v(" of data to work well.")]),e._v(" "),t("h4",{attrs:{id:"the-data-logger"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-data-logger"}},[e._v("#")]),e._v(" The data logger")]),e._v(" "),t("p",[e._v("The "),t("a",{attrs:{href:"https://github.com/RCasatta/bitcoin_logger",target:"_blank",rel:"noopener noreferrer"}},[e._v("data logger"),t("OutboundLink")],1),e._v(" is built with the purpose of collecting all the data we need, and it's MIT licensed open source software written in Rust.")]),e._v(" "),t("p",[e._v("We need to register the moment in time when transactions enter in the node's mempool; to be efficient and precise we should not only call the RPC endpoints but listen to "),t("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ZMQ"),t("OutboundLink")],1),e._v(" events. Luckily, the just released bitcoin core 0.21.0 added a new "),t("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ZMQ"),t("OutboundLink")],1),e._v(" topic "),t("code",[e._v("zmqpubsequence")]),e._v(" notifying mempool events (and block events). The logger is also listening to "),t("code",[e._v("zmqpubrawtx")]),e._v(" and "),t("code",[e._v("zmqpubrawblock")]),e._v(" topics, to make less RPC calls.")]),e._v(" "),t("p",[e._v("We are not only interested in the timestamp of the transaction entering the mempool, but also how many blocks it will take until the same transaction is confirmed.\nIn the final dataset this field is called "),t("code",[e._v("confirms_in")]),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn4",id:"fnref4"}},[e._v("[4]")])]),e._v("; if "),t("code",[e._v("confirms_in = 1")]),e._v(" it means the transaction is confirmed in the first block created after it has been seen for the first time.")]),e._v(" "),t("p",[e._v("Another critical piece of information logged by the data logger is the "),t("code",[e._v("fee_rate")]),e._v(" of the transaction, since the absolute fee value paid by a bitcoin transaction is not available nor derivable given only the transaction itself, as the inputs don't have explicit amounts.")]),e._v(" "),t("p",[e._v("All these data (apart from the time of the transaction entering in the mempool) can actually be reconstructed simply by looking at the blockchain. However, querying the bitcoin node can be fairly slow, and during the model training iterations we want to recreate the ML dataset rapidly"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn5",id:"fnref5"}},[e._v("[5]")])]),e._v(", for example whenever we need to modify or add a new field.")]),e._v(" "),t("p",[e._v("For these reasons, the logger is split into two parts: a process listening to the events sent by our node, which creates raw logs, and then a second process that uses these logs to create the final CSV dataset.\nRaw logs are self-contained: for example, they contain all the previous transaction output values for every relevant transaction. This causes some redundancy, but in this case it's better to trade some efficiency for more performance\nwhen recreating the dataset.")]),e._v(" "),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/high-level-graph.svg",alt:"High level graph"}})]),e._v(" "),t("p",[e._v("My logger instance started collecting data on the 18th of December 2020, and as of today (25th January 2020), the raw logs are about 16GB.")]),e._v(" "),t("p",[e._v("I expect (or at least hope) the raw logs, the CSV dataset, or the data logger will be useful also for other projects as well, like monitoring the propagation of transactions or other works involving raw mempool data. We will share raw logs data through torrent soon.")]),e._v(" "),t("p",[e._v("In the following "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-2/"}},[e._v("Part 2")]),e._v(" we are going to talk about the dataset.")],1),e._v(" "),t("hr",{staticClass:"footnotes-sep"}),e._v(" "),t("section",{staticClass:"footnotes"},[t("ol",{staticClass:"footnotes-list"},[t("li",{staticClass:"footnote-item",attrs:{id:"fn1"}},[t("p",[e._v("The transaction fee rate is the ratio between the absolute fee expressed in satoshi, over the weight of the transaction measured in virtual bytes. The weight of the transaction is similar to the byte size, however a part of the transaction (the segwit part) is discounted, their byte size is considered less because it creates less burden for the network. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref1"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn2"}},[t("p",[e._v("mempool is the set of transactions that are valid by consensus rules (for example, they are spending existing bitcoin), broadcasted in the bitcoin peer to peer network, but they are not yet part of the blockchain. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref2"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn3"}},[t("p",[e._v("DISCLAIMER: I am not an expert data-scientist! "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref3"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn4"}},[t("p",[e._v("Conceptually similar to bitcoin core "),t("code",[e._v("estimatesmartfee")]),e._v(' parameter called "blocks target", however, '),t("code",[e._v("confirms_in")]),e._v(" is the real value not the desired target. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref4"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn5"}},[t("p",[e._v("16GB of compressed raw logs are processed and a compressed CSV produced in about 5 minutes. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref5"}},[e._v("↩︎")])])])])])])}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[47],{395:function(e,t,a){"use strict";a.r(t);var o=a(7),n=Object(o.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("p",[e._v("This post is part 1 of 3 of a series. ("),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-2/"}},[e._v("Part 2")]),e._v(", "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-3/"}},[e._v("Part 3")]),e._v(")")],1),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#introduction-what-is-fee-estimation"}},[e._v("Introduction: what is fee estimation?")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#the-problem"}},[e._v("The problem")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#the-challenges-and-the-solution"}},[e._v("The challenges and the solution")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#the-question"}},[e._v("The question")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#the-data-logger"}},[e._v("The data logger")])])])])]),e._v(" "),t("h2",{attrs:{id:"introduction-what-is-fee-estimation"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#introduction-what-is-fee-estimation"}},[e._v("#")]),e._v(" Introduction: what is fee estimation?")]),e._v(" "),t("p",[e._v("Fee estimation is the process of selecting the fee rate"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn1",id:"fnref1"}},[e._v("[1]")])]),e._v(" for a bitcoin transaction being created, according to two main factors:")]),e._v(" "),t("ul",[t("li",[e._v("The current congestion of the Bitcoin network.")]),e._v(" "),t("li",[e._v("The urgency, or lack thereof, for the transaction confirmation, i.e, its inclusion in a block.")])]),e._v(" "),t("p",[e._v("A fee rate should be adequate to the above factors: a fee too high would be a waste of money, because the same result could have been achieved with a lower expense. On the other hand, a fee rate too low would wait for a confirmation longer than planned or, even worse, the transaction could not be confirmed at all.")]),e._v(" "),t("h2",{attrs:{id:"the-problem"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-problem"}},[e._v("#")]),e._v(" The problem")]),e._v(" "),t("p",[e._v("Bitcoin Core offers fee estimation through the "),t("a",{attrs:{href:"https://bitcoincore.org/en/doc/0.20.0/rpc/util/estimatesmartfee/",target:"_blank",rel:"noopener noreferrer"}},[t("code",[e._v("estimatesmartfee")]),t("OutboundLink")],1),e._v(" RPC method, and there are also a lot of third-party "),t("a",{attrs:{href:"https://b10c.me/blog/003-a-list-of-public-bitcoin-feerate-estimation-apis/",target:"_blank",rel:"noopener noreferrer"}},[e._v("fee estimators"),t("OutboundLink")],1),e._v(" online, so do we need yet another estimator?")]),e._v(" "),t("p",[e._v("The model used by Bitcoin Core is not well suited for light-clients such as mobile wallets, even when running in pruned mode. Online estimators are lacking in terms of:")]),e._v(" "),t("ul",[t("li",[e._v("Privacy: Contacting the server leaks your IP (unless you are using Tor or a VPN), and the request timing may be used to correlate the request to a transaction broadcasted to the network soon thereafter.")]),e._v(" "),t("li",[e._v("Security: A malicious estimator could provide a high fee rate leading to a waste of money, or a low fee rate hampering the transaction confirmation.")])]),e._v(" "),t("p",[e._v("Replace By Fee (RBF) and Child Pays For Parent (CPFP) are techniques that can somewhat minimize the fee estimation problem, because one could simply underestimate the fee rate and then raise it when necessary, however:")]),e._v(" "),t("ul",[t("li",[e._v("RBF and CPFP may leak more information, such as patterns that may allow to detect the kind of wallet used, or which one of the transaction outputs is the change.")]),e._v(" "),t("li",[e._v('Requires additional interaction: the client must come back "online" to perform the fee bump. Sometimes this might be impractical or risky, for instance when using an offline signer or a multisignature with geographically distributed keys.')])]),e._v(" "),t("p",[e._v("Thus, this work is an effort to build a "),t("strong",[e._v("good fee estimator for purely peer to peer light clients")]),e._v(" such as "),t("a",{attrs:{href:"https://github.com/bitcoin/bips/blob/master/bip-0157.mediawiki",target:"_blank",rel:"noopener noreferrer"}},[e._v("neutrino"),t("OutboundLink")],1),e._v(" based ones, or at least determine whether the approach we take is infeasible and open the discussion\nfor other, better, models.")]),e._v(" "),t("p",[e._v("In the meantime, another sub-goal is pursued: attract the interest of data scientists; Indeed the initial step for this analysis consists in constructing a data set, which could also also help kickstart other studies on fee estimation or, more broadly, on the Bitcoin mempool.")]),e._v(" "),t("h4",{attrs:{id:"the-challenges-and-the-solution"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-challenges-and-the-solution"}},[e._v("#")]),e._v(" The challenges and the solution")]),e._v(" "),t("p",[e._v("The hardest part of doing fee estimation on a light client is the lack of information: for example, Bitcoin Core's "),t("code",[e._v("estimatesmartfee")]),e._v(" uses up to the last 1008 blocks and knows everything about the mempool"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn2",id:"fnref2"}},[e._v("[2]")])]),e._v(", such as the fee rate of every transaction it contains, but a light-client does not.")]),e._v(" "),t("p",[e._v("Also, there are other factors that may help doing fee estimation, such as the day of the week (the mempool usually empties during the "),t("a",{attrs:{href:"https://www.blockchainresearchlab.org/2020/03/30/a-week-with-bitcoin-transaction-timing-and-transaction-fees/",target:"_blank",rel:"noopener noreferrer"}},[e._v("weekend"),t("OutboundLink")],1),e._v(") or the time of the day to anticipate recurring daily events\n(such as the batch of "),t("a",{attrs:{href:"https://b10c.me/mempool-observations/2-bitmex-broadcast-13-utc/",target:"_blank",rel:"noopener noreferrer"}},[e._v("bitmex withdrawals"),t("OutboundLink")],1),e._v(").")]),e._v(" "),t("p",[e._v("The idea is to apply Machine Learning (ML) techniques"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn3",id:"fnref3"}},[e._v("[3]")])]),e._v(" to discover patterns over what a light-client knows and see if they are enough to achieve consistently good estimations.")]),e._v(" "),t("h4",{attrs:{id:"the-question"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-question"}},[e._v("#")]),e._v(" The question")]),e._v(" "),t("p",[e._v('We are going to use a DNN (Deep Neural Network), a ML technique in the supervised learning branch. The "ELI5" is: give a lot of example inputs and the desired output to a black box; if there are correlations between inputs and outputs,\nand there are enough examples, the black box will eventually start predicting the correct output even with inputs it has never seen before.')]),e._v(" "),t("p",[e._v("To define our inputs and outputs, we need to start from the question we want to answer. For a fee estimator this is:")]),e._v(" "),t("p",[t("em",[e._v('"Which minimum fee rate should I use if I want this transaction to be confirmed in at most '),t("code",[e._v("n")]),e._v(' blocks?"')])]),e._v(" "),t("p",[e._v("This can be translated to a table with many rows like:")]),e._v(" "),t("table",[t("thead",[t("tr",[t("th",[e._v("confirms_in")]),e._v(" "),t("th",[e._v("other_information")]),e._v(" "),t("th",[e._v("fee_rate")])])]),e._v(" "),t("tbody",[t("tr",[t("td",[e._v("1")]),e._v(" "),t("td",[e._v("...")]),e._v(" "),t("td",[e._v("100.34")])]),e._v(" "),t("tr",[t("td",[e._v("2")]),e._v(" "),t("td",[e._v("...")]),e._v(" "),t("td",[e._v("84.33")])]),e._v(" "),t("tr",[t("td",[e._v("10")]),e._v(" "),t("td",[e._v("...")]),e._v(" "),t("td",[e._v("44.44")])])])]),e._v(" "),t("p",[e._v("where the "),t("code",[e._v("fee_rate")]),e._v(' column is the output we want, also called the "'),t("em",[e._v("target")]),e._v('" or "'),t("em",[e._v("label")]),e._v('" in ML terminology, and the other columns are our inputs.')]),e._v(" "),t("p",[e._v("Can we build this table just by looking at the Bitcoin blockchain? Unfortunately, we can't:\nThe main thing that's missing is an indication of when the node first saw a transaction that has been later confirmed in a block. With that knowledge we can say that the fee rate of that transaction was the exact value required to confirm\nwithin the number of blocks it actually took to be confirmed. For instance, if we see transaction "),t("code",[e._v("t")]),e._v(" when the blockchain is at height "),t("code",[e._v("1000")]),e._v(" and then we notice that "),t("code",[e._v("t")]),e._v(" has been included in block "),t("code",[e._v("1006")]),e._v(", we can deduce that the\nfee rate paid by "),t("code",[e._v("t")]),e._v(" was the exact value required to get confirmed within the next "),t("code",[e._v("6")]),e._v(" blocks.")]),e._v(" "),t("p",[e._v("So to build our model, we first need to gather these data, and machine learning needs a "),t("em",[e._v("lot")]),e._v(" of data to work well.")]),e._v(" "),t("h4",{attrs:{id:"the-data-logger"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-data-logger"}},[e._v("#")]),e._v(" The data logger")]),e._v(" "),t("p",[e._v("The "),t("a",{attrs:{href:"https://github.com/RCasatta/bitcoin_logger",target:"_blank",rel:"noopener noreferrer"}},[e._v("data logger"),t("OutboundLink")],1),e._v(" is built with the purpose of collecting all the data we need, and it's MIT licensed open source software written in Rust.")]),e._v(" "),t("p",[e._v("We need to register the moment in time when transactions enter in the node's mempool; to be efficient and precise we should not only call the RPC endpoints but listen to "),t("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ZMQ"),t("OutboundLink")],1),e._v(" events. Luckily, the just released bitcoin core 0.21.0 added a new "),t("a",{attrs:{href:"https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("ZMQ"),t("OutboundLink")],1),e._v(" topic "),t("code",[e._v("zmqpubsequence")]),e._v(" notifying mempool events (and block events). The logger is also listening to "),t("code",[e._v("zmqpubrawtx")]),e._v(" and "),t("code",[e._v("zmqpubrawblock")]),e._v(" topics, to make less RPC calls.")]),e._v(" "),t("p",[e._v("We are not only interested in the timestamp of the transaction entering the mempool, but also how many blocks it will take until the same transaction is confirmed.\nIn the final dataset this field is called "),t("code",[e._v("confirms_in")]),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn4",id:"fnref4"}},[e._v("[4]")])]),e._v("; if "),t("code",[e._v("confirms_in = 1")]),e._v(" it means the transaction is confirmed in the first block created after it has been seen for the first time.")]),e._v(" "),t("p",[e._v("Another critical piece of information logged by the data logger is the "),t("code",[e._v("fee_rate")]),e._v(" of the transaction, since the absolute fee value paid by a bitcoin transaction is not available nor derivable given only the transaction itself, as the inputs don't have explicit amounts.")]),e._v(" "),t("p",[e._v("All these data (apart from the time of the transaction entering in the mempool) can actually be reconstructed simply by looking at the blockchain. However, querying the bitcoin node can be fairly slow, and during the model training iterations we want to recreate the ML dataset rapidly"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn5",id:"fnref5"}},[e._v("[5]")])]),e._v(", for example whenever we need to modify or add a new field.")]),e._v(" "),t("p",[e._v("For these reasons, the logger is split into two parts: a process listening to the events sent by our node, which creates raw logs, and then a second process that uses these logs to create the final CSV dataset.\nRaw logs are self-contained: for example, they contain all the previous transaction output values for every relevant transaction. This causes some redundancy, but in this case it's better to trade some efficiency for more performance\nwhen recreating the dataset.")]),e._v(" "),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/high-level-graph.svg",alt:"High level graph"}})]),e._v(" "),t("p",[e._v("My logger instance started collecting data on the 18th of December 2020, and as of today (25th January 2020), the raw logs are about 16GB.")]),e._v(" "),t("p",[e._v("I expect (or at least hope) the raw logs, the CSV dataset, or the data logger will be useful also for other projects as well, like monitoring the propagation of transactions or other works involving raw mempool data. We will share raw logs data through torrent soon.")]),e._v(" "),t("p",[e._v("In the following "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-2/"}},[e._v("Part 2")]),e._v(" we are going to talk about the dataset.")],1),e._v(" "),t("hr",{staticClass:"footnotes-sep"}),e._v(" "),t("section",{staticClass:"footnotes"},[t("ol",{staticClass:"footnotes-list"},[t("li",{staticClass:"footnote-item",attrs:{id:"fn1"}},[t("p",[e._v("The transaction fee rate is the ratio between the absolute fee expressed in satoshi, over the weight of the transaction measured in virtual bytes. The weight of the transaction is similar to the byte size, however a part of the transaction (the segwit part) is discounted, their byte size is considered less because it creates less burden for the network. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref1"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn2"}},[t("p",[e._v("mempool is the set of transactions that are valid by consensus rules (for example, they are spending existing bitcoin), broadcasted in the bitcoin peer to peer network, but they are not yet part of the blockchain. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref2"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn3"}},[t("p",[e._v("DISCLAIMER: I am not an expert data-scientist! "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref3"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn4"}},[t("p",[e._v("Conceptually similar to bitcoin core "),t("code",[e._v("estimatesmartfee")]),e._v(' parameter called "blocks target", however, '),t("code",[e._v("confirms_in")]),e._v(" is the real value not the desired target. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref4"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn5"}},[t("p",[e._v("16GB of compressed raw logs are processed and a compressed CSV produced in about 5 minutes. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref5"}},[e._v("↩︎")])])])])])])}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/49.b70acbda.js b/assets/js/49.fd2860a9.js similarity index 99% rename from assets/js/49.b70acbda.js rename to assets/js/49.fd2860a9.js index aa155b378b..9ce7757fab 100644 --- a/assets/js/49.b70acbda.js +++ b/assets/js/49.fd2860a9.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[49],{399:function(e,t,a){"use strict";a.r(t);var n=a(7),s=Object(n.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("p",[e._v("This post is part 3 of 3 of a series. ("),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-1/"}},[e._v("Part 1")]),e._v(", "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-2/"}},[e._v("Part 2")]),e._v(")")],1),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#the-model"}},[e._v("The model")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#splitting"}},[e._v("Splitting")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#preprocessing"}},[e._v("Preprocessing")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#build"}},[e._v("Build")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#finally--training"}},[e._v("Finally, training")])])])]),e._v(" "),t("li",[t("a",{attrs:{href:"#the-prediction-phase"}},[e._v("The prediction phase")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#conclusion-and-future-development"}},[e._v("Conclusion and future development")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#acknowledgements"}},[e._v("Acknowledgements")])])]),e._v(" "),t("h2",{attrs:{id:"the-model"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-model"}},[e._v("#")]),e._v(" The model")]),e._v(" "),t("p",[e._v("The code building and training the model with "),t("a",{attrs:{href:"https://www.tensorflow.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("tensorflow"),t("OutboundLink")],1),e._v(" is available in "),t("a",{attrs:{href:"https://colab.research.google.com/drive/1yamwh8nE4NhmGButep-pfUT-1uRKs49a?usp=sharing",target:"_blank",rel:"noopener noreferrer"}},[e._v("google colab notebook"),t("OutboundLink")],1),e._v(" (jupyter notebook); you can also download the file as plain python and run it locally. At least 1 hour is needed to train the full model, but it heavily depends on the hardware available.")]),e._v(" "),t("p",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-confirms_in-fee_rate.png",alt:"graph confirm_in blocks vs fee_rate"}})]),t("div",{attrs:{align:"center"}},[e._v("Do you want to choose the fee without a model? In the last 5 weeks a ~50 sat/vbyte transaction never took more than a day to confirm and a ~10 sat/vbyte never took more than a week")]),t("br"),t("p"),e._v(" "),t("p",[e._v("As a reference, in the code we have a calculation of the bitcoin core "),t("code",[e._v("estimatesmartfee")]),e._v(" MAE"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn1",id:"fnref1"}},[e._v("[1]")])]),e._v(" and drift"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn2",id:"fnref2"}},[e._v("[2]")])]),e._v(".\nMAE is computed as "),t("code",[e._v("avg(abs(fee_rate - core_econ))")]),e._v(" when "),t("code",[e._v("core_econ")]),e._v(" is available (about 1.2M observations, sometime the value is not available when considered too old).")]),e._v(" "),t("table",[t("thead",[t("tr",[t("th",[e._v("estimatesmartfee mode")]),e._v(" "),t("th",[e._v("MAE [satoshi/vbytes]")]),e._v(" "),t("th",[e._v("drift")])])]),e._v(" "),t("tbody",[t("tr",[t("td",[e._v("economic")]),e._v(" "),t("td",[e._v("28.77")]),e._v(" "),t("td",[e._v("20.79")])]),e._v(" "),t("tr",[t("td",[e._v("conservative")]),e._v(" "),t("td",[e._v("46.49")]),e._v(" "),t("td",[e._v("44.73")])])])]),e._v(" "),t("p",[e._v("As seen from the table, the error is quite high, but the positive drift suggests "),t("code",[e._v("estimatesmartfee")]),e._v(" prefers to overestimate to avoid transactions not confirming.")]),e._v(" "),t("p",[e._v('As we said in the introduction, network traffic is correlated with time and we have the timestamp of when the transaction has been first seen, however a ML model doesn\'t like plain numbers too much, but it behaves better with "number that repeats", like categories, so we are converting the timestamp in '),t("code",[e._v("day_of_week")]),e._v(" a number from 0 to 6, and "),t("code",[e._v("hours")]),e._v(" a number from 0 to 24.")]),e._v(" "),t("h4",{attrs:{id:"splitting"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#splitting"}},[e._v("#")]),e._v(" Splitting")]),e._v(" "),t("p",[e._v("The dataset is splitted in training and test data with a 80/20 proportion. As the name suggest the training part is used to train the model, the test is composed of other observations to test if the model is good with observations that has never seen (proving the model can generalize, not just memorizing the answer).")]),e._v(" "),t("p",[e._v("During the training the data is splitted again in 80/20 for training and validation respectively, validation is basically testing during training.")]),e._v(" "),t("p",[e._v("During splitting, the dataset is converted from a pandas data frame to tensorflow dataset, decreasing training times.")]),e._v(" "),t("h4",{attrs:{id:"preprocessing"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#preprocessing"}},[e._v("#")]),e._v(" Preprocessing")]),e._v(" "),t("p",[e._v("The preprocessing phase is part of the model however it contains transformations without parameters trained by the model.\nThis transformations are useful because model trains better if data are in some format, and having this phase inside the model helps to avoid to prepare the data before feeding the model at prediction phase.")]),e._v(" "),t("p",[e._v("Our model performs 2 kind of preprocessing:")]),e._v(" "),t("ul",[t("li",[t("p",[e._v("Normalization: model trains faster if numerical features have mean 0 and standard deviation equal to 1, so this layer is built by computing the "),t("code",[e._v("mean")]),e._v(" and "),t("code",[e._v("std")]),e._v(" from the series of a feature before training, and the model is feed with "),t("code",[e._v("(feature - mean)/std")]),e._v(". Our model normalize "),t("code",[e._v("confirms_in")]),e._v(" feature and all the buckets "),t("code",[e._v("a*")])])]),e._v(" "),t("li",[t("p",[e._v("one-hot vector: Numerical categories having a small number of different unique values like our "),t("code",[e._v("day_of_week")]),e._v(" and "),t("code",[e._v("hours")]),e._v(" could be trained better/faster by being converted in one hot vector. For example "),t("code",[e._v("day_of_week=6")]),e._v(" (Sunday) is converted in a vector "),t("code",[e._v("['0', '0', '0', '0', '0', '0', '1']")]),e._v(" while "),t("code",[e._v("day_of_week=5")]),e._v(" (Saturday) is converted in the vector "),t("code",[e._v("['0', '0', '0', '0', '0', '1', '0']")])])])]),e._v(" "),t("h4",{attrs:{id:"build"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#build"}},[e._v("#")]),e._v(" Build")]),e._v(" "),t("div",{staticClass:"language-python extra-class"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[e._v("all_features "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("layers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("concatenate"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("encoded_features"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\nx "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("layers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Dense"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" activation"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[e._v('"relu"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("all_features"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\nx "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("layers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Dense"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" activation"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[e._v('"relu"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("x"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\noutput "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("layers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Dense"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" activation"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v("clip"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("x"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\nmodel "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Model"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("all_inputs"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" output"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\noptimizer "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("optimizers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Adam"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("learning_rate"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("0.01")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\nmodel"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),t("span",{pre:!0,attrs:{class:"token builtin"}},[e._v("compile")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("loss"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[e._v("'mse'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v("\n optimizer"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v("optimizer"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v("\n metrics"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[e._v("'mae'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[e._v("'mse'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n")])])]),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-model.png",alt:"model graph"}})]),e._v(" "),t("p",[e._v("The model is fed with the "),t("code",[e._v("encoded_features")]),e._v(" coming from the processing phase, then there are 2 layers with 64 neurons each followed by one neuron giving the "),t("code",[e._v("fee_rate")]),e._v(" as output.")]),e._v(" "),t("p",[e._v("With this configurations the model has:")]),e._v(" "),t("ul",[t("li",[e._v("Total params: "),t("code",[e._v("7,412")])]),e._v(" "),t("li",[e._v("Trainable params: "),t("code",[e._v("7,361")])]),e._v(" "),t("li",[e._v("Non-trainable params: "),t("code",[e._v("51")])])]),e._v(" "),t("p",[e._v("Non-trainable params comes from the normalization layer and are computed in the pre-processing phase (it contains, for example, the mean of a series). Trainable parameters are values initialized randomly and changed during the training phase. The trainable parameters are "),t("code",[e._v("7,361")]),e._v(", this number comes from the following, every neuron has an associated bias and a weight for every element in the inputs, thus:")]),e._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("48")]),e._v(" input_values_weights + "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("1")]),e._v(" bias"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v(" * "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),e._v(" first_layer_neurons"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n+ "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),e._v(" input_values_weights + "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("1")]),e._v(" bias"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v(" * "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),e._v(" second layer neurons"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n+ "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),e._v(" input values weights + "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("1")]),e._v(" bias"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n\n"),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("49")]),e._v("*64+65*64+65 "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("7361")]),e._v("\n")])])]),t("p",[e._v("Honestly, neural network parameters are mostly the one taken from this tensorflow "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/keras/regression",target:"_blank",rel:"noopener noreferrer"}},[e._v("example"),t("OutboundLink")],1),e._v(", we even tried to "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/keras/keras_tuner",target:"_blank",rel:"noopener noreferrer"}},[e._v("tune hyperparameters"),t("OutboundLink")],1),e._v(", however, we decided to follow this "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/keras/overfit_and_underfit#demonstrate_overfitting",target:"_blank",rel:"noopener noreferrer"}},[e._v("advice"),t("OutboundLink")],1),e._v(": "),t("em",[e._v('"The simplest way to prevent overfitting is to start with a small model:"')]),e._v(". We hope this work will attract other data scientists to this bitcoin problem, improving the model. We also think that a longer time for the data collection is needed to capture various situations.")]),e._v(" "),t("p",[e._v("A significant part of a ML model are the activation functions, "),t("code",[e._v("relu")]),e._v(" (Rectified Linear Unit) is one of the most used lately, because it's simple and works well as we learned in this "),t("a",{attrs:{href:"https://youtu.be/aircAruvnKk?t=1035",target:"_blank",rel:"noopener noreferrer"}},[e._v("introducing neural network video"),t("OutboundLink")],1),e._v(". "),t("code",[e._v("relu")]),e._v(" it's equal to zero for negative values and equal to the input for positive values. Being non-linear allows the whole model to be non-linear.")]),e._v(" "),t("p",[e._v("For the last layer it is different: we want to enforce a minimum for the output, which is the minimum relay fee "),t("code",[e._v("1.0")]),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn3",id:"fnref3"}},[e._v("[3]")])]),e._v(". One could not simply cut the output of the model after prediction because all the training would not consider this constraint. So we need to build a custom activation function that the model training will be able to use for the "),t("a",{attrs:{href:"https://en.wikipedia.org/wiki/Gradient_descent#:~:text=Gradient%20descent%20is%20a%20first,the%20direction%20of%20steepest%20descent.",target:"_blank",rel:"noopener noreferrer"}},[e._v("gradient descent"),t("OutboundLink")],1),e._v(" optimization step. Luckily this is very simple using tensorflow primitives:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("def clip(x):\n min = tf.constant(1.0)\n return tf.where(tf.less(x, min), min, x)\n")])])]),t("p",[e._v("Another important part is the optimizer, when we first read the aforementioned "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/keras/regression",target:"_blank",rel:"noopener noreferrer"}},[e._v("example"),t("OutboundLink")],1),e._v(" the optimizer used was "),t("code",[e._v("RMSProp")]),e._v(" however the example updated lately and we noticed the optimizer changed in favor of "),t("code",[e._v("Adam")]),e._v(" which we read is the "),t("a",{attrs:{href:"https://towardsdatascience.com/adam-latest-trends-in-deep-learning-optimization-6be9a291375c",target:"_blank",rel:"noopener noreferrer"}},[e._v("latest trend"),t("OutboundLink")],1),e._v(" in data science. We changed the model to use "),t("code",[e._v("Adam")]),e._v(" and effectively the training is faster with "),t("code",[e._v("Adam")]),e._v(" and even slightly lower error is achieved.\nAnother important parameter is the learning rate, which we set to "),t("code",[e._v("0.01")]),e._v(" after manual trials; however there might be space for improvements such as using "),t("a",{attrs:{href:"https://www.tensorflow.org/api_docs/python/tf/compat/v1/train/exponential_decay",target:"_blank",rel:"noopener noreferrer"}},[e._v("exponential decay"),t("OutboundLink")],1),e._v(", starting with an high learning rate and decreasing it through training epochs.")]),e._v(" "),t("p",[e._v('The last part of the model configuration is the loss function: the objective of the training is to find the minimum of this function. Usually for regression problem (the ones having a number as output, not a category) the most used is the Mean squared error (MSE). MSE is measured as the average of squared difference between predictions and actual observations, giving larger penalties to large difference because of the square. An interesting property is that the bigger the error the faster the changes is good at the beginning of the training, while slowing down when the model predicts better is desirable to avoid "jumping out" the local minimum.')]),e._v(" "),t("h4",{attrs:{id:"finally-the-model-training"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#finally-the-model-training"}},[e._v("#")]),e._v(" Finally, the model training")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("PATIENCE = 20\nMAX_EPOCHS = 200\n\ndef train():\n early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=PATIENCE)\n history = model.fit(train_ds, epochs=MAX_EPOCHS, validation_data=val_ds, verbose=1, callbacks=[early_stop])\n return history\n\nhistory = train()\n")])])]),t("p",[e._v("This steps is the core of the neural network, it takes a while, let's analyze the output:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("Epoch 1/200\n7559/7559 [==============================] - 34s 3ms/step - loss: 547.8023 - mae: 16.9547 - mse: 547.8023 - val_loss: 300.5965 - val_ma\ne: 11.9202 - val_mse: 300.5965\n...\nEpoch 158/200\n7559/7559 [==============================] - 31s 3ms/step - loss: 163.2548 - mae: 8.3126 - mse: 163.2548 - val_loss: 164.8296 - val_mae: 8.3402 - val_mse: 164.8296\n")])])]),t("p",[e._v("Training is done in epochs, under every epoch all the training data is iterated and model parameters updated to minimize the loss function.")]),e._v(" "),t("p",[e._v("The number "),t("code",[e._v("7559")]),e._v(" represent the number of steps. Theoretically the whole training data should be processed at once and parameters updated accordingly, however in practice this is infeasible for example for memory resource, thus the training happens in batch. In our case we have "),t("code",[e._v("1,934,999")]),e._v(" observations in the training set and our batch size is "),t("code",[e._v("256")]),e._v(", thus we have "),t("code",[e._v("1,437,841/256=7,558.58")]),e._v(" which by excess result in "),t("code",[e._v("7559")]),e._v(" steps.")]),e._v(" "),t("p",[e._v("The "),t("code",[e._v("~31s")]),e._v(" is the time it takes to process the epoch on a threadripper CPU but GPU or TPU could do better.")]),e._v(" "),t("p",[e._v("The value "),t("code",[e._v("loss")]),e._v(" is the MSE on the training data while "),t("code",[e._v("val_loss")]),e._v(" is the MSE value on the validation data. As far as we understand the separated validation data helps to detect the machine learning enemy, overfitting. Because in case of overfitting the value "),t("code",[e._v("loss")]),e._v(" continue to improve (almost indefinitely) while "),t("code",[e._v("val_loss")]),e._v(" start improving with the loss but a certain point diverge, indicating the network is memorizing the training data to improve "),t("code",[e._v("loss")]),e._v(" but in doing so losing generalizing capabilities.")]),e._v(" "),t("p",[e._v("Our model doesn't look to suffer overfitting cause "),t("code",[e._v("loss")]),e._v(" and "),t("code",[e._v("val_loss")]),e._v(" doesn't diverge during training")]),e._v(" "),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-train-history.png",alt:"train history"}})]),e._v(" "),t("p",[e._v("While we told the training to do 200 epochs, the training stopped at 158 because we added an "),t("code",[e._v("early_stop")]),e._v(" call back with "),t("code",[e._v("20")]),e._v(" as "),t("code",[e._v("PATIENCE")]),e._v(", meaning that after 20 epoch and no improvement in "),t("code",[e._v("val_loss")]),e._v(" the training is halted, saving time and potentially avoiding overfitting.")]),e._v(" "),t("h2",{attrs:{id:"the-prediction-phase"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-prediction-phase"}},[e._v("#")]),e._v(" The prediction phase")]),e._v(" "),t("p",[e._v("A "),t("a",{attrs:{href:"https://github.com/RCasatta/estimate_ml_fee",target:"_blank",rel:"noopener noreferrer"}},[e._v("prediction test tool"),t("OutboundLink")],1),e._v(" is available on github. At the moment it uses a bitcoin core node to provide network data for simplicity, but it queries it only for the mempool and the last 6 blocks, so it's something that also a light-client could do"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn4",id:"fnref4"}},[e._v("[4]")])]),e._v(".")]),e._v(" "),t("p",[e._v("The following chart is probably the best visualization to evaluate the model, on the x axis there is the real fee rate while on the y axis there is the prediction, the more the points are centered on the bisection, the more the model is good.\nWe can see the model is doing quite well, the MAE is 8 which is way lower than "),t("code",[e._v("estimatesmartfee")]),e._v(". However, there are big errors some times, in particular for prediction for fast confirmation ("),t("code",[e._v("confirms_in=1 or confirms_in=2")]),e._v(") as shown by the orange points. Creating a model only for blocks target greater than 2 instead of simply remove some observations may be an option.")]),e._v(" "),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-true-and-predictions.png",alt:"prediction results"}})]),e._v(" "),t("p",[e._v("The following chart is instead a distribution of the errors, which for good model should resemble the normal distribution centered in 0, and it loooks like the model is respecting that.")]),e._v(" "),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-error-distribution.png",alt:"error distribution"}})]),e._v(" "),t("h2",{attrs:{id:"conclusion-and-future-development"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#conclusion-and-future-development"}},[e._v("#")]),e._v(" Conclusion and future development")]),e._v(" "),t("p",[e._v("The results have shown deep neural network are a tool capable of good bitcoin transaction fee estimations; this suggests that further ML research in this area might be promising.")]),e._v(" "),t("p",[e._v("This is just a starting point, there are many future improvements such as:")]),e._v(" "),t("ul",[t("li",[e._v("Build a separate model with full knowledge, thus for full, always-connected nodes could be interesting and improve network resource allocation with respect to current estimators.")]),e._v(" "),t("li",[e._v("Tensorflow is a huge dependency, and since it contains all the feature to build and train a model, most of the feature are not needed in the prediction phase. In fact tensorflow lite exists which is specifically created for embedded and mobile devices; the "),t("a",{attrs:{href:"https://github.com/RCasatta/estimate_ml_fee",target:"_blank",rel:"noopener noreferrer"}},[e._v("prediction test tool"),t("OutboundLink")],1),e._v(" and the final integration in "),t("a",{attrs:{href:"https://github.com/bitcoindevkit/bdk",target:"_blank",rel:"noopener noreferrer"}},[e._v("bdk"),t("OutboundLink")],1),e._v(" should use it.")]),e._v(" "),t("li",[e._v("Explore other fields to improve model predictions such as:\n"),t("ul",[t("li",[e._v("A bucket array of the transactions in the last 6 blocks with known fee rates. This should in particular help estimations with almost empty mempool.")]),e._v(" "),t("li",[e._v("Transaction weight")]),e._v(" "),t("li",[e._v("Time from last block")])])]),e._v(" "),t("li",[e._v("Some fields are very important and could benefit from pre-processing expansion, for instance applying "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/structured_data/feature_columns#hashed_feature_columns",target:"_blank",rel:"noopener noreferrer"}},[e._v("hashed feature columns"),t("OutboundLink")],1),e._v(" to "),t("code",[e._v("confirms_in")]),e._v(".")]),e._v(" "),t("li",[e._v("Bitcoin logger could be improved by a merge command to unify raw logs files, reducing redundancy and consequently disk occupation.")]),e._v(" "),t("li",[e._v("The dataset could be created in multiple files to allow more parallelism and use less memory during training.")]),e._v(" "),t("li",[e._v("Saving the dataset in "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/load_data/tfrecord",target:"_blank",rel:"noopener noreferrer"}},[e._v("TFRecord format"),t("OutboundLink")],1),e._v(" instead of CSV")]),e._v(" "),t("li",[e._v("At the moment we are training the model on a threadripper CPU, training the code on GPU or even TPU will be needed to decrease training time, especially because input data will grow.")]),e._v(" "),t("li",[e._v("The "),t("a",{attrs:{href:"https://github.com/RCasatta/estimate_ml_fee",target:"_blank",rel:"noopener noreferrer"}},[e._v("prediction test tool"),t("OutboundLink")],1),e._v(" should estimate only using the p2p bitcoin network, without requiring a node. This work would be propedeutic for "),t("a",{attrs:{href:"https://github.com/bitcoindevkit/bdk",target:"_blank",rel:"noopener noreferrer"}},[e._v("bdk"),t("OutboundLink")],1),e._v(" integration")]),e._v(" "),t("li",[e._v("At the moment mempool buckets are multiple inputs "),t("code",[e._v("a*")]),e._v(" as show in the model graph; since they are related, is it possible to merge them in one TensorArray?")]),e._v(" "),t("li",[e._v("Sometimes the model does not learn and "),t("a",{attrs:{href:"https://github.com/RCasatta/bitcoin_logger/blob/master/notes.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("gets stuck"),t("OutboundLink")],1),e._v(". The reason is the "),t("code",[e._v("clip")]),e._v(" function applied in the last layer is constant for a value lower than 1. In this case, the derivative is 0 and the gradient descent doesn't know where to go. Instead of using the "),t("code",[e._v("clip")]),e._v(" function apply penalties to the loss function for values lower than 1.")]),e._v(" "),t("li",[e._v("There are issues regarding dead neurons (going to 0) or neurons with big weight, weight results should be monitored for this events, and also weight decay and L2 regularization should be explored.")]),e._v(" "),t("li",[e._v("Tune hyper-parameters technique should be re-tested.")]),e._v(" "),t("li",[e._v("Predictions should be monotonic decreasing for growing "),t("code",[e._v("confirms_in")]),e._v(" parameter; for obvious reason it doesn't make sense that an higher fee rate will result in a higher confirmation time. But since this is not enforced anywhere in the model, at the moment this could happen.")]),e._v(" "),t("li",[e._v("Since nodes with bloom filter disabled doesn't serve the mempool anymore, a model based only on last blocks should be evaluated.")])]),e._v(" "),t("h2",{attrs:{id:"acknowledgements"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#acknowledgements"}},[e._v("#")]),e._v(" Acknowledgements")]),e._v(" "),t("p",[e._v("Thanks to "),t("a",{attrs:{href:"https://squarecrypto.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Square crypto"),t("OutboundLink")],1),e._v(" for sponsoring this work and thanks to the reviewers: "),t("a",{attrs:{href:"https://twitter.com/LeoComandini",target:"_blank",rel:"noopener noreferrer"}},[e._v("Leonardo Comandini"),t("OutboundLink")],1),e._v(", "),t("a",{attrs:{href:"https://twitter.com/domegabri",target:"_blank",rel:"noopener noreferrer"}},[e._v("Domenico Gabriele"),t("OutboundLink")],1),e._v(", "),t("a",{attrs:{href:"https://twitter.com/afilini",target:"_blank",rel:"noopener noreferrer"}},[e._v("Alekos Filini"),t("OutboundLink")],1),e._v(", "),t("a",{attrs:{href:"https://twitter.com/Ferdinando1970",target:"_blank",rel:"noopener noreferrer"}},[e._v("Ferdinando Ametrano"),t("OutboundLink")],1),e._v(".")]),e._v(" "),t("p",[e._v("And also this tweet that remembered me "),t("a",{attrs:{href:"https://twitter.com/RCasatta",target:"_blank",rel:"noopener noreferrer"}},[e._v("I"),t("OutboundLink")],1),e._v(" had this work in my TODO list")]),e._v(" "),t("blockquote",{staticClass:"twitter-tweet"},[t("p",{attrs:{lang:"en",dir:"ltr"}},[e._v("I don't understand Machine Learning(ML), but is it horrible to use ML to predict bitcoin fees? "),t("br"),t("br"),e._v('I have heard tales of this "Deep Learning" thing where you throw a bunch of data at it and it gives you good results with high accuracy.')]),e._v("— sanket1729 (@sanket1729) "),t("a",{attrs:{href:"https://twitter.com/sanket1729/status/1336624662365822977?ref_src=twsrc%5Etfw"}},[e._v("December 9, 2020")])]),e._v(" "),t("script",{attrs:{async:"",src:"https://platform.twitter.com/widgets.js",charset:"utf-8"}}),e._v(" "),t("p",[e._v("This is the final part of the series. In the previous "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-1/"}},[e._v("Part 1")]),e._v(" we talked about the problem and in "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-2/"}},[e._v("Part 2")]),e._v(" we talked about the dataset.")],1),e._v(" "),t("hr",{staticClass:"footnotes-sep"}),e._v(" "),t("section",{staticClass:"footnotes"},[t("ol",{staticClass:"footnotes-list"},[t("li",{staticClass:"footnote-item",attrs:{id:"fn1"}},[t("p",[e._v("MAE is Mean Absolute Error, which is the average of the series built by the absolute difference between the real value and the estimation. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref1"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn2"}},[t("p",[e._v("drift like MAE, but without the absolute value "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref2"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn3"}},[t("p",[e._v("Most node won't relay transactions with fee lower than the min relay fee, which has a default of "),t("code",[e._v("1.0")]),e._v(" "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref3"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn4"}},[t("p",[e._v("An important issue emerged after the article came out, a bitcoin core client with bloom filter disabled (default as of 0.21) doesn't serve the mempool via p2p. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref4"}},[e._v("↩︎")])])])])])])}),[],!1,null,null,null);t.default=s.exports}}]); \ No newline at end of file +(window.webpackJsonp=window.webpackJsonp||[]).push([[49],{397:function(e,t,a){"use strict";a.r(t);var n=a(7),s=Object(n.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("p",[e._v("This post is part 3 of 3 of a series. ("),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-1/"}},[e._v("Part 1")]),e._v(", "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-2/"}},[e._v("Part 2")]),e._v(")")],1),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#the-model"}},[e._v("The model")]),e._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"#splitting"}},[e._v("Splitting")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#preprocessing"}},[e._v("Preprocessing")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#build"}},[e._v("Build")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#finally--training"}},[e._v("Finally, training")])])])]),e._v(" "),t("li",[t("a",{attrs:{href:"#the-prediction-phase"}},[e._v("The prediction phase")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#conclusion-and-future-development"}},[e._v("Conclusion and future development")])]),e._v(" "),t("li",[t("a",{attrs:{href:"#acknowledgements"}},[e._v("Acknowledgements")])])]),e._v(" "),t("h2",{attrs:{id:"the-model"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-model"}},[e._v("#")]),e._v(" The model")]),e._v(" "),t("p",[e._v("The code building and training the model with "),t("a",{attrs:{href:"https://www.tensorflow.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("tensorflow"),t("OutboundLink")],1),e._v(" is available in "),t("a",{attrs:{href:"https://colab.research.google.com/drive/1yamwh8nE4NhmGButep-pfUT-1uRKs49a?usp=sharing",target:"_blank",rel:"noopener noreferrer"}},[e._v("google colab notebook"),t("OutboundLink")],1),e._v(" (jupyter notebook); you can also download the file as plain python and run it locally. At least 1 hour is needed to train the full model, but it heavily depends on the hardware available.")]),e._v(" "),t("p",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-confirms_in-fee_rate.png",alt:"graph confirm_in blocks vs fee_rate"}})]),t("div",{attrs:{align:"center"}},[e._v("Do you want to choose the fee without a model? In the last 5 weeks a ~50 sat/vbyte transaction never took more than a day to confirm and a ~10 sat/vbyte never took more than a week")]),t("br"),t("p"),e._v(" "),t("p",[e._v("As a reference, in the code we have a calculation of the bitcoin core "),t("code",[e._v("estimatesmartfee")]),e._v(" MAE"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn1",id:"fnref1"}},[e._v("[1]")])]),e._v(" and drift"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn2",id:"fnref2"}},[e._v("[2]")])]),e._v(".\nMAE is computed as "),t("code",[e._v("avg(abs(fee_rate - core_econ))")]),e._v(" when "),t("code",[e._v("core_econ")]),e._v(" is available (about 1.2M observations, sometime the value is not available when considered too old).")]),e._v(" "),t("table",[t("thead",[t("tr",[t("th",[e._v("estimatesmartfee mode")]),e._v(" "),t("th",[e._v("MAE [satoshi/vbytes]")]),e._v(" "),t("th",[e._v("drift")])])]),e._v(" "),t("tbody",[t("tr",[t("td",[e._v("economic")]),e._v(" "),t("td",[e._v("28.77")]),e._v(" "),t("td",[e._v("20.79")])]),e._v(" "),t("tr",[t("td",[e._v("conservative")]),e._v(" "),t("td",[e._v("46.49")]),e._v(" "),t("td",[e._v("44.73")])])])]),e._v(" "),t("p",[e._v("As seen from the table, the error is quite high, but the positive drift suggests "),t("code",[e._v("estimatesmartfee")]),e._v(" prefers to overestimate to avoid transactions not confirming.")]),e._v(" "),t("p",[e._v('As we said in the introduction, network traffic is correlated with time and we have the timestamp of when the transaction has been first seen, however a ML model doesn\'t like plain numbers too much, but it behaves better with "number that repeats", like categories, so we are converting the timestamp in '),t("code",[e._v("day_of_week")]),e._v(" a number from 0 to 6, and "),t("code",[e._v("hours")]),e._v(" a number from 0 to 24.")]),e._v(" "),t("h4",{attrs:{id:"splitting"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#splitting"}},[e._v("#")]),e._v(" Splitting")]),e._v(" "),t("p",[e._v("The dataset is splitted in training and test data with a 80/20 proportion. As the name suggest the training part is used to train the model, the test is composed of other observations to test if the model is good with observations that has never seen (proving the model can generalize, not just memorizing the answer).")]),e._v(" "),t("p",[e._v("During the training the data is splitted again in 80/20 for training and validation respectively, validation is basically testing during training.")]),e._v(" "),t("p",[e._v("During splitting, the dataset is converted from a pandas data frame to tensorflow dataset, decreasing training times.")]),e._v(" "),t("h4",{attrs:{id:"preprocessing"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#preprocessing"}},[e._v("#")]),e._v(" Preprocessing")]),e._v(" "),t("p",[e._v("The preprocessing phase is part of the model however it contains transformations without parameters trained by the model.\nThis transformations are useful because model trains better if data are in some format, and having this phase inside the model helps to avoid to prepare the data before feeding the model at prediction phase.")]),e._v(" "),t("p",[e._v("Our model performs 2 kind of preprocessing:")]),e._v(" "),t("ul",[t("li",[t("p",[e._v("Normalization: model trains faster if numerical features have mean 0 and standard deviation equal to 1, so this layer is built by computing the "),t("code",[e._v("mean")]),e._v(" and "),t("code",[e._v("std")]),e._v(" from the series of a feature before training, and the model is feed with "),t("code",[e._v("(feature - mean)/std")]),e._v(". Our model normalize "),t("code",[e._v("confirms_in")]),e._v(" feature and all the buckets "),t("code",[e._v("a*")])])]),e._v(" "),t("li",[t("p",[e._v("one-hot vector: Numerical categories having a small number of different unique values like our "),t("code",[e._v("day_of_week")]),e._v(" and "),t("code",[e._v("hours")]),e._v(" could be trained better/faster by being converted in one hot vector. For example "),t("code",[e._v("day_of_week=6")]),e._v(" (Sunday) is converted in a vector "),t("code",[e._v("['0', '0', '0', '0', '0', '0', '1']")]),e._v(" while "),t("code",[e._v("day_of_week=5")]),e._v(" (Saturday) is converted in the vector "),t("code",[e._v("['0', '0', '0', '0', '0', '1', '0']")])])])]),e._v(" "),t("h4",{attrs:{id:"build"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#build"}},[e._v("#")]),e._v(" Build")]),e._v(" "),t("div",{staticClass:"language-python extra-class"},[t("pre",{pre:!0,attrs:{class:"language-python"}},[t("code",[e._v("all_features "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("layers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("concatenate"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("encoded_features"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\nx "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("layers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Dense"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" activation"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[e._v('"relu"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("all_features"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\nx "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("layers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Dense"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" activation"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[e._v('"relu"')]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("x"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\noutput "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("layers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Dense"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("1")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" activation"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v("clip"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("x"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\nmodel "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("keras"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Model"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("all_inputs"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" output"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\noptimizer "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" tf"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("optimizers"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("Adam"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("learning_rate"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("0.01")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\nmodel"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),t("span",{pre:!0,attrs:{class:"token builtin"}},[e._v("compile")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("loss"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token string"}},[e._v("'mse'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v("\n optimizer"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v("optimizer"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v("\n metrics"),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("[")]),t("span",{pre:!0,attrs:{class:"token string"}},[e._v("'mae'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token string"}},[e._v("'mse'")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("]")]),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n")])])]),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-model.png",alt:"model graph"}})]),e._v(" "),t("p",[e._v("The model is fed with the "),t("code",[e._v("encoded_features")]),e._v(" coming from the processing phase, then there are 2 layers with 64 neurons each followed by one neuron giving the "),t("code",[e._v("fee_rate")]),e._v(" as output.")]),e._v(" "),t("p",[e._v("With this configurations the model has:")]),e._v(" "),t("ul",[t("li",[e._v("Total params: "),t("code",[e._v("7,412")])]),e._v(" "),t("li",[e._v("Trainable params: "),t("code",[e._v("7,361")])]),e._v(" "),t("li",[e._v("Non-trainable params: "),t("code",[e._v("51")])])]),e._v(" "),t("p",[e._v("Non-trainable params comes from the normalization layer and are computed in the pre-processing phase (it contains, for example, the mean of a series). Trainable parameters are values initialized randomly and changed during the training phase. The trainable parameters are "),t("code",[e._v("7,361")]),e._v(", this number comes from the following, every neuron has an associated bias and a weight for every element in the inputs, thus:")]),e._v(" "),t("div",{staticClass:"language-shell extra-class"},[t("pre",{pre:!0,attrs:{class:"language-shell"}},[t("code",[t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("48")]),e._v(" input_values_weights + "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("1")]),e._v(" bias"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v(" * "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),e._v(" first_layer_neurons"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n+ "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),e._v(" input_values_weights + "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("1")]),e._v(" bias"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v(" * "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),e._v(" second layer neurons"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n+ "),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("64")]),e._v(" input values weights + "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("1")]),e._v(" bias"),t("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n\n"),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("49")]),e._v("*64+65*64+65 "),t("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),e._v(" "),t("span",{pre:!0,attrs:{class:"token number"}},[e._v("7361")]),e._v("\n")])])]),t("p",[e._v("Honestly, neural network parameters are mostly the one taken from this tensorflow "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/keras/regression",target:"_blank",rel:"noopener noreferrer"}},[e._v("example"),t("OutboundLink")],1),e._v(", we even tried to "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/keras/keras_tuner",target:"_blank",rel:"noopener noreferrer"}},[e._v("tune hyperparameters"),t("OutboundLink")],1),e._v(", however, we decided to follow this "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/keras/overfit_and_underfit#demonstrate_overfitting",target:"_blank",rel:"noopener noreferrer"}},[e._v("advice"),t("OutboundLink")],1),e._v(": "),t("em",[e._v('"The simplest way to prevent overfitting is to start with a small model:"')]),e._v(". We hope this work will attract other data scientists to this bitcoin problem, improving the model. We also think that a longer time for the data collection is needed to capture various situations.")]),e._v(" "),t("p",[e._v("A significant part of a ML model are the activation functions, "),t("code",[e._v("relu")]),e._v(" (Rectified Linear Unit) is one of the most used lately, because it's simple and works well as we learned in this "),t("a",{attrs:{href:"https://youtu.be/aircAruvnKk?t=1035",target:"_blank",rel:"noopener noreferrer"}},[e._v("introducing neural network video"),t("OutboundLink")],1),e._v(". "),t("code",[e._v("relu")]),e._v(" it's equal to zero for negative values and equal to the input for positive values. Being non-linear allows the whole model to be non-linear.")]),e._v(" "),t("p",[e._v("For the last layer it is different: we want to enforce a minimum for the output, which is the minimum relay fee "),t("code",[e._v("1.0")]),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn3",id:"fnref3"}},[e._v("[3]")])]),e._v(". One could not simply cut the output of the model after prediction because all the training would not consider this constraint. So we need to build a custom activation function that the model training will be able to use for the "),t("a",{attrs:{href:"https://en.wikipedia.org/wiki/Gradient_descent#:~:text=Gradient%20descent%20is%20a%20first,the%20direction%20of%20steepest%20descent.",target:"_blank",rel:"noopener noreferrer"}},[e._v("gradient descent"),t("OutboundLink")],1),e._v(" optimization step. Luckily this is very simple using tensorflow primitives:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("def clip(x):\n min = tf.constant(1.0)\n return tf.where(tf.less(x, min), min, x)\n")])])]),t("p",[e._v("Another important part is the optimizer, when we first read the aforementioned "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/keras/regression",target:"_blank",rel:"noopener noreferrer"}},[e._v("example"),t("OutboundLink")],1),e._v(" the optimizer used was "),t("code",[e._v("RMSProp")]),e._v(" however the example updated lately and we noticed the optimizer changed in favor of "),t("code",[e._v("Adam")]),e._v(" which we read is the "),t("a",{attrs:{href:"https://towardsdatascience.com/adam-latest-trends-in-deep-learning-optimization-6be9a291375c",target:"_blank",rel:"noopener noreferrer"}},[e._v("latest trend"),t("OutboundLink")],1),e._v(" in data science. We changed the model to use "),t("code",[e._v("Adam")]),e._v(" and effectively the training is faster with "),t("code",[e._v("Adam")]),e._v(" and even slightly lower error is achieved.\nAnother important parameter is the learning rate, which we set to "),t("code",[e._v("0.01")]),e._v(" after manual trials; however there might be space for improvements such as using "),t("a",{attrs:{href:"https://www.tensorflow.org/api_docs/python/tf/compat/v1/train/exponential_decay",target:"_blank",rel:"noopener noreferrer"}},[e._v("exponential decay"),t("OutboundLink")],1),e._v(", starting with an high learning rate and decreasing it through training epochs.")]),e._v(" "),t("p",[e._v('The last part of the model configuration is the loss function: the objective of the training is to find the minimum of this function. Usually for regression problem (the ones having a number as output, not a category) the most used is the Mean squared error (MSE). MSE is measured as the average of squared difference between predictions and actual observations, giving larger penalties to large difference because of the square. An interesting property is that the bigger the error the faster the changes is good at the beginning of the training, while slowing down when the model predicts better is desirable to avoid "jumping out" the local minimum.')]),e._v(" "),t("h4",{attrs:{id:"finally-the-model-training"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#finally-the-model-training"}},[e._v("#")]),e._v(" Finally, the model training")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("PATIENCE = 20\nMAX_EPOCHS = 200\n\ndef train():\n early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=PATIENCE)\n history = model.fit(train_ds, epochs=MAX_EPOCHS, validation_data=val_ds, verbose=1, callbacks=[early_stop])\n return history\n\nhistory = train()\n")])])]),t("p",[e._v("This steps is the core of the neural network, it takes a while, let's analyze the output:")]),e._v(" "),t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[e._v("Epoch 1/200\n7559/7559 [==============================] - 34s 3ms/step - loss: 547.8023 - mae: 16.9547 - mse: 547.8023 - val_loss: 300.5965 - val_ma\ne: 11.9202 - val_mse: 300.5965\n...\nEpoch 158/200\n7559/7559 [==============================] - 31s 3ms/step - loss: 163.2548 - mae: 8.3126 - mse: 163.2548 - val_loss: 164.8296 - val_mae: 8.3402 - val_mse: 164.8296\n")])])]),t("p",[e._v("Training is done in epochs, under every epoch all the training data is iterated and model parameters updated to minimize the loss function.")]),e._v(" "),t("p",[e._v("The number "),t("code",[e._v("7559")]),e._v(" represent the number of steps. Theoretically the whole training data should be processed at once and parameters updated accordingly, however in practice this is infeasible for example for memory resource, thus the training happens in batch. In our case we have "),t("code",[e._v("1,934,999")]),e._v(" observations in the training set and our batch size is "),t("code",[e._v("256")]),e._v(", thus we have "),t("code",[e._v("1,437,841/256=7,558.58")]),e._v(" which by excess result in "),t("code",[e._v("7559")]),e._v(" steps.")]),e._v(" "),t("p",[e._v("The "),t("code",[e._v("~31s")]),e._v(" is the time it takes to process the epoch on a threadripper CPU but GPU or TPU could do better.")]),e._v(" "),t("p",[e._v("The value "),t("code",[e._v("loss")]),e._v(" is the MSE on the training data while "),t("code",[e._v("val_loss")]),e._v(" is the MSE value on the validation data. As far as we understand the separated validation data helps to detect the machine learning enemy, overfitting. Because in case of overfitting the value "),t("code",[e._v("loss")]),e._v(" continue to improve (almost indefinitely) while "),t("code",[e._v("val_loss")]),e._v(" start improving with the loss but a certain point diverge, indicating the network is memorizing the training data to improve "),t("code",[e._v("loss")]),e._v(" but in doing so losing generalizing capabilities.")]),e._v(" "),t("p",[e._v("Our model doesn't look to suffer overfitting cause "),t("code",[e._v("loss")]),e._v(" and "),t("code",[e._v("val_loss")]),e._v(" doesn't diverge during training")]),e._v(" "),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-train-history.png",alt:"train history"}})]),e._v(" "),t("p",[e._v("While we told the training to do 200 epochs, the training stopped at 158 because we added an "),t("code",[e._v("early_stop")]),e._v(" call back with "),t("code",[e._v("20")]),e._v(" as "),t("code",[e._v("PATIENCE")]),e._v(", meaning that after 20 epoch and no improvement in "),t("code",[e._v("val_loss")]),e._v(" the training is halted, saving time and potentially avoiding overfitting.")]),e._v(" "),t("h2",{attrs:{id:"the-prediction-phase"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#the-prediction-phase"}},[e._v("#")]),e._v(" The prediction phase")]),e._v(" "),t("p",[e._v("A "),t("a",{attrs:{href:"https://github.com/RCasatta/estimate_ml_fee",target:"_blank",rel:"noopener noreferrer"}},[e._v("prediction test tool"),t("OutboundLink")],1),e._v(" is available on github. At the moment it uses a bitcoin core node to provide network data for simplicity, but it queries it only for the mempool and the last 6 blocks, so it's something that also a light-client could do"),t("sup",{staticClass:"footnote-ref"},[t("a",{attrs:{href:"#fn4",id:"fnref4"}},[e._v("[4]")])]),e._v(".")]),e._v(" "),t("p",[e._v("The following chart is probably the best visualization to evaluate the model, on the x axis there is the real fee rate while on the y axis there is the prediction, the more the points are centered on the bisection, the more the model is good.\nWe can see the model is doing quite well, the MAE is 8 which is way lower than "),t("code",[e._v("estimatesmartfee")]),e._v(". However, there are big errors some times, in particular for prediction for fast confirmation ("),t("code",[e._v("confirms_in=1 or confirms_in=2")]),e._v(") as shown by the orange points. Creating a model only for blocks target greater than 2 instead of simply remove some observations may be an option.")]),e._v(" "),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-true-and-predictions.png",alt:"prediction results"}})]),e._v(" "),t("p",[e._v("The following chart is instead a distribution of the errors, which for good model should resemble the normal distribution centered in 0, and it loooks like the model is respecting that.")]),e._v(" "),t("figure",[t("img",{attrs:{src:"/img/fee-estimation-for-light-clients/20210125-091313-error-distribution.png",alt:"error distribution"}})]),e._v(" "),t("h2",{attrs:{id:"conclusion-and-future-development"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#conclusion-and-future-development"}},[e._v("#")]),e._v(" Conclusion and future development")]),e._v(" "),t("p",[e._v("The results have shown deep neural network are a tool capable of good bitcoin transaction fee estimations; this suggests that further ML research in this area might be promising.")]),e._v(" "),t("p",[e._v("This is just a starting point, there are many future improvements such as:")]),e._v(" "),t("ul",[t("li",[e._v("Build a separate model with full knowledge, thus for full, always-connected nodes could be interesting and improve network resource allocation with respect to current estimators.")]),e._v(" "),t("li",[e._v("Tensorflow is a huge dependency, and since it contains all the feature to build and train a model, most of the feature are not needed in the prediction phase. In fact tensorflow lite exists which is specifically created for embedded and mobile devices; the "),t("a",{attrs:{href:"https://github.com/RCasatta/estimate_ml_fee",target:"_blank",rel:"noopener noreferrer"}},[e._v("prediction test tool"),t("OutboundLink")],1),e._v(" and the final integration in "),t("a",{attrs:{href:"https://github.com/bitcoindevkit/bdk",target:"_blank",rel:"noopener noreferrer"}},[e._v("bdk"),t("OutboundLink")],1),e._v(" should use it.")]),e._v(" "),t("li",[e._v("Explore other fields to improve model predictions such as:\n"),t("ul",[t("li",[e._v("A bucket array of the transactions in the last 6 blocks with known fee rates. This should in particular help estimations with almost empty mempool.")]),e._v(" "),t("li",[e._v("Transaction weight")]),e._v(" "),t("li",[e._v("Time from last block")])])]),e._v(" "),t("li",[e._v("Some fields are very important and could benefit from pre-processing expansion, for instance applying "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/structured_data/feature_columns#hashed_feature_columns",target:"_blank",rel:"noopener noreferrer"}},[e._v("hashed feature columns"),t("OutboundLink")],1),e._v(" to "),t("code",[e._v("confirms_in")]),e._v(".")]),e._v(" "),t("li",[e._v("Bitcoin logger could be improved by a merge command to unify raw logs files, reducing redundancy and consequently disk occupation.")]),e._v(" "),t("li",[e._v("The dataset could be created in multiple files to allow more parallelism and use less memory during training.")]),e._v(" "),t("li",[e._v("Saving the dataset in "),t("a",{attrs:{href:"https://www.tensorflow.org/tutorials/load_data/tfrecord",target:"_blank",rel:"noopener noreferrer"}},[e._v("TFRecord format"),t("OutboundLink")],1),e._v(" instead of CSV")]),e._v(" "),t("li",[e._v("At the moment we are training the model on a threadripper CPU, training the code on GPU or even TPU will be needed to decrease training time, especially because input data will grow.")]),e._v(" "),t("li",[e._v("The "),t("a",{attrs:{href:"https://github.com/RCasatta/estimate_ml_fee",target:"_blank",rel:"noopener noreferrer"}},[e._v("prediction test tool"),t("OutboundLink")],1),e._v(" should estimate only using the p2p bitcoin network, without requiring a node. This work would be propedeutic for "),t("a",{attrs:{href:"https://github.com/bitcoindevkit/bdk",target:"_blank",rel:"noopener noreferrer"}},[e._v("bdk"),t("OutboundLink")],1),e._v(" integration")]),e._v(" "),t("li",[e._v("At the moment mempool buckets are multiple inputs "),t("code",[e._v("a*")]),e._v(" as show in the model graph; since they are related, is it possible to merge them in one TensorArray?")]),e._v(" "),t("li",[e._v("Sometimes the model does not learn and "),t("a",{attrs:{href:"https://github.com/RCasatta/bitcoin_logger/blob/master/notes.md",target:"_blank",rel:"noopener noreferrer"}},[e._v("gets stuck"),t("OutboundLink")],1),e._v(". The reason is the "),t("code",[e._v("clip")]),e._v(" function applied in the last layer is constant for a value lower than 1. In this case, the derivative is 0 and the gradient descent doesn't know where to go. Instead of using the "),t("code",[e._v("clip")]),e._v(" function apply penalties to the loss function for values lower than 1.")]),e._v(" "),t("li",[e._v("There are issues regarding dead neurons (going to 0) or neurons with big weight, weight results should be monitored for this events, and also weight decay and L2 regularization should be explored.")]),e._v(" "),t("li",[e._v("Tune hyper-parameters technique should be re-tested.")]),e._v(" "),t("li",[e._v("Predictions should be monotonic decreasing for growing "),t("code",[e._v("confirms_in")]),e._v(" parameter; for obvious reason it doesn't make sense that an higher fee rate will result in a higher confirmation time. But since this is not enforced anywhere in the model, at the moment this could happen.")]),e._v(" "),t("li",[e._v("Since nodes with bloom filter disabled doesn't serve the mempool anymore, a model based only on last blocks should be evaluated.")])]),e._v(" "),t("h2",{attrs:{id:"acknowledgements"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#acknowledgements"}},[e._v("#")]),e._v(" Acknowledgements")]),e._v(" "),t("p",[e._v("Thanks to "),t("a",{attrs:{href:"https://squarecrypto.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Square crypto"),t("OutboundLink")],1),e._v(" for sponsoring this work and thanks to the reviewers: "),t("a",{attrs:{href:"https://twitter.com/LeoComandini",target:"_blank",rel:"noopener noreferrer"}},[e._v("Leonardo Comandini"),t("OutboundLink")],1),e._v(", "),t("a",{attrs:{href:"https://twitter.com/domegabri",target:"_blank",rel:"noopener noreferrer"}},[e._v("Domenico Gabriele"),t("OutboundLink")],1),e._v(", "),t("a",{attrs:{href:"https://twitter.com/afilini",target:"_blank",rel:"noopener noreferrer"}},[e._v("Alekos Filini"),t("OutboundLink")],1),e._v(", "),t("a",{attrs:{href:"https://twitter.com/Ferdinando1970",target:"_blank",rel:"noopener noreferrer"}},[e._v("Ferdinando Ametrano"),t("OutboundLink")],1),e._v(".")]),e._v(" "),t("p",[e._v("And also this tweet that remembered me "),t("a",{attrs:{href:"https://twitter.com/RCasatta",target:"_blank",rel:"noopener noreferrer"}},[e._v("I"),t("OutboundLink")],1),e._v(" had this work in my TODO list")]),e._v(" "),t("blockquote",{staticClass:"twitter-tweet"},[t("p",{attrs:{lang:"en",dir:"ltr"}},[e._v("I don't understand Machine Learning(ML), but is it horrible to use ML to predict bitcoin fees? "),t("br"),t("br"),e._v('I have heard tales of this "Deep Learning" thing where you throw a bunch of data at it and it gives you good results with high accuracy.')]),e._v("— sanket1729 (@sanket1729) "),t("a",{attrs:{href:"https://twitter.com/sanket1729/status/1336624662365822977?ref_src=twsrc%5Etfw"}},[e._v("December 9, 2020")])]),e._v(" "),t("script",{attrs:{async:"",src:"https://platform.twitter.com/widgets.js",charset:"utf-8"}}),e._v(" "),t("p",[e._v("This is the final part of the series. In the previous "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-1/"}},[e._v("Part 1")]),e._v(" we talked about the problem and in "),t("RouterLink",{attrs:{to:"/blog/2021/01/fee-estimation-for-light-clients-part-2/"}},[e._v("Part 2")]),e._v(" we talked about the dataset.")],1),e._v(" "),t("hr",{staticClass:"footnotes-sep"}),e._v(" "),t("section",{staticClass:"footnotes"},[t("ol",{staticClass:"footnotes-list"},[t("li",{staticClass:"footnote-item",attrs:{id:"fn1"}},[t("p",[e._v("MAE is Mean Absolute Error, which is the average of the series built by the absolute difference between the real value and the estimation. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref1"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn2"}},[t("p",[e._v("drift like MAE, but without the absolute value "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref2"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn3"}},[t("p",[e._v("Most node won't relay transactions with fee lower than the min relay fee, which has a default of "),t("code",[e._v("1.0")]),e._v(" "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref3"}},[e._v("↩︎")])])]),e._v(" "),t("li",{staticClass:"footnote-item",attrs:{id:"fn4"}},[t("p",[e._v("An important issue emerged after the article came out, a bitcoin core client with bloom filter disabled (default as of 0.21) doesn't serve the mempool via p2p. "),t("a",{staticClass:"footnote-backref",attrs:{href:"#fnref4"}},[e._v("↩︎")])])])])])])}),[],!1,null,null,null);t.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/51.29907970.js b/assets/js/51.1ead12c8.js similarity index 99% rename from assets/js/51.29907970.js rename to assets/js/51.1ead12c8.js index 2f2802e1b6..f1dcc6d6c6 100644 --- a/assets/js/51.29907970.js +++ b/assets/js/51.1ead12c8.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[51],{401:function(t,s,e){"use strict";e.r(s);var a=e(7),n=Object(a.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("p",[t._v("This is the second part of a two-part blog series in which I talk through the changes made to BDK to make a Taproot transaction. If you haven't read it yet, check out "),s("a",{attrs:{href:"/blog/2021/11/first-bdk-taproot-tx-look-at-the-code-part-1"}},[t._v("Part 1")]),t._v(".")]),t._v(" "),s("p",[t._v("While in the first part I managed to show full raw commits, in this case I will only focus on the relevant changes, otherwise the post would get very long. You can always find the "),s("a",{attrs:{href:"https://github.com/bitcoindevkit/bdk/compare/aa075f0...afilini:taproot-testing",target:"_blank",rel:"noopener noreferrer"}},[t._v("full diff"),s("OutboundLink")],1),t._v(" here, if you are interested\nin that.")]),t._v(" "),s("h2",{attrs:{id:"shortcuts"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#shortcuts"}},[t._v("#")]),t._v(" Shortcuts")]),t._v(" "),s("p",[t._v("As mentioned previously, the main goal of this journey for me was to find out what it really takes to support Taproot in BDK. The code shown here wasn't written to be readable and/or maintainable, so\nsome shortcuts were taken, in particular:")]),t._v(" "),s("ul",[s("li",[t._v('No support for BIP32 extended keys: this is probably very quick to add, but in the first "proof of concept" I decided to only work with WIF keys for simplicity')]),t._v(" "),s("li",[t._v("No support for "),s("a",{attrs:{href:"https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki",target:"_blank",rel:"noopener noreferrer"}},[s("code",[t._v("SIGHASH_DEFAULT")]),s("OutboundLink")],1),t._v(': this would require some minor changes to a few traits in BDK that still use the "legacy" '),s("code",[t._v("SigHashType")]),t._v(" enum from "),s("a",{attrs:{href:"https://github.com/rust-bitcoin/rust-bitcoin",target:"_blank",rel:"noopener noreferrer"}},[t._v("rust-bitcoin"),s("OutboundLink")],1)])]),t._v(" "),s("h2",{attrs:{id:"utilities"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#utilities"}},[t._v("#")]),t._v(" Utilities")]),t._v(" "),s("p",[t._v("Let's start with some utilities:")]),t._v(" "),s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("ecdsa_to_schnorr")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pk"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("ecdsa"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PublicKey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("schnorr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PublicKey")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("schnorr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("PublicKey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("from_slice")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("pk"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("to_bytes")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("..")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("expect")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Key conversion failure"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("pub")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("compute_merkle_root")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n leaf_hash"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("taproot"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TapLeafHash")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n control_block"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("taproot"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("ControlBlock")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("taproot"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TapBranchHash")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("taproot"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("TapBranchHash")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("from_inner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n control_block\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("merkle_branch\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("as_inner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("iter")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("fold")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("taproot"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("NodeInfo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("new_hidden")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("sha256"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Hash")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("from_slice")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("leaf_hash"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("as_inner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("expect")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Invalid TapLeafHash"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token closure-params"}},[s("span",{pre:!0,attrs:{class:"token closure-punctuation punctuation"}},[t._v("|")]),t._v("acc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" branch"),s("span",{pre:!0,attrs:{class:"token closure-punctuation punctuation"}},[t._v("|")])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("taproot"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("NodeInfo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("combine")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("acc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("taproot"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")])]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("NodeInfo")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("::")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("new_hidden")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("branch"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("expect")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Invalid tree"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("hash")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("into_inner")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v('The first function "converts" an ECDSA key to a Schnorr key by dropping the first byte that encodes the key parity, since Schnorr keys are "x-only".')]),t._v(" "),s("p",[t._v("The second one constructs the merkle root of a taptree given a leaf hash and the corresponding control block.")]),t._v(" "),s("h2",{attrs:{id:"wrap-fallible-methods"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#wrap-fallible-methods"}},[t._v("#")]),t._v(" Wrap Fallible Methods")]),t._v(" "),s("p",[t._v("Many of the methods exposed by a "),s("code",[t._v("Descriptor")]),t._v(' struct used to be infallible: for instance, it was always possible to "encode" a descriptor into a Bitcoin script by calling the '),s("code",[t._v("script_pubkey()")]),t._v(" method.")]),t._v(" "),s("p",[t._v("Unfortunately, taproot descriptors need some extra metadata to do that: they can be computed by calling the "),s("code",[t._v("spend_info()")]),t._v(" method, and they will be cached inside the descriptor, but since it's not guaranteed by the\ncompiler that the method will be called before trying to encode it, the infallible methods had to be changed to return a "),s("code",[t._v("Result")]),t._v(", so that they can fail if the spend info is not present.")]),t._v(" "),s("p",[t._v("In BDK we call the "),s("code",[t._v("spend_info()")]),t._v(' method right after "deriving" the descriptor, so it\'s guaranteed that we will never encounter that error: for this reason, we wrap those methods and call '),s("code",[t._v("expect()")]),t._v(" on them, to keep\nthe original code mostly unchanged.")]),t._v(" "),s("p",[t._v("Here we call "),s("code",[t._v("spend_info()")]),t._v(" right after deriving the descriptor, if it's a "),s("code",[t._v("Tr")]),t._v(" variant:")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[t._v("@@ -136,10 +133,16 @@ impl AsDerived for Descriptor {\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" index: u32,\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" secp: &'s SecpCtx,\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" ) -> Descriptor> {\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" self.derive(index).translate_pk_infallible(\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" let mut derived = self.derive(index).translate_pk_infallible(\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" |key| DerivedDescriptorKey::new(key.clone(), secp),\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" |key| DerivedDescriptorKey::new(key.clone(), secp),\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" )\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" );\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" if let Descriptor::Tr(tr) = &mut derived {\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" tr.spend_info(secp);\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" derived\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" }\n")])])])])]),s("p",[t._v("And here we wrap the "),s("code",[t._v("script_pubkey()")]),t._v(" method and call "),s("code",[t._v("expect()")]),t._v(" on it. Note that we only implement it on "),s("code",[t._v("DerivedDescriptor")]),t._v(', because it\'s not guaranteed that "extended descriptors" will have the cached metadata inside.')]),t._v(" "),s("div",{staticClass:"language-rust extra-class"},[s("pre",{pre:!0,attrs:{class:"language-rust"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("pub")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("crate")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token type-definition class-name"}},[t._v("DerivedDescriptorSafeOps")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// The [`Descriptor::script_pubkey`] method can fail on `Tr` descriptors that don't have the")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// `spend_info` inside. Since we generate those upon derivation, it's guaranteed that the")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// method will not fail on `DerivedDescriptor`s.")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("script_pubkey_derived")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Script")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("impl")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token lifetime-annotation symbol"}},[t._v("'s")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DerivedDescriptorSafeOps")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Descriptor")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DerivedDescriptorKey")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),s("span",{pre:!0,attrs:{class:"token lifetime-annotation symbol"}},[t._v("'s")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">>")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fn")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function-definition function"}},[t._v("script_pubkey_derived")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("->")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Script")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("self")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("script_pubkey")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("expect")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"`spend_info` is always present in `DerivedDescriptor`s"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h2",{attrs:{id:"descriptor-metadata"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#descriptor-metadata"}},[t._v("#")]),t._v(" Descriptor Metadata")]),t._v(" "),s("p",[t._v('In BDK we have a few traits that in a way "unify" the interface of a descriptor: things like the '),s("code",[t._v("redeem_script")]),t._v(" of an input has to be computed differently depending on the type of descriptor. The traits we define\nare implemented on the "),s("code",[t._v("DerivedDescriptor")]),t._v(" or "),s("code",[t._v("ExtendedDescriptor")]),t._v(" structs and allow us to quickly get what we need without having to check the descriptor type manually.")]),t._v(" "),s("p",[t._v("Internally, they are essentially large "),s("code",[t._v("match")]),t._v("es that return different things depending on the descriptor variant. Due to some renaming that had been done recently in "),s("code",[t._v("miniscript")]),t._v(" (not necessarily related to taproot)\nwe have to update them:")]),t._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[t._v("@@ -337,6 +339,7 @@ pub(crate) trait DerivedDescriptorMeta {\n\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v("pub(crate) trait DescriptorMeta {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" fn is_witness(&self) -> bool;\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[t._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" fn is_tap(&self) -> bool;\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" fn get_extended_keys(&self) -> Result>, DescriptorError>;\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" fn derive_from_hd_keypaths<'s>(\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" &self,\n")])]),t._v("@@ -358,23 +361,29 @@ pub(crate) trait DescriptorScripts {\n\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v("impl<'s> DescriptorScripts for DerivedDescriptor<'s> {\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[t._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[t._v(" fn psbt_redeem_script(&self) -> Option + diff --git a/bdk-cli/concept/index.html b/bdk-cli/concept/index.html index 4d919e2ee7..6c6f3507be 100644 --- a/bdk-cli/concept/index.html +++ b/bdk-cli/concept/index.html @@ -31,7 +31,7 @@ - + @@ -62,7 +62,7 @@ will never contain any data that can't be recreated purely by looking at the blockchain. Keys, descriptors, Electrum endpoints are not stored in the database. This explains why you'll have to specify them every time in the command line. It can be seen more like a cache and can be safely deleted without risking funds.
  • BDK doesn't automatically "monitor" the blockchain, instead there's a sync command that has to be called by the user.
  • When you create a transaction and then sign it, it's not automatically broadcast to the network. There's a broadcast command that does this. Moreover, the command doesn't accept a normal Bitcoin raw transaction, but instead a PSBT. That's because internally transactions are always moved as PSBTs, and again, the broadcast command is just a very thin wrapper over the raw library call.
  • There are probably more of these examples, but hopefully by this point you'll have more or less understood the gist of it. If you are not a developer, some parts of this will feel weird, inefficient, hard -to understand, and that's absolutely normal. Just try to survive through the pain and you'll be rewarded!

    - + diff --git a/bdk-cli/index.html b/bdk-cli/index.html index 251de242a8..1401b30461 100644 --- a/bdk-cli/index.html +++ b/bdk-cli/index.html @@ -33,7 +33,7 @@ - + @@ -59,7 +59,7 @@ GitHub (opens new window)

    # BDK-CLI

    The bdk-cli (opens new window) repo has an example interactive shell built using the bdk library called bdk-cli that acts both as a reference implementation of a wallet -and a tool to quickly experiment with descriptors and transactions.

    - + diff --git a/bdk-cli/installation/index.html b/bdk-cli/installation/index.html index 6a6afa1617..f89518b49c 100644 --- a/bdk-cli/installation/index.html +++ b/bdk-cli/installation/index.html @@ -35,7 +35,7 @@ - + @@ -110,7 +110,7 @@ wallet Wallet Operations

    An example command to sync a testnet wallet to a default electrum server looks like this:

    bdk-cli wallet -w example --descriptor "wpkh(tprv8ZgxMBicQKsPexGYyaFwnAsCXCjmz2FaTm6LtesyyihjbQE3gRMfXqQBXKM43DvC1UgRVv1qom1qFxNMSqVAs88qx9PhgFnfGVUdiiDf6j4/0/*)" sync
    -
    BDK Foundation
    - + diff --git a/bdk-cli/interface/index.html b/bdk-cli/interface/index.html index 1d90cde000..e3ff42e236 100644 --- a/bdk-cli/interface/index.html +++ b/bdk-cli/interface/index.html @@ -29,7 +29,7 @@ - + @@ -364,7 +364,7 @@ --psbt <BASE64_PSBT> Sets the PSBT to sign --assume_height <HEIGHT> Assume the blockchain has reached a specific height. This affects the transaction finalization, if there are timelocks in the descriptor --trust_witness_utxo <WITNESS> Whether the signer should trust the witness_utxo, if the non_witness_utxo hasn’t been provided -

    Adds to the PSBT all the signatures it can produce with the secrets embedded in the descriptor (xprv or WIF keys). Returns the signed PSBT and, if there are enough item to satisfy the script, also the extracted raw Bitcoin transaction.

    Optionally, the --assume_height option can be specified to let the wallet assume the blockchain has reached a specific height. This affects the finalization of the PSBT which is done right at the end of the signing process: the wallet tries to satisfy the spending condition of each input using the partial signatures collected. In case timelocks are present the wallet needs to know whether or not they have expired. This flag is particularly useful for offline wallets.

    # sync

    This subcommand has no extra flags. It connects to the chosen Electrum server and synchronizes the list of transactions received and available UTXOs.

    Adds to the PSBT all the signatures it can produce with the secrets embedded in the descriptor (xprv or WIF keys). Returns the signed PSBT and, if there are enough item to satisfy the script, also the extracted raw Bitcoin transaction.

    Optionally, the --assume_height option can be specified to let the wallet assume the blockchain has reached a specific height. This affects the finalization of the PSBT which is done right at the end of the signing process: the wallet tries to satisfy the spending condition of each input using the partial signatures collected. In case timelocks are present the wallet needs to know whether or not they have expired. This flag is particularly useful for offline wallets.

    # sync

    This subcommand has no extra flags. It connects to the chosen Electrum server and synchronizes the list of transactions received and available UTXOs.

    BDK Foundation
    - + diff --git a/bdk-cli/introduction/index.html b/bdk-cli/introduction/index.html index 7878a70615..3b6a2c7193 100644 --- a/bdk-cli/introduction/index.html +++ b/bdk-cli/introduction/index.html @@ -29,7 +29,7 @@ - + @@ -53,7 +53,7 @@ Blog GitHub - (opens new window)

    # Introduction

    bdk-cli (opens new window) is a lightweight repl (opens new window) wrapper over bdk that comes as a command line application. It is useful for quick testing and prototyping of bdk functionalities.

    This can also be used as an example application to create your own command line bitcoin wallet tool using bdk.

    bdk-cli can interface with all the blockchain backends currently supported by bdk, like rpc, electrum, esplora and compact_filters.

    Check out project documentation (opens new window) for more details.

    The following sections goes into more details on the installation and usage of bdk-cli.

    - + diff --git a/bdk-cli/playground/index.html b/bdk-cli/playground/index.html index 0c209f59c5..043f507492 100644 --- a/bdk-cli/playground/index.html +++ b/bdk-cli/playground/index.html @@ -27,7 +27,7 @@ - + @@ -51,7 +51,7 @@ Blog GitHub - (opens new window)

    # Playground

    - + diff --git a/bdk-cli/regtest/index.html b/bdk-cli/regtest/index.html index 343713889e..b31395dcef 100644 --- a/bdk-cli/regtest/index.html +++ b/bdk-cli/regtest/index.html @@ -31,7 +31,7 @@ - + @@ -60,7 +60,7 @@

    Just like before, this command will probably take a while to finish.

    Once it's done, assuming you have a regtest bitcoind running in background, you can launch a new terminal and run the following command to actually start electrs:

    electrs --log-filters INFO --timestamp --db-dir /tmp/electrs-db --electrum-rpc-addr="127.0.0.1:50001" --network=regtest --cookie-file=$HOME/.bitcoin/regtest/.cookie
     

    on macOS you should change the cookie-file to $HOME/Library/Application Support/Bitcoin/regtest/.cookie.

    This will start the Electrum server on port 50001. You can then add the -n regtest -s 127.0.0.1:50001 to the bdk-cli commands to switch to the local regtest.

    # Troubleshooting

    # Stuck with "wait until bitcoind is synced (i.e. initialblockdownload = false)"

    Just generate a few blocks with bitcoin-cli generatetoaddress 1 <address>

    # Bonus: Docker

    If you have already installed Docker on your machine, you can also use 🍣 Nigiri CLI (opens new window) to spin-up a complete development environment in regtest that includes a bitcoin node, an electrs explorer and the esplora (opens new window) web-app to visualize blocks and transactions in the browser.

    Install 🍣 Nigiri

    $ curl https://getnigiri.vulpem.com | bash
     

    Start Docker daemon and run Nigiri box

    $ nigiri start
    -

    This will start electrum RPC interface on port 51401, the REST interface on 3000 and the esplora UI on 5000 (You can visit with the browser and look for blocks, addresses and transactions)

    You can then add the -n regtest -s 127.0.0.1:51401 to the bdk-cli commands to switch to the local regtest.

    This will start electrum RPC interface on port 51401, the REST interface on 3000 and the esplora UI on 5000 (You can visit with the browser and look for blocks, addresses and transactions)

    You can then add the -n regtest -s 127.0.0.1:51401 to the bdk-cli commands to switch to the local regtest.

    BDK Foundation
    - + diff --git a/blog/2020/12/hello-world/index.html b/blog/2020/12/hello-world/index.html index e6646dcf6a..62249194da 100644 --- a/blog/2020/12/hello-world/index.html +++ b/blog/2020/12/hello-world/index.html @@ -30,7 +30,7 @@ - + @@ -147,7 +147,7 @@ txid = txid );

    # Custom Database and Blockchain types

    We briefly mentioned before that for our example we used the MemoryDatabase, but that it could also be swapped for a different one: this is one example of the modularity of BDK. By default, some database types are already implemented in the library, namely the MemoryDatabase (opens new window) which only keeps data in RAM, the Sled (opens new window) database that can store data on a filesystem, and the SqliteDatabase (opens new window) that can store data into a SQLite database. But since the Database trait is public, users of the library can also implement different database types more suitable for their use-case.

    The same is true for the Blockchain types: the library provides (through the use of opt-in features) implementations for the Electrum, Esplora, CompactFilters (Neutrino) and Bitcoin Core rpc backends. Those again can also be -swapped with custom types if the user desires to do so.

    # Conclusion

    Hopefully, this article will help you get started with BDK! This is just a very quick and gentle introduction to the library, and only barely scratches the surface of what's inside: we will keep publishing more articles in the future to explain some of the more advanced features of BDK, like key generation, using complex descriptors with multiple keys and/or timelocks, using external signers, etc.

    If you'd like to learn more about the library feel free to ask any questions in the comment section down below, or join our Discord Community (opens new window) to chat with us directly!

    - + diff --git a/blog/2020/12/release-v0.2.0/index.html b/blog/2020/12/release-v0.2.0/index.html index 3c98bd4020..f9da51d8b2 100644 --- a/blog/2020/12/release-v0.2.0/index.html +++ b/blog/2020/12/release-v0.2.0/index.html @@ -30,7 +30,7 @@ - + @@ -128,7 +128,7 @@ .map_err(|e| KeyError::Message(e.to_string()))?) } } -

    # Support for sortedmulti()

    Thanks to the addition of sortedmulti() in rust-miniscript, we can now also support them in BDK, which means we are getting more and more compatible with other descriptor-based wallets out there like Bitcoin Core.

    # Contributors

    A huge thanks to everybody who contributed to this new release with suggestions, pull requests and bug reports.

    Since the 0.1.0-beta.1 release over three months ago, we've had 213 new commits made by 10 different contributors for a total of 9990 additions and 2993 deletions. Here's the full diff (opens new window).

    A special thanks to the 7 new contributors:

    - + diff --git a/blog/bdk-cli-basics-multisig-2of3/index.html b/blog/bdk-cli-basics-multisig-2of3/index.html index 1a5eae4973..a004446c86 100644 --- a/blog/bdk-cli-basics-multisig-2of3/index.html +++ b/blog/bdk-cli-basics-multisig-2of3/index.html @@ -30,7 +30,7 @@ - + @@ -126,7 +126,7 @@

    # Step 12: Broadcast Transaction

    ▶️ bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 broadcast --psbt $SECONDSIG_PSBT

    {
       "txid": "61da2451874a483aa8d1d0787c7680d157639f284840de8885098cac43f6cc2f"
     }
    -

    # Verify Transaction

    Verify transcation in the memory pool on testnet Mempool-testnet! (opens new window)

    - + diff --git a/blog/tags/basics/index.html b/blog/tags/basics/index.html index bcff6c827d..20057ef007 100644 --- a/blog/tags/basics/index.html +++ b/blog/tags/basics/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/bdk-cli/index.html b/blog/tags/bdk-cli/index.html index 47284f1b8b..1c1ce67e6f 100644 --- a/blog/tags/bdk-cli/index.html +++ b/blog/tags/bdk-cli/index.html @@ -25,7 +25,7 @@ - + @@ -114,6 +114,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/bdk-rn/index.html b/blog/tags/bdk-rn/index.html index 5c301f25aa..7f3cb7d260 100644 --- a/blog/tags/bdk-rn/index.html +++ b/blog/tags/bdk-rn/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/bdk/index.html b/blog/tags/bdk/index.html index d1e3fc039d..16c3c90ff6 100644 --- a/blog/tags/bdk/index.html +++ b/blog/tags/bdk/index.html @@ -25,7 +25,7 @@ - + @@ -169,6 +169,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/bindings/index.html b/blog/tags/bindings/index.html index 6197862eac..fd05d9c2ff 100644 --- a/blog/tags/bindings/index.html +++ b/blog/tags/bindings/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/bitcoin-cli/index.html b/blog/tags/bitcoin-cli/index.html index a529cb9987..313724e11b 100644 --- a/blog/tags/bitcoin-cli/index.html +++ b/blog/tags/bitcoin-cli/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/bitcoin/index.html b/blog/tags/bitcoin/index.html index c6b4be2dc5..e3ae94560e 100644 --- a/blog/tags/bitcoin/index.html +++ b/blog/tags/bitcoin/index.html @@ -25,7 +25,7 @@ - + @@ -92,6 +92,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/blockchain/index.html b/blog/tags/blockchain/index.html index 7c83509769..c4462fa1c5 100644 --- a/blog/tags/blockchain/index.html +++ b/blog/tags/blockchain/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/coin selection/index.html b/blog/tags/coin selection/index.html index 918e7d7f41..7f2e0d52b1 100644 --- a/blog/tags/coin selection/index.html +++ b/blog/tags/coin selection/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/compact_filters/index.html b/blog/tags/compact_filters/index.html index 5ca52e23e3..685c1e4805 100644 --- a/blog/tags/compact_filters/index.html +++ b/blog/tags/compact_filters/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/descriptor/index.html b/blog/tags/descriptor/index.html index 092bebeaed..e6360ba3d6 100644 --- a/blog/tags/descriptor/index.html +++ b/blog/tags/descriptor/index.html @@ -25,7 +25,7 @@ - + @@ -114,6 +114,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/development/index.html b/blog/tags/development/index.html index 529b4c5f90..4476fb19e0 100644 --- a/blog/tags/development/index.html +++ b/blog/tags/development/index.html @@ -25,7 +25,7 @@ - + @@ -103,6 +103,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/fee/index.html b/blog/tags/fee/index.html index 26e4df7e81..fb3fc13870 100644 --- a/blog/tags/fee/index.html +++ b/blog/tags/fee/index.html @@ -25,7 +25,7 @@ - + @@ -60,7 +60,7 @@ - Tags: - Fee, Machine learning


    Fee estimation for light-clients (Part 2)

    + Fee, Machine learning


    Fee estimation for light-clients (Part 3)

    By Riccardo Casatta @@ -71,7 +71,7 @@ - Tags: - Fee, Machine learning


    Fee estimation for light-clients (Part 3)

    + Fee, Machine learning


    Fee estimation for light-clients (Part 2)

    By Riccardo Casatta @@ -103,6 +103,6 @@

    BDK Foundation
    - + diff --git a/blog/tags/getting started/index.html b/blog/tags/getting started/index.html index 440952a50f..7476e38c9d 100644 --- a/blog/tags/getting started/index.html +++ b/blog/tags/getting started/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/guide/index.html b/blog/tags/guide/index.html index 432544cdd8..158fc7abee 100644 --- a/blog/tags/guide/index.html +++ b/blog/tags/guide/index.html @@ -25,7 +25,7 @@ - + @@ -125,6 +125,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/iOS/index.html b/blog/tags/iOS/index.html index ddc7ef2294..77a5c51a21 100644 --- a/blog/tags/iOS/index.html +++ b/blog/tags/iOS/index.html @@ -25,7 +25,7 @@ - + @@ -92,6 +92,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/index.html b/blog/tags/index.html index 18ae0d6c4d..41b69721e1 100644 --- a/blog/tags/index.html +++ b/blog/tags/index.html @@ -25,7 +25,7 @@ - + @@ -49,7 +49,7 @@ Blog GitHub - (opens new window)
    - + diff --git a/blog/tags/miniscript/index.html b/blog/tags/miniscript/index.html index 90ac2493b8..afe947988e 100644 --- a/blog/tags/miniscript/index.html +++ b/blog/tags/miniscript/index.html @@ -25,7 +25,7 @@ - + @@ -114,6 +114,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/mobile/index.html b/blog/tags/mobile/index.html index 69fdbfd9ba..a15b999760 100644 --- a/blog/tags/mobile/index.html +++ b/blog/tags/mobile/index.html @@ -25,7 +25,7 @@ - + @@ -92,6 +92,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/multi-sig/index.html b/blog/tags/multi-sig/index.html index 5ed860bd24..436abce7e4 100644 --- a/blog/tags/multi-sig/index.html +++ b/blog/tags/multi-sig/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/novice/index.html b/blog/tags/novice/index.html index 18f169275b..62ff5db28e 100644 --- a/blog/tags/novice/index.html +++ b/blog/tags/novice/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/paper wallets/index.html b/blog/tags/paper wallets/index.html index 284362e797..f18300c309 100644 --- a/blog/tags/paper wallets/index.html +++ b/blog/tags/paper wallets/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/project/index.html b/blog/tags/project/index.html index 33d23cff3f..282c71b91b 100644 --- a/blog/tags/project/index.html +++ b/blog/tags/project/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/release/index.html b/blog/tags/release/index.html index 3423e3f6b1..b5c7c5d82f 100644 --- a/blog/tags/release/index.html +++ b/blog/tags/release/index.html @@ -25,7 +25,7 @@ - + @@ -158,6 +158,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/rust/index.html b/blog/tags/rust/index.html index 2ee138aa24..b73d0183ec 100644 --- a/blog/tags/rust/index.html +++ b/blog/tags/rust/index.html @@ -25,7 +25,7 @@ - + @@ -169,6 +169,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/security/index.html b/blog/tags/security/index.html index abe1eb754e..b596f9498b 100644 --- a/blog/tags/security/index.html +++ b/blog/tags/security/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/summer of bitcoin/index.html b/blog/tags/summer of bitcoin/index.html index a18ce693dc..2a9e0518bd 100644 --- a/blog/tags/summer of bitcoin/index.html +++ b/blog/tags/summer of bitcoin/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/taproot/index.html b/blog/tags/taproot/index.html index f1b10d93da..0fb19c6503 100644 --- a/blog/tags/taproot/index.html +++ b/blog/tags/taproot/index.html @@ -25,7 +25,7 @@ - + @@ -92,6 +92,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/tor/index.html b/blog/tags/tor/index.html index ba17753b7c..aa5ffc9ed9 100644 --- a/blog/tags/tor/index.html +++ b/blog/tags/tor/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/tutorial/index.html b/blog/tags/tutorial/index.html index 63dba073a0..325e325951 100644 --- a/blog/tags/tutorial/index.html +++ b/blog/tags/tutorial/index.html @@ -25,7 +25,7 @@ - + @@ -147,6 +147,6 @@
    BDK Foundation
    - + diff --git a/blog/tags/wallet/index.html b/blog/tags/wallet/index.html index a1167256f9..dc7824fb95 100644 --- a/blog/tags/wallet/index.html +++ b/blog/tags/wallet/index.html @@ -25,7 +25,7 @@ - + @@ -81,6 +81,6 @@
    BDK Foundation
    - + diff --git a/blog/using-bdk-with-hardware-wallets/index.html b/blog/using-bdk-with-hardware-wallets/index.html index 11c53b9d46..a4f4c2fef9 100644 --- a/blog/using-bdk-with-hardware-wallets/index.html +++ b/blog/using-bdk-with-hardware-wallets/index.html @@ -30,7 +30,7 @@ - + @@ -181,7 +181,7 @@ blockchain.broadcast(&raw_transaction)?; println!("Transaction broadcasted! TXID: {txid}.\nExplorer URL: https://mempool.space/testnet/tx/{txid}", txid = txid); -

    # Conclusion

    We just received coins on a hardware wallet and spent from it - how cool is that?!

    See the hardware signer example (opens new window) for the full code, and, if you have any questions or suggestions, head to our Discord (opens new window). See you there!

    - + diff --git a/descriptors/index.html b/descriptors/index.html index 05fef21864..18c6efb7a2 100644 --- a/descriptors/index.html +++ b/descriptors/index.html @@ -33,7 +33,7 @@ - + @@ -63,7 +63,7 @@ aims to produce the first "Native Descriptor" Bitcoin library that can be used by developers to build their own "Native Descriptor Wallets" (opens new window).

    # Compatibility Matrix

    Below are some tables to highlight the differences between Bitcoin Core's descriptor support, rust-miniscript's one and BDK's.

    # Key Types

    Key Type BDK rust-miniscript Bitcoin Core
    Hex PublicKey
    WIF PrivateKey
    Extended Keys (xpub/xprv)

    # Script Types (top level)

    Script Type BDK rust-miniscript Bitcoin Core
    pk()
    pkh()
    wpkh()
    sh(wpkh())
    sh()
    wsh()
    sh(wsh())
    combo()
    addr()
    raw()
    Bare scripts

    # Operators

    Operator BDK rust-miniscript Bitcoin Core
    pk()
    pkh()
    older()
    after()
    sha256()
    hash256()
    ripemd160()
    hash160()
    andor()
    and_{v,b,n}()
    or_{b,c,d,i}()
    multi()
    thresh()
    sortedmulti()

    # Modifiers

    Script Type BDK rust-miniscript Bitcoin Core
    a:
    s:
    c:
    t:
    d:
    v:
    j:
    n:
    l:
    u:

    For a more thorough description of these operators and modifiers see Sipa's Miniscript Page (opens new window) and Bitcoin Core's (opens new window).

    # Examples

    Some examples of valid BDK descriptors are:

    Spending Policy Descriptor Address 0 Address 1
    Static P2PKH pkh(cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR) mrkwtj5xpYQjHeJe5wsweNjVeTKkvR5fCr mrkwtj5xpYQjHeJe5wsweNjVeTKkvR5fCr
    Static P2PKH, watch-only pkh(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c) mrkwtj5xpYQjHeJe5wsweNjVeTKkvR5fCr mrkwtj5xpYQjHeJe5wsweNjVeTKkvR5fCr
    P2WSH 2-of-2 with one private key wsh(multi(2,tprv8ZgxMBicQKsPePmENhT9N9yiSfTtDoC1f39P7nNmgEyCB6Nm4Qiv1muq4CykB9jtnQg2VitBrWh8PJU8LHzoGMHTrS2VKBSgAz7Ssjf9S3P/0/*,tpubDBYDcH8P2PedrEN3HxWYJJJMZEdgnrqMsjeKpPNzwe7jmGwk5M3HRdSf5vudAXwrJPfUsfvUPFooKWmz79Lh111U51RNotagXiGNeJe3i6t/1/*)) tb1qqsat6c82fvdy73rfzye8f7nwxcz3xny7t56azl73g95mt3tmzvgs9a8vjs tb1q7sgx6gscgtau57jduend6a8l445ahpk3dt3u5zu58rx5qm27lhkqgfdjdr
    P2WSH-P2SH one key + 10 days timelock sh(wsh(and_v(vc:pk_h(tprv8ZgxMBicQKsPePmENhT9N9yiSfTtDoC1f39P7nNmgEyCB6Nm4Qiv1muq4CykB9jtnQg2VitBrWh8PJU8LHzoGMHTrS2VKBSgAz7Ssjf9S3P/0/*),older(1440)))) 2Mtk2nyS98MCi2P7TkoBGLaJviBy956XxB1 2MuEStKzYhqb5HCFgHz9153tZsL5sVqV5xC

    # Implementation Details

    BDK extends the capabilities of rust-miniscript (opens new window) by introducing the concept of an ExtendedDescriptor: it represents a descriptor that contains one or more "derivable keys" like xpubs or xprvs and can be "derived" from a normal Descriptor by deriving every single one of its keys. It is currently called "StringDescriptor" in the code, because it's implemented as a wrapped miniscript::Descriptor<String>.

    ExtendedDescriptors are derived using a single index instead of a full derivation path: this is because normally most of the path is fixed and can be represented right after the xpub/xprv itself, and only the final index changes for each address. This is what's normally called a DescriptorExtendedKey in the codebase, it is represented with a similar syntax to Bitcoin Core's, such as:

    [d34db33f/44'/0'/0']xpub6ERApfZwUNrhL.......rBGRjaDMzQLcgJvLJuZZvRcEL/0/*
    -
    BDK Foundation
    - + diff --git a/examples/index.html b/examples/index.html index ebc5390c09..17c097fe4c 100644 --- a/examples/index.html +++ b/examples/index.html @@ -29,7 +29,7 @@ - + @@ -53,7 +53,7 @@ Blog GitHub - (opens new window)

    # Examples

    Click the links below and learn from community-built example projects.

    # BDK-CLI (opens new window)

    A command line interface to experiment with the bitcoindevkit.

    # DevkitWallet (opens new window)

    A demo app for the bitcoindevkit on Android using bdk-kotlin.

    # Padawan Wallet (opens new window)

    A testnet-only bitcoin wallet full of tutorials on how to use bitcoin wallets.

    # BDKSwiftExampleWallet (opens new window)

    An example iOS app using bdk-swift.

    # Tatooine (opens new window)

    Tatooine is a small bitcoin testnet faucet built with Ktor, a Kotlin asynchronous framework for creating microservices and web applications.

    # SEBA Bank Proof of reserves (opens new window)

    The bdk library aims to be the core building block for Bitcoin wallets of any kind. The bdk-reserves library provides an implementation of proof-of-reserves for bdk.

    # Stackmate (opens new window)

    A multi-purpose Bitcoin Wallet.

    # Spotbit (opens new window)

    Spotbit's purpose is to allow users to access price feeds in a customisable way that preserves privacy and mitigate the reliance on a single source of data.

    - + diff --git a/foundation/index.html b/foundation/index.html index ffb19885e0..bd8dcdd842 100644 --- a/foundation/index.html +++ b/foundation/index.html @@ -29,7 +29,7 @@ - + @@ -74,6 +74,6 @@
    BDK Foundation
    - + diff --git a/getting-started/index.html b/getting-started/index.html index 43341e665c..e4b0b428ac 100644 --- a/getting-started/index.html +++ b/getting-started/index.html @@ -31,7 +31,7 @@ - + @@ -105,7 +105,7 @@ wallet.get_descriptor_for_keychain(KeychainKind::External).to_string(), wallet.get_descriptor_for_keychain(KeychainKind::Internal).to_string()); } -

    More information about each component used in the code can be found in BDK Documentation (opens new window).

    More information about each component used in the code can be found in BDK Documentation (opens new window).

    BDK Foundation
    - + diff --git a/img/case-studies-logos/lexe-130.png b/img/case-studies-logos/lexe-130.png index 9a227f25d938ecb6827c50908e62ec366d223fcc..ea32e504053e15310c730eba82a424a7ff026d7b 100644 GIT binary patch literal 8563 zcmeHtXIPV4vvxx7ReB9bkJywKB8B_^OJ0002Q>S{^`X!;esLhx|V zcfQ5a-vkRTuPqM%)FcqzSYxA~S#8w}v;hD=P5>Y%3;?)7i-NWR053rRVAl!&kbVgO z(70wd>qF3rbQo0KURxW$gXZx7*gy&ZCYl4HUjQII;I}t42hadA{LLExg#Z|68992H zMIr!Lf7v*p>F-Moy?$f$q6xmMnyCi>KuGy}0RdUrv}hkVh=)*5sJ520Ez+6a+74+0#Eik5v)r4r3MP7vvXYlOZ+4XbV0MUl&hnA3hfk z_CKBcw;v@Kx_E@ECj#lh`rEIy4bsa~hK=oapuev_^Mv^z{uRl^1&^cUnGqJLQbE>;?90P{dP zdHwFTtb{N`;C~4J9bo#O0RI&JJ3!YR0YmTf9}&!V*&Q_}^9!1_lJQYY~7V#`~Xh1b%*WS-}| zHq_jGx0kA~LVpLS%*uv0k)U?ZEQ&234-Z2_(g;hLH3W})g$=KPx&%!h;@%0yW6KYV zVVyBcMN>m2JuJ4UAPW8vD@Xn4APOBXIr1o042?#-kV@ryXa|#Mpa19JKlOl&vc2}F zZZr=VTr@Ujz~7QvbR+!2ab)>sp7BVSQMq3?)iI<_hjX!5=whJU@`K;QHreSkWLgx& z__mUZ$C90CERrFJ(he1s_9G5ii1VGH*V#K$+f4n*^>MT+1$ZeMW_I#@cM_i2nbB!{ zjO|VLF09j$=N?~CcO1QejSWWHiC3aWqg{Ad2-AHxwtPD=e+j+TuuC8QvZOR!=RMW@ zD)>-t2_=`VftD7L7;-9gSDw7!&G{(?LYIbm3^g9E74IkSMZ&>k7lj!4)K8=Iz4v`_ zaag~qHfdAW<~jGUl{^I%##y zCNp4UDp4K05LVdT)jc-kY|gWA#?Dq`PAcuUFL|)gRMfvO(tMHM(ZM#r6QSmuMEpcg zg<-0KBx$U9vNTW?GoIlpFh80|Ha#`pxqK z{UVjoOcD4DPyRKuyHeOYu2EUQPV;!ePt7g*Zl(WAfsDbo7W`wdQo*DN=< zS)y*q5ajpu4m+0;g8^+VI`b9DhWwVNhsE>HgkQMiexuBC|8?gX2JEHh<>q4t!0M{Q zXh-Q7`CD119i0K0w+YaOm)nJh^RE-FRKM`%$X3@StO;B%?e*qd5;FKGJZ2}O4sQ5#FXb{w>%%yOI7cMeA_mdB`$ z_F60Mhz@)WVh}b&rfv`w*fl(WkI`kG#jmRy$|bbbkx(bB#=7T z;qcELbl9H>`M{dR3oUhsv#WO#Mc$8Vur@bLRH>orMKBb~Icy|ci@;FD>$F^le&Sud zXmG9fLGpUp8OfMA@-~;*->EsagbdMaUah38{Jq?8Ye@8fg{3g#*LUR__fH+gN#AaO zj=5RMT8_q*#aoYklDaLQNm1R&rU;oIw`rY<>M7Hd{lyU1w#l=*@EqX$s3pRzngbHJ znf;C8iMf5R{v$?Bb8&Nv?_(5_9}3yC+hPteI)M;5Zy|{zJeY-Ow45Y2*=j!Rc^i8+ z4lq8-z&i1!VoFs@E0h(Ll43ZL=fT%$X{lXR;-b}TF-yIIL(HTyJevO%zL@izXnFMzgqIK-O!bo`m1O0PdHKP7!@xV#RtPp> z`CZAWWb;#?=}=8d0dnc1=Rw#W;x_B(bDy=rvTv*mDI-J)#dLpsO$2VB$m#N<@r%Zb zD;tQ<_6W#tIT%5anK}VwRH8|s*3E#_5Q~FeS173H&-U6>Xl%y-?wehp+|36P_0}p zr*_szabL<#C-zZaJ$5$N_LELGEH+-pcq_flfUQEc4E%ecZMm-F0n$kX9b(jyui>0BY}G(1w~*~5^4Wjy<3sk z(8{;VIIC>h4ecN?M$r>a;^rpSp2LL}@-7dU&?=k6`sH!x1(4zC1vZZ;W^BdsQur9VR>M%p_g zQAPvQ1_?Sj_wggT#`MxOt^32h$>XxM%<6g*=Zb3|Yuevf7ub{pvvbMH28%B*xXR_) zsB7a;n0r4c9O{^aD+^ilrdOKbq<#GvE!Sw_{2A$)O)9&7*p5TwY^uOPF2^>eTvd^ElxsTC4)b$g0VTR zG9?8uH*viKI;1$?oqxQGUn_HdsYU7YsS-`7yu8Vrb? z0|BCFV8}W)Bd7MvJ=e=#edF|9Qh3s1w|xJ!4vhn%(%2%^_>pJEQ?j?X8Wi;trQs4w zo2F;?^T#)g6TK?KK6oCp(n~E{YpwdR^FFFiWM21`^LVT9?9uROQIFak`XH}2CZkj) z7K(;t+LZ24ZNQSpdAGy<=uLbvA_$R%id^?uV51%fK)OI;_~3(c?)y8|wm*=Y5Uw&r>Ck zUXRF7vTX~JmZjk%mF5z3Gm1(30wmK>1ATo-p0r%EN1Ap~q}*gvOo#A{?=$-BU2Imc zFmvW>jN*tQ3=j3OxuP!8i{XNX!q16mOcHo8)co!ofc4Kl=g};W@9q4&+OgT4`p^QN zt*%{LZVwgp+I^@VlP0aKY)oTpXHV$6E=K}8X(T0~T+>ed+>o&EHnGhP3W~K*uQPsv*UsAdays=`yV4a2d(~7}i5MR0%h}J3bFmC#B*|cR~I1UlBMib-$ zC!rkF3hkF02Cidg?i=#;;<_E*JZtcIu}-|D)d;F&_kZB)*OAI_c;*EPd`M0b9h$a* zcdwNKBie>kMM!Yya`TM&3a`=b!djXjs#H zSU5=SjmcXQmmc{{7vLI?Q${djwfkDthji(>>4{mu@(&U50r;r`q+;^t&D?wFC%zG>Il8oP|^9*J$yBML%oms$7z=;pz_u31|K5_8&vn! zVdxwQqFju*7P~|40m(@EZAdS3XpW1mSr{H2tYC46?p)O)#!D$s%2G{_plMAWdDdO; zJcmaWV4!z4vE?nm5wTw{b0r$6ou2466W#PqIWUE3$*YX04YZ&0CAtJbkhL ztYI<$hKZ9mZ$B2AbLkc@-r|ht=URD}c8XL#wV#$J@PC%ms9-Nj0adxccsuYJ$xfLo zF}>NMz-YJWwh3Z>VA;JV6%5Q4<u18;jmJVu4X<*fdU2!osN`r<>zwC}>HT*&R+GX8mgdwgf82{T^*&h8&4{G~ zgQ3T*<^WS`p#x#lX*Yg1YR+=7cx_=)U*DoSUuL~g|Gu#g(~B7T4*7N)+lm^hW+R_b z7e1j)@7Rm#My%_P9sY){K|vE4)sYm0{vTSsdZ{itZaY12eN8Dbp1zLkXEDN6&pI<3 z`ob_Zx^SUQ4yN3tYsaS!??I4i`OEdUyLgG9_{q;nH=P3@0zv#d`LZAUv0*BqtJGgN z_$nzPZ?7v6^cBnaqsEO?re&*zrJDHenQ14`Lo7`@=XhQdqWVwpp*y1yaSZDmFXrzr z%au==+ioa+#B+U_FQ#hJj|!mu)tf{z*-VPH~bHO^s594j6=Sy>SF{ zN1w*Ou-GgIF{XCZ@Autnwdf|#Ct@AIKP0KLXo+`y802Zu|6|*yW21~a?|Tpp>f}x- zrw!#S&qO{dg0badKdA$hPH$w=kx$15IdE#GD`co6h3fCj(VO-?a&5Nvn2 zak99o%c(c6vr}tKVd3=NN^L1=I*yIFx?V!q!HO(Fs?Aj$*H=F;XXz-lFs394f2*W% z^0FfiyyEs)OV~9v4uF5Uqu|EM>?o3RjidcRh@(1^fq*y3mi9H(F)w|4 z3YYFk?ZyDgSHo;%;87*fu2mtrhi23Jfl{KU$(oA%JHlYC{4kP_VF7^(e9u1lh>edK zG|fXteF50f8$ye+mdt5l4syLW3JXp-Q@PT|{HJyBOaPS>--FRKE$a^3_u|I4k3Yj3 zpl^N-wB(f&Mqz{T+-EUYi`_4i-LqDr-?}rACHmf6|IFtQ_$Id9w9_|h6-)I#)Rw|8AaugLB4|zM z2GV0u^{_#db$)ypqB(6LQ84!Uc_J|}L8|va`j^(RyLAR8q~CE=Siw(SJ5M|3KC#hb zh961BD<&*oy>g)@<;ZnQnXS-f5_ZC3vVDs(rY zh)0fhDDoBs!GDw-&R8=EMb;=lAyyVa3e=2Jc1c+|R4)CIwdY4m0uBl*ZV#F8?hLV{ zE>^mfdpOE!V%fEv9s2JqUO(g6n1P9%c4)SE_EFG(ZOd!?s_$Ja0t^e<&|En=q#S1{ z%9fdR&rb||0@w-Wz)1JB<^g)urQlB|j%&ROW9J$Rae&@+e)|e#EBZ{8cqNvi-f_t? zY9%1p;bi5*d~=ud%hIqitPXs85$;@agF0m|Fs6o6H&S^Y{h3*B%=PEj-SFD1Q+#uV zsj5;?r|QTcnJT-Lg1=baXRkA2%DZpgw*8de-ml(r7HqkR2G0aYtpDgrX&)a-B!P9; za8VYRzdw{G|ojM%)=dbUGt&4}YN_fU2=sy)SD6hzT0qsMjc0kqX8Ts={)!a$a`np~dy; zc@i79H#Nc)lhH55>7qm7$*{r~;C?QE;5x$pyeQ_Q{p-3Yyx#r`{bH=NSvI)4wm_{t z;)LP;xjQP@G$z^|lmPO5VrEtnK{RY8yfyy572&mgoU~fdHO4#fp&2M#1dgGCj4~I7 zxE|{rt_I&;?jaK?>%Vpz>tT#fn%C^_(SI;;3YY&x?#3E!;YLnM2q+r_S-uEqJHwyh z1H|~H_oyNuLuR)J1b%y@&x`eG9`i1Ux4aW7mJh7n{Y84VRQNOW2P4IzbG!?8N65uu z5pOPzU_7~@_d2HL3kyJ9Uan>xa~|_cVJZHBvFf!JaNaWGlkuJ0h4cz=#x#*hC?zEA zWhYT&#pZ1Dsl^sPE-Ys4-7`E|a}M!u%rk3x1-{4|OX`DF$>9E6{Q7=}dGZR!#`c=g zGvwm{5bkbl+lmUO7fgWr4y{rTsz=~<^L?;z2h*!mZ_QEXL8(N|omO_Boxl5#h-k2P zO(^3KfXJ141NTTE(E^io9&}!6M*TL?@B5za+u>`z1>b24QL9e?Pz5Mkt$)~7ol3_N zXC?o*Iu^S1(%b5RMc@Wid&Hn+L+{|F=#~iojc{jB25gM5qa-irshZ1k2k% zti&nqbPI$wos9qYLfPA^g15#-4QYp=lGRg^Y$Q{qeBOa8Nv*e4Ejwj~rBTXV(6Nde zIUEN`3e(ZH>vQug&+$0-=x_QB8s#FVDGwI)gN{cFl_w(0wD-TOK*l;k32&JF*0;W` zzRle#y1eRp9r*R=<Tx8;d@Vb7s#X9?Vr z1d2IajF!@=1i)81ae?k1!HReyBp!Ri6KLIZ=XZM zx}EeFlqJ{OkC6Dn?FY}<&0WbzH2aBaL|UMZ+$+IY%;!NjAf%~PfMc7<wC%Pb!$n5j*pr)GX$Is&od{J2`PTVZUxQ>eOp@3}n3wz3J`6@08H~-X6znTeG zVumx!a$x*D%zC7vYzzM15~V(B?@i&9;Vlc|Q)w;4@lM`~4}*@fS=@{9%BYBqTr65m z(8H|2xbZ7!yvclcd5XLhZ$~4O--x*FVSf&1?qz`;k|fTb zs+|-0QEU4ZrC#379c_-@|8>IUmJGlZl}p3$Aha}=w8{OoMS};n)}k3*e}RO;d9hzf zA`0z*v*gLXbaegoWtEkV>j|wF2k#oTXu%1W4n$rbHD3hZuCVx6{kpi&dt+SrIh+Lc zIxfgCs0JmLpv$ka-Psdp3lz9R4$BX(`Vw@L--|XXl-O$1&WQi`(D=2SivRLmcj?*t zM>pZQFNG}PRpcayhHwN(S-##@(&c>*5Z@5a#Z#R&h$1uqb;`;8P`o^zZaHj9SPXDx zzP;RaV-wkp@G5)h{Uzswi+rQb_zZpANux5^(CyL%Fx=Vd9*jsKL7{c@C}eFkR;kJ}Fd z#ycOKO{*7QEZ&IjY5OFtec;2TV66`lz>o!bSXWDPF4LE3BIoOq1d-t)!XOco-n%C^ z4Y?caY&z7gS;89DF_A9ChSbn=`&PgWtGRZCE6_9t0U<=S6T68vO%&mnS{x@RVXx#9 z%?x07o!PEVa5J4g=rp$%)FFUrKRKO(h2w*z`*|cSxUqDfOz81u%RH!~D4`q14!Lv* zt10ug4E5Y5S!+^|y&k(4V?-*u5yOya4?)Cpc&Is&H)mfC1vs9+V!*E=g(OQ#bS+j- z@U{{DLY1>JTH)-BP+XH+$rlh+gI=NE3~_2+{ZUv|la(H##b-Tt zl`w^QLfMuv(LY?V$W8K60R^y9T`hYZTK`Y>|8oz1(&&v9e|wLY$o~6}1M12;N>vJ0 GA^!!@Q6#|EO8kXWfUE@ae{0~Bmk3?r2=l9L`06;aHY+&c3BrRc_=kMbg zE-#d5aonT9o;ipr6d+-X;}VuhN(e@L#E2NOg5cA{+|=xs;TJUi=46m?ZJ8?*b!-f4 zxiL38&lj9~$uJ=GeS?xtqB(AVGk=NFUj6K-3Pmv`J+k(7lvunAyL_NE1XT_JrE3#4 zfBsTTusykkFIXwo+}&!=!PJ8dM`Jma9QO(X+;S;J9dE!VzEAq#{o*5*)?15k%Y~w* zr;d(>;Vw$P1_&TCB5*6?1^AlyCyAPI_mUbP>^U4j7J>WYh28>j+U?m81pTEjUS%}) z2AS7m)U}giQ)EW65pu|K`3yc^YnSr?UqKuw6)9B!)=65CSc7NB8Go3FRH1-T=)I3$ zb9!bR1RRG1`qQ{@2eMRj(!;W@`KG!>_rsdlDFgkh7H9rwKU0^I8R`WT!a5xk;YyNs z3v|vFi~(P|>$sG>?)GM}`16I9bJ399^YN28$1Pvw2PUo4a ztwWrq!8tkMr?QwjEO{$ zV$cq64A3+b0 zDbxB4nC@iS_oKt-R`H0nQwX2zMO4keo9r+OoG!ZjvwL15Y7|$cUbSJw3 zu3n=BinW)+Os0wEz(qD(-8Jko;QWK^hz>i7smg^dvnx+aJ zZ`~3?Y6i)GsPggI!7>}$ZVHuZ59a3GGbUEuD)Cr6XT7c&%ed~@reE^VR~2?MNFJIg zz#5U_6@$>KGc~W;oIA}xxP9;NoJjA(dPWyA(px1KUdu>!nm>1}H2pQ>Jc^yMSc{C5 z?nyKWng;%CFJCz8bWX=R-j-RWMlE*H7g*hjii<3`a)`-fe%98hD;XXs=c}0qkV_L| zbvcR`9`_TB)p5CtnciJyi-bL%|1b!BJG3=t5s|_0qcvCHmxcqe{dy|ZKiL1(jg0(J z6ri88x&PX`DpNE&D_~RjsfQ$1DBcDS3&NzdhLXsGjWq@3`@5)7W%|(7M=ojK<3cH# zSE+{lTo2_A$hZWL(&FuJ892rvaWIa1<_SF=^!%$YVLV^_KB(W{N%)!*t@*r+$K~#!^zlJj{!rstmv`_HX7cgMwi)V)<7J{k*Q!2+PK|5m132O~Ju7{%0fva~vgvdhz!Ipkw)k6-7+wGPr+^ubshoC3S_oWb3>GNTh9YX$_ zb?m3Ibk()&fc*)lh@j0*EE3iz)$bnsP@n0^Bz1hf;578d(pM5|Do6M(*cPoAoC!?_ z``^B*SH6LANQN%m#aO0n_p>)eftvgexOMUdaIDsUe7n4$T)<@VaA3&3KQYg^*&hs(!w7F+;Y} zmUg%3?HyAwQmzDwpEh1MH>RWcl(g}WIHdYlr{NPeJWWep^ysiX^FMl;vfKl=SrG1~ m9DaGWetEX)Ul)H*GC+V&X@!F<>bCQw2K-kA`&{x8r~D5Am6FH+ diff --git a/index.html b/index.html index c31dd5264c..dbfb18deb9 100644 --- a/index.html +++ b/index.html @@ -29,7 +29,7 @@ - + @@ -80,6 +80,6 @@
    - + diff --git a/sitemap.xml b/sitemap.xml index 5f8e8c2a79..3cc0a6fe8f 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1 +1 @@ -https://bitcoindevkit.org/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/_2024-q4-update/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/bitcoin-core-rpc-demo/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/bdk-cli-basics/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/bdk-core-pt1/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/bdk-rn-making-of/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/bdk-cli-basics-multisig-2of3/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/bdk-with-tor/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/bindings-scope/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/compact-filters-demo/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/descriptor-based-paper-wallet/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/descriptors-in-the-wild/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/exploring-bdk-flutter/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/exploring-bdk-rn/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/01/fee-estimation-for-light-clients-part-1/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/01/fee-estimation-for-light-clients-part-2/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/01/fee-estimation-for-light-clients-part-3/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/11/first-bdk-taproot-tx-look-at-the-code-part-1/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/12/first-bdk-taproot-tx-look-at-the-code-part-2/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/getting-started-with-rust-hwi/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/improving-coin-selection-in-bdk/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2020/12/hello-world/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/hidden-power-of-bitcoin/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/miniscript-vulnerability/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2020/12/release-v0.2.0/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/01/release-v0.3.0/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/02/release-v0.4.0/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/03/release-v0.5.0/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/04/release-v0.6.0/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/06/release-v0.8.0/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/07/release-v0.9.0/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/2021/05/release-v0.7.0/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/spending-policy-demo/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/using-bdk-with-hardware-wallets/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/bdk-cli/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/road-to-bdk-1/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/bdk-cli/compiler/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/bdk-cli/concept/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/bdk-cli/installation/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/bdk-cli/introduction/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/bdk-cli/playground/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/bdk-cli/interface/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/bdk-cli/regtest/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/case-studies/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/descriptors/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/foundation/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/examples/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/getting-started/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/supporters/2024-04-12T16:05:46.000Zdailyhttps://bitcoindevkit.org/blog/dailyhttps://bitcoindevkit.org/blog/tags/dailyhttps://bitcoindevkit.org/blog/author/dailyhttps://bitcoindevkit.org/blog/tags/BDK/dailyhttps://bitcoindevkit.org/blog/tags/project/dailyhttps://bitcoindevkit.org/blog/tags/tutorial/dailyhttps://bitcoindevkit.org/blog/tags/Bitcoin%20Core/dailyhttps://bitcoindevkit.org/blog/tags/RPC/dailyhttps://bitcoindevkit.org/blog/tags/Wallet/dailyhttps://bitcoindevkit.org/blog/tags/bdk-cli/dailyhttps://bitcoindevkit.org/blog/tags/basics/dailyhttps://bitcoindevkit.org/blog/tags/novice/dailyhttps://bitcoindevkit.org/blog/tags/architecture/dailyhttps://bitcoindevkit.org/blog/tags/BDK-RN/dailyhttps://bitcoindevkit.org/blog/tags/Development/dailyhttps://bitcoindevkit.org/blog/tags/Architecture/dailyhttps://bitcoindevkit.org/blog/tags/multi-sig/dailyhttps://bitcoindevkit.org/blog/tags/tor/dailyhttps://bitcoindevkit.org/blog/tags/wallet/dailyhttps://bitcoindevkit.org/blog/tags/blockchain/dailyhttps://bitcoindevkit.org/blog/tags/bindings/dailyhttps://bitcoindevkit.org/blog/tags/compact_filters/dailyhttps://bitcoindevkit.org/blog/tags/BIP157/dailyhttps://bitcoindevkit.org/blog/tags/Neutrino/dailyhttps://bitcoindevkit.org/blog/tags/guide/dailyhttps://bitcoindevkit.org/blog/tags/descriptor/dailyhttps://bitcoindevkit.org/blog/tags/paper%20wallets/dailyhttps://bitcoindevkit.org/blog/tags/bitcoin/dailyhttps://bitcoindevkit.org/blog/tags/React%20Native/dailyhttps://bitcoindevkit.org/blog/tags/Flutter/dailyhttps://bitcoindevkit.org/blog/tags/iOS/dailyhttps://bitcoindevkit.org/blog/tags/Android/dailyhttps://bitcoindevkit.org/blog/tags/mobile/dailyhttps://bitcoindevkit.org/blog/tags/bdk-rn/dailyhttps://bitcoindevkit.org/blog/tags/bdk/dailyhttps://bitcoindevkit.org/blog/tags/fee/dailyhttps://bitcoindevkit.org/blog/tags/machine%20learning/dailyhttps://bitcoindevkit.org/blog/tags/taproot/dailyhttps://bitcoindevkit.org/blog/tags/miniscript/dailyhttps://bitcoindevkit.org/blog/tags/Hardware%20Wallets/dailyhttps://bitcoindevkit.org/blog/tags/coin%20selection/dailyhttps://bitcoindevkit.org/blog/tags/development/dailyhttps://bitcoindevkit.org/blog/tags/summer%20of%20bitcoin/dailyhttps://bitcoindevkit.org/blog/tags/getting%20started/dailyhttps://bitcoindevkit.org/blog/tags/rust/dailyhttps://bitcoindevkit.org/blog/tags/bitcoin-cli/dailyhttps://bitcoindevkit.org/blog/tags/security/dailyhttps://bitcoindevkit.org/blog/tags/release/dailyhttps://bitcoindevkit.org/blog/author/Steve%20Myers/dailyhttps://bitcoindevkit.org/blog/author/Daniela%20Brozzoni/dailyhttps://bitcoindevkit.org/blog/author/Rajarshi%20Maitra/dailyhttps://bitcoindevkit.org/blog/author/waterst0ne/dailyhttps://bitcoindevkit.org/blog/author/Lloyd%20Fournier/dailyhttps://bitcoindevkit.org/blog/author/Bitcoin%20Zavior/dailyhttps://bitcoindevkit.org/blog/author/rorp/dailyhttps://bitcoindevkit.org/blog/author/thunderbiscuit/dailyhttps://bitcoindevkit.org/blog/author/Riccardo%20Casatta/dailyhttps://bitcoindevkit.org/blog/author/Gabriele%20Domenichini/dailyhttps://bitcoindevkit.org/blog/author/Alekos%20Filini/dailyhttps://bitcoindevkit.org/blog/author/Wszdexdrf/dailyhttps://bitcoindevkit.org/blog/author/C%C3%A9sar%20Alvarez%20Vallero/dailyhttps://bitcoindevkit.org/blog/author/Sandipan%20Dey/dailyhttps://bitcoindevkit.org/blog/page/2/dailyhttps://bitcoindevkit.org/blog/page/3/dailyhttps://bitcoindevkit.org/blog/page/4/dailyhttps://bitcoindevkit.org/blog/author/Alekos%20Filini/page/2/daily \ No newline at end of file +https://bitcoindevkit.org/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/_2024-q4-update/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/bitcoin-core-rpc-demo/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/bdk-cli-basics/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/bdk-core-pt1/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/bdk-cli-basics-multisig-2of3/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/bdk-rn-making-of/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/bdk-with-tor/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/bindings-scope/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/compact-filters-demo/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/descriptor-based-paper-wallet/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/exploring-bdk-flutter/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/01/fee-estimation-for-light-clients-part-1/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/exploring-bdk-rn/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/01/fee-estimation-for-light-clients-part-3/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/01/fee-estimation-for-light-clients-part-2/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/12/first-bdk-taproot-tx-look-at-the-code-part-2/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/11/first-bdk-taproot-tx-look-at-the-code-part-1/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/descriptors-in-the-wild/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/getting-started-with-rust-hwi/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/hidden-power-of-bitcoin/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/improving-coin-selection-in-bdk/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2020/12/hello-world/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2020/12/release-v0.2.0/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/miniscript-vulnerability/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/01/release-v0.3.0/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/02/release-v0.4.0/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/04/release-v0.6.0/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/03/release-v0.5.0/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/05/release-v0.7.0/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/06/release-v0.8.0/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/2021/07/release-v0.9.0/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/spending-policy-demo/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/road-to-bdk-1/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/using-bdk-with-hardware-wallets/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/bdk-cli/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/bdk-cli/compiler/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/bdk-cli/installation/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/bdk-cli/concept/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/bdk-cli/interface/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/bdk-cli/introduction/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/bdk-cli/playground/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/case-studies/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/bdk-cli/regtest/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/foundation/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/examples/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/descriptors/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/getting-started/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/supporters/2024-04-10T19:23:58.000Zdailyhttps://bitcoindevkit.org/blog/dailyhttps://bitcoindevkit.org/blog/tags/dailyhttps://bitcoindevkit.org/blog/author/dailyhttps://bitcoindevkit.org/blog/tags/BDK/dailyhttps://bitcoindevkit.org/blog/tags/project/dailyhttps://bitcoindevkit.org/blog/tags/tutorial/dailyhttps://bitcoindevkit.org/blog/tags/Bitcoin%20Core/dailyhttps://bitcoindevkit.org/blog/tags/RPC/dailyhttps://bitcoindevkit.org/blog/tags/Wallet/dailyhttps://bitcoindevkit.org/blog/tags/bdk-cli/dailyhttps://bitcoindevkit.org/blog/tags/basics/dailyhttps://bitcoindevkit.org/blog/tags/novice/dailyhttps://bitcoindevkit.org/blog/tags/architecture/dailyhttps://bitcoindevkit.org/blog/tags/multi-sig/dailyhttps://bitcoindevkit.org/blog/tags/BDK-RN/dailyhttps://bitcoindevkit.org/blog/tags/Development/dailyhttps://bitcoindevkit.org/blog/tags/Architecture/dailyhttps://bitcoindevkit.org/blog/tags/tor/dailyhttps://bitcoindevkit.org/blog/tags/wallet/dailyhttps://bitcoindevkit.org/blog/tags/blockchain/dailyhttps://bitcoindevkit.org/blog/tags/bindings/dailyhttps://bitcoindevkit.org/blog/tags/compact_filters/dailyhttps://bitcoindevkit.org/blog/tags/BIP157/dailyhttps://bitcoindevkit.org/blog/tags/Neutrino/dailyhttps://bitcoindevkit.org/blog/tags/guide/dailyhttps://bitcoindevkit.org/blog/tags/descriptor/dailyhttps://bitcoindevkit.org/blog/tags/paper%20wallets/dailyhttps://bitcoindevkit.org/blog/tags/bitcoin/dailyhttps://bitcoindevkit.org/blog/tags/React%20Native/dailyhttps://bitcoindevkit.org/blog/tags/Flutter/dailyhttps://bitcoindevkit.org/blog/tags/iOS/dailyhttps://bitcoindevkit.org/blog/tags/Android/dailyhttps://bitcoindevkit.org/blog/tags/mobile/dailyhttps://bitcoindevkit.org/blog/tags/bdk-rn/dailyhttps://bitcoindevkit.org/blog/tags/bdk/dailyhttps://bitcoindevkit.org/blog/tags/fee/dailyhttps://bitcoindevkit.org/blog/tags/machine%20learning/dailyhttps://bitcoindevkit.org/blog/tags/taproot/dailyhttps://bitcoindevkit.org/blog/tags/miniscript/dailyhttps://bitcoindevkit.org/blog/tags/Hardware%20Wallets/dailyhttps://bitcoindevkit.org/blog/tags/bitcoin-cli/dailyhttps://bitcoindevkit.org/blog/tags/coin%20selection/dailyhttps://bitcoindevkit.org/blog/tags/development/dailyhttps://bitcoindevkit.org/blog/tags/summer%20of%20bitcoin/dailyhttps://bitcoindevkit.org/blog/tags/getting%20started/dailyhttps://bitcoindevkit.org/blog/tags/rust/dailyhttps://bitcoindevkit.org/blog/tags/release/dailyhttps://bitcoindevkit.org/blog/tags/security/dailyhttps://bitcoindevkit.org/blog/author/Steve%20Myers/dailyhttps://bitcoindevkit.org/blog/author/Daniela%20Brozzoni/dailyhttps://bitcoindevkit.org/blog/author/Rajarshi%20Maitra/dailyhttps://bitcoindevkit.org/blog/author/waterst0ne/dailyhttps://bitcoindevkit.org/blog/author/Lloyd%20Fournier/dailyhttps://bitcoindevkit.org/blog/author/Bitcoin%20Zavior/dailyhttps://bitcoindevkit.org/blog/author/rorp/dailyhttps://bitcoindevkit.org/blog/author/thunderbiscuit/dailyhttps://bitcoindevkit.org/blog/author/Riccardo%20Casatta/dailyhttps://bitcoindevkit.org/blog/author/Alekos%20Filini/dailyhttps://bitcoindevkit.org/blog/author/Gabriele%20Domenichini/dailyhttps://bitcoindevkit.org/blog/author/Wszdexdrf/dailyhttps://bitcoindevkit.org/blog/author/Sandipan%20Dey/dailyhttps://bitcoindevkit.org/blog/author/C%C3%A9sar%20Alvarez%20Vallero/dailyhttps://bitcoindevkit.org/blog/page/2/dailyhttps://bitcoindevkit.org/blog/page/3/dailyhttps://bitcoindevkit.org/blog/page/4/dailyhttps://bitcoindevkit.org/blog/author/Alekos%20Filini/page/2/daily \ No newline at end of file diff --git a/supporters/index.html b/supporters/index.html index 896147cb9c..28e98a3ec2 100644 --- a/supporters/index.html +++ b/supporters/index.html @@ -29,7 +29,7 @@ - + @@ -63,7 +63,7 @@ Brink Sponsor Kraken Kraken -