From effc4d470e352ee77d4d7fe0ae8d51a8add49b76 Mon Sep 17 00:00:00 2001 From: mDuo13 Date: Fri, 21 Jan 2022 19:09:50 -0800 Subject: [PATCH] Build a Wallet: finished draft of tutorial --- .../build-a-wallet/py/5_send_xrp.py | 4 +- .../py/6_verification_and_polish.py | 40 ++++--- .../build-a-desktop-wallet-in-python.md | 107 +++++++++++++++++- img/python-wallet-6-err.png | Bin 0 -> 22815 bytes img/python-wallet-6.png | Bin 0 -> 11391 bytes 5 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 img/python-wallet-6-err.png create mode 100644 img/python-wallet-6.png diff --git a/content/_code-samples/build-a-wallet/py/5_send_xrp.py b/content/_code-samples/build-a-wallet/py/5_send_xrp.py index 17ad8fed31..d2a3258c1f 100644 --- a/content/_code-samples/build-a-wallet/py/5_send_xrp.py +++ b/content/_code-samples/build-a-wallet/py/5_send_xrp.py @@ -107,8 +107,8 @@ class XRPLMonitorThread(Thread): "amt": Amount of decimal XRP to send, as a string } """ - dtag = paydata.get("dtag") - if dtag and dtag.strip() == "": + dtag = paydata.get("dtag", "") + if dtag.strip() == "": dtag = None if dtag is not None: try: diff --git a/content/_code-samples/build-a-wallet/py/6_verification_and_polish.py b/content/_code-samples/build-a-wallet/py/6_verification_and_polish.py index a724711700..e0ffd6aafe 100644 --- a/content/_code-samples/build-a-wallet/py/6_verification_and_polish.py +++ b/content/_code-samples/build-a-wallet/py/6_verification_and_polish.py @@ -1,5 +1,5 @@ # "Build a Wallet" tutorial, step 6: Verification and Polish -# This step adds sanity checks to the Send XRP dialog, along with some other +# This step adds safety checks to the Send XRP dialog, along with some other # small improvements including account domain verification. # License: MIT. https://github.com/XRPLF/xrpl-dev-portal/blob/master/LICENSE @@ -120,7 +120,6 @@ class XRPLMonitorThread(Thread): account_status = { "funded": None, "disallow_xrp": None, - "deposit_auth": None, "domain_verified": None, "domain_str": "" # the decoded domain, regardless of verification } @@ -149,8 +148,6 @@ class XRPLMonitorThread(Thread): account_status["domain_verified"] = verified account_status["domain_str"] = domain - # If you want to also check Deposit Auth, here's where you would do so. - # Send data back to the main thread. wx.CallAfter(dlg.update_dest_info, account_status) @@ -164,7 +161,7 @@ class XRPLMonitorThread(Thread): "amt": Amount of decimal XRP to send, as a string } """ - dtag = paydata.get("dtag") + dtag = paydata.get("dtag", "") if dtag.strip() == "": dtag = None if dtag is not None: @@ -212,6 +209,7 @@ class AutoGridBagSizer(wx.GridBagSizer): self.Add(ctrl, (x,y), flag=flags, border=5) self.parent.SetSizer(self) + class SendXRPDialog(wx.Dialog): """ Pop-up dialog that prompts the user for the information necessary to send a @@ -222,7 +220,7 @@ class SendXRPDialog(wx.Dialog): sizer = AutoGridBagSizer(self) self.parent = parent - # little "X" icons to indicate a validation error + # Icons to indicate a validation error bmp_err = wx.ArtProvider.GetBitmap(wx.ART_ERROR, wx.ART_CMN_DIALOG, size=(16,16)) self.err_to = wx.StaticBitmap(self, bitmap=bmp_err) self.err_dtag = wx.StaticBitmap(self, bitmap=bmp_err) @@ -231,7 +229,7 @@ class SendXRPDialog(wx.Dialog): self.err_dtag.Hide() self.err_amt.Hide() - # ✅/❌ for domain verification + # Icons for domain verification bmp_check = wx.ArtProvider.GetBitmap(wx.ART_TICK_MARK, wx.ART_CMN_DIALOG, size=(16,16)) self.domain_text = wx.StaticText(self, label="") self.domain_verified = wx.StaticBitmap(self, bitmap=bmp_check) @@ -265,6 +263,17 @@ class SendXRPDialog(wx.Dialog): self.txt_dtag.Bind(wx.EVT_TEXT, self.on_dest_tag_edit) self.txt_to.Bind(wx.EVT_TEXT, self.on_to_edit) + def get_payment_data(self): + """ + Construct a dictionary with the relevant payment details to pass to the + worker thread for making a payment. Called after the user clicks "Send". + """ + return { + "to": self.txt_to.GetValue().strip(), + "dtag": self.txt_dtag.GetValue().strip(), + "amt": self.txt_amt.GetValue(), + } + def on_to_edit(self, event): """ When the user edits the "To" field, check that the address is well- @@ -282,6 +291,8 @@ class SendXRPDialog(wx.Dialog): if xrpl.core.addresscodec.is_valid_xaddress(v): cl_addr, tag, is_test = xrpl.core.addresscodec.xaddress_to_classic_address(v) + if tag is None: # Not the same as tag = 0 + tag = "" self.txt_dtag.ChangeValue(str(tag)) self.txt_dtag.Disable() @@ -320,17 +331,6 @@ class SendXRPDialog(wx.Dialog): self.txt_dtag.ChangeValue(v) # SetValue would generate another EVT_TEXT self.txt_dtag.SetInsertionPointEnd() - def get_payment_data(self): - """ - Construct a dictionary with the relevant payment details to pass to the - worker thread for making a payment. Called after the user clicks "Send". - """ - return { - "to": self.txt_to.GetValue().strip(), - "dtag": self.txt_dtag.GetValue().strip(), - "amt": self.txt_amt.GetValue(), - } - def update_dest_info(self, dest_status): """ Update the UI with details provided by a background job to check the @@ -367,10 +367,14 @@ class SendXRPDialog(wx.Dialog): self.domain_verified.Show() if err_msg: + # Disabling the button is optional. These types of errors can be + # benign, so you could let the user "click through" them. + self.btn_send.Disable() self.err_to.SetToolTip(err_msg) self.err_to.Show() else: self.btn_send.Enable() + self.err_to.SetToolTip("") self.err_to.Hide() diff --git a/content/tutorials/build-apps/build-a-desktop-wallet-in-python.md b/content/tutorials/build-apps/build-a-desktop-wallet-in-python.md index cd1577e80e..d19315bb41 100644 --- a/content/tutorials/build-apps/build-a-desktop-wallet-in-python.md +++ b/content/tutorials/build-apps/build-a-desktop-wallet-in-python.md @@ -403,8 +403,113 @@ You can now use your wallet to send XRP! You can even fund an entirely new accou **Full code for this step:** [`6_verification_and_polish.py`]({{target.github_forkurl}}//tree/{{target.github_branch}}/content/_code-samples/build-a-wallet/py/6_verification_and_polish.py) -***TODO*** +One of the biggest shortcomings of the wallet app from the previous step is that it doesn't provide a lot of protections or feedback for users to save them from human error and scams. These sorts of protections are extra important when dealing with the cryptocurrency space, because decentralized systems like the XRP Ledger don't have an admin or support team you can ask to cancel or refund a payment if you made a mistake such as sending it to the wrong address. This step shows how to add some checks on destination addresses to warn the user before sending. +One type of check you can make is to verify the domain name associated with an XRP Ledger address; this is called [account domain verification](xrp-ledger-toml.html#account-verification). When an account's domain is verified, you could show it like this: + +![Screenshot: domain verified destination](img/python-wallet-6.png) + +When there are other errors, you can expose them to the user with an icon and a tooltip, which looks like this: + +![Screenshot: invalid address error icon with tooltip](img/python-wallet-6-err.png) + +The following code implements account domain verification; **save it as a new file** named `verify_domain.py`: + +{{ include_code("_code-samples/build-a-wallet/py/verify_domain.py", language="py") }} + +In your app's main file, import the `verify_account_domain` function: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="from verify_domain", end_before="class XRPLMonitorThread") }} + +In the `XRPLMonitorThread` class, add a new `check_destination()` method to check the destination address, as follows: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="async def check_destination", end_before="async def send_xrp") }} + +This code uses [`xrpl.asyncio.account.get_account_info()`](https://xrpl-py.readthedocs.io/en/stable/source/xrpl.asyncio.account.html#xrpl.asyncio.account.get_account_info) to look up the account in the ledger; unlike using the client's `request()` method, `get_account_info()` raises an exception if the account is not found. + +If the account _does_ exist, the code checks for the [`lsfDisallowXRP` flag](accountroot.html#accountroot-flags). Note that this is a "lsf" (ledger state flag) value because this is an object from the ledger state data; these are different than the flag values [AccountSet transaction][] uses to configure the same settings. + +Finally, the code decodes the account's `Domain` field, if present, and performs domain verification using the method imported above. + +**Caution:** The background check takes the Send XRP dialog (`dlg`) as a parameter, since each dialog is a separate instance, but does modify the dialog directly since that might not be threadsafe. (It _only_ uses `wx.CallAfter` to pass the results of the check back to the dialog.) + +After this, it's time to update the `SendXRPDialog` class to make it capable of displaying these errors. You can also set a more specific upper bound for how much XRP the account can actually send. Change the constructor to take a new parameter: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="def __init__(self, parent, max_send=100000000.0)", end_before="wx.Dialog.__init__") }} + +Add some icon widgets to the UI, also in the `SendXRPDialog` constructor: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="# Icons to indicate", end_before="lbl_to =") }} + +Still in the `SendXRPDialog` constructor, add a maximum value to the line that creates the `self.txt_amt` widget: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="self.txt_amt =", end_before="self.txt_amt.SetDigits(6)") }} + +Don't forget to add all the new widgets to the `SendXRPDialog`'s sizer so they fit in the right places. Update the `BulkAdd` call in the constructor as follows: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="sizer.BulkAdd(((lbl_to,", end_before="sizer.Fit(self)") }} + +Next, refactor the `on_to_edit()` handler in the `SendXRPDialog` class to perform more checks, including the new background check on the destination address. The updated handler should be as follows: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="def on_to_edit", end_before="def on_dest_tag_edit") }} + +In addition to starting the background check, this handler does some checks immediately. Any check that doesn't require getting data from the network is probably fast enough to run directly in the handler; if the check requires network access, you have to run it in the worker thread instead. + +One of the new checks is to decode X-addresses to pull out the additional data they encode: + +- If the X-address includes a destination tag, show it in the destination tag field. +- If the X-address is not intended for a test network and the app is connected to a test network (or the other way around), show an error. + +One tricky bit of writing handlers like this in GUI code is that you have to be ready for the handler to be called numerous times as the user inputs and erases data. For example, if you disable a field when some input is invalid, you also have to enable it if the user changes their input to be valid. + +The code shows the error icons when it finds errors (and hides them when it doesn't), and adds tooltips with the error message. You could, of course, display errors to the user in another way as well, such as additional pop-up dialogs or a status bar. + +Moving on, you also need a new method in the `SendXRPDialog` class to process the results from the background check. Add the following code: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="def update_dest_info", end_before="class TWaXLFrame") }} + +This code takes the dictionary passed by the `check_destination()` and uses it to update various widgets in the Send XRP dialog's GUI. + +You need to make a few small updates to configure the maximum send amount in the Send XRP dialog. Start by adding these lines to the `TWaXLFrame` class's constructor: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="# This account's total XRP reserve", end_before="self.build_ui()") }} + +Then modify the `update_account()` method of the `TWaXLFrame` to save the latest calculated balance. Modify the last few lines to look like this: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="# Display account reserve and", end_before="def enable_readwrite") }} + +Finally, calculate the maximum amount the user can send and provide it to the Send XRP dialog. Modify **the beginning of the `click_send_xrp()` handler** as follows: + +{{ include_code("_code-samples/build-a-wallet/py/6_verification_and_polish.py", language="py", start_with="xrp_bal = Decimal", end_before="dlg.CenterOnScreen()") }} + +The formula this code uses to calculate the maximum amount the user can send is the account's XRP balance, minus its [reserve](reserves.html) and minus the [transaction cost](transaction-cost.html). The calculation uses the `Decimal` class to avoid rounding errors, but ultimately it has to be converted down to a `float` because that's what wxPython's [`wx.SpinCtrlDouble`](https://docs.wxpython.org/wx.SpinCtrlDouble.html) accepts for minimum and maximum values. Still there is less opportunity for floating-point rounding errors to occur if the conversion happens _after_ the other calculations. + +Test your wallet app the same way you did in the previous steps. To test domain verification, try entering the following addresses in the "To" box of the Send XRP dialog: + +| Address | Domain | Verified? | +|:-------------------------------------|:-------------|:----------| +| `rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW` | `mduo13.com` | ✅ Yes | +| `rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn` | `xrpl.org` | ❌ No | +| `rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe` | (Not set) | ❌ No | + +To test X-addresses, try the following addresses: + +| Address | Destination Tag | Test Net? | +|:--------------------------------------------------|:----------------|:----------| +| `T7YChPFWifjCAXLEtg5N74c7fSAYsvPKxzQAET8tbZ8q3SC` | 0 | Yes | +| `T7YChPFWifjCAXLEtg5N74c7fSAYsvJVm6xKZ14AmjegwRM` | None | Yes | +| `X7d3eHCXzwBeWrZec1yT24iZerQjYLjJrFT7A8ZMzzYWCCj` | 0 | No | +| `X7d3eHCXzwBeWrZec1yT24iZerQjYLeTFXz1GU9RBnWr7gZ` | None | No | + + +## Next Steps + +Now that you have a functional wallet, you can take it in several new directions. The following are just a few ideas: + +- You could support more of the XRP Ledger's [transaction types](transaction-types.html) including [tokens](issued-currencies.html) and [cross-currency payments](cross-currency-payments.html) +- Allow the user to trade in the [decentralized exchange](decentralized-exchange.html) +- Add a way to request payments, such as with QR codes or URIs that open in your wallet. +- Support better account security including [regular key pairs](cryptographic-keys.html#regular-key-pair) or [multi-signing](multi-signing.html). {% include '_snippets/rippled-api-links.md' %} diff --git a/img/python-wallet-6-err.png b/img/python-wallet-6-err.png new file mode 100644 index 0000000000000000000000000000000000000000..ac5e132abd8da3d0659b1bcb908272678e9a16dc GIT binary patch literal 22815 zcmeFZWl&zxk~WH4u;9Uh1$SQDU4jO8cX!ud!3pjb+=4qng1fuB2Y0xeoHJ+6)Sa(x zP1XJWO;UNY-@VuFwYpdLv$~%q`6e$bj)Z`V00stzBq<^C1q=*a3%I_Ag8@D*e@6Cz zf#E!OD5*Jp(RU@XbFeirw=yPjabXr{3L?Vl(0%gfW%K3(kAHSImchk_g~UEPRi-P${gUp^N7 z{I-I;lY7?|MQykUFZW$-xcg&2t!(PNxC5)QZs)GYKz}T+(2n|Sd~aR#Z&Wt*UtaU|+*8xddMex+s4EoDGSU6DSLSh={6RE2rFgVb zur0)m-+nclYNZ*sInjE9eL|#(KQc>7Med9Kf;lYz6zd-Roc#pd+=~5UyBzz0!PU}T z7wj2@;8Z7Wn61yMgxGWA*1K4(9JD#fc}Vf+r1nP7C6mG?*V1KoPTA|I-t>!2Ip5(d z9lI#0PR@}}Ij(YEZF5FbNTRZp>0%APjdE1B8=vV$|RBB2;|U7n<^zx zZL%RL$XBy)os8RX^}-sRb6Mz7!E<+o92OQfz3rpEl|dCvIe*OkDAJL?jh%7D6ew*~ zGFRU8qtUZOF0(s$%kM+1#I!0g+ca)x7*j0&Cl!aH!WaW7s>1IU7R7rphO(S{-=V|W z0?}k7KP7#!FwdWpHS_El+%k<_EVYEyO)r- zbERO^>|ijF;51wwG`b!~_>z?LKbJP9pz;46d9~h-5V{SCedM#@4_ib-8o{e7L|`pu zllN{LWyLwt${BX&_`bEknVuq!AnkHekx?sN)H0or_rM?a@l)`YCVyZ>p;i)o#`Ks?HNiN8@U^jothatX|k5FYg(2w{8CdC z4SGM3mnHP>4hd;Q@c8#BX!G(d2!g^((d291(YD!Ip!MP8^1$Vgh$FTq7eJv!WaNJh`Y!BvH ztw49wvtv~Y?;e(vhMl9uVa4@!AH)DyY|K>tZd&FrD#9OuL+(!GRG^C}3}uLhI&xN2 z_8>G4bM@0Si9~ttRzCM$OT;U`MGTJvUfEZ z2c9G_RqSZK(n+eJL%p1FNS>;$VQVhfWU|D$;387Nh^J{(>eD1wX)3x#0>Pyngk<_7 zHi0#&oZWI^@0uj=Ew;{NiW`|(w$Ta(c8;g0^iAw;a3QHAY`~}O^cNjhT<>CAY0OD$ zv3f2M;}G#KSq%*3DxCKUqD0zyg&oFq23fHCo@I4^dsm8bWonO3mVfBr^GxKR6dkm} z@!(!w$IK8tWa6$0I=qqENXK`@JDZeeAyv1@v^ z1BP+t|Ix!mklA$(Wxn1tPcPbpd&*}4Dl%tfYxj}CLpFAj^3zygW2iu{is%=;lCc^v zKA0xBb%LGa!1GpXIZ#-5g7R_D@u zmpUFjE^YDLbZuX(A8#6tVK6RmoeO&5}{>AigXsDRUK%h4{kY`uQEoYN5eln2TR|7b}e!dX2Fa!o5V@GSV}jQf0YmfMp)F@Q>~sK=#7f!opB(w z+NZsmB`|zUHS2v?PXQAT2079c)4@@4rF`?rx)pbxss~ z?IShK^JIpFUDi4gsBXmFV+$>&y}Kj`f--5wQ-X8fbaId>gq~B7)SDL61#cb3PhY|A zvSxaJ@!k&y?!!e2l6$>Tsf$_^y~CNLb@xp6o}Uzson~yyBZrufTnAyABuqjMgK@p+ zIcJf9O4=oS0{H<-|4~egq&CIJ5;wg_`fP%$!U8_{h#eAg-XiUbn(+y?D{XxLJ~B{T*7IJ{jfD98mBUvyphN! zoyBiLz0kVr^dgJ$-d{cOE9uaVvM4x2D5*0d73yk_i%B`ZllAlvJ+Vs8mbV$l%B#KA zG0W-NA5f}pN{Qk$?pKG2r>WC`OoQI(YeI->wcNhLcxo057Y_DhR>wTx&}=B~Q$Wns zCt;v7ZGm>Q6PF?(x;hk5Y(_uxVyx}u$T3VqfE1_j5IY`_FhVIzB?FBgijlro(4RoQ zQcpA5s||f8F-Zd{@pFR6F_4(hFZ|*|5zY!7QK5VtnWPyFHhCLI?I|vIE*djoAWu^R zDO4zJYcRhuISGkq+9|#xg__fn9PRJK+TQZKB+^lQox-oBWKw4We}cSzk1_P&Bl|8& zn=eNE`1#$gDYgstg&B;%@5hyY)_Y$vOSoY#3%dML3REM7QZ(>Z&032fqT+0dtOlL& z^MxYrgaJ8oDg`enJwyBi3q?S09`m`G7}LVw&gV9vjKO> z=2Oi%IoS*T9P8@Gor`Sk+1y!@AC+A;c}&FsItQcsqDX@3 z7+&6@h-z&x2cG4F%WU4j@>785eGZ)fc?t6;uPI!C!%+Q*MOr~8OJw{Q6ff{Upk*D& z-U5Glf#3kVTRQc|oDN2a5OpRZT*DpPb%s5@iy)*!YsudVic5K4sWi~`gK};h!`Ipr ztG5fpzrv*Q63+*^);X&Pt48B12v09e(eIH5SEt*_#a2hX$QnJg$ zY*5R+?x!$2PL~?@fDZ8lgr_1(+ zb_$i(GVa9pTP53?Zi{s5=P2-`cq4Kd$J8~ZjIrb`R7k_SYbPtO(=6+>KnP3G zmr~aduxDI?=)ecZCqi zuQuqY6uBfp>oTAw_M|n4YDd-7S(}-HSf2K{E9rb5vK%SuGm-}>gvjZ=*#_? z2>82K4apSf@A$?&V{gavB+bi`evNI=x%j-;_}qx*{GO?3X)v{M36eA!V~Ou5uW`r+ z|AN)SRX$6sAd8rpp2C=PRi*8|^^>CO2P0}U9QuSgIGWLd052)Yj-BUDNwyP&UWZnfn^KPNcOusl*%;;dl>i8uy`%SzDB$XpwaO%M&9Z?m;ORlg zh3j~^?*;{Zorf`>ke64{vT;yp6r4Y&;GoQFQm5?CDPZ40mb$Ky?|>sHP~yt9m4=D5 zG0Qr!b)i_R2D)skKopPkLHcxpZ4-1kL?y@IzRP?M zzZ^@O#CmJ}8%R4S7RW7(ZZaj?N=}JewUJybI{P^~^u)--BcR6{WYVI7NMKw841$zo%~f-nlV9 zC6XxV{y+!~Cu^XWIdw= z1EFY9lHh6n`{V|SOE5Oc`dsh!&Cj~<0sUQpCSbNFL(l~nU_I{Q{;GJe;<7m=%rrbW z3#Ws13@rL#aPLM9xi2F66>x(QBk07p!3%Iogp=xF9#)aUNm0Mc`uYE~=6#Yzjnb=y zDZJ)40`(ey&Sd8)JNj8h5iWVi$?j)_w@=9G8uN)$Tg+I4ZJaPxtsAkiPJ0l=+>ou* zE955Mj!SZA*sI@;+I;KB*Sc{p2sH0gE{r`c#&>*!CXx%;0+6moT^%sl#mM5Vy=rqh zx%!QcU1f|@@P7)}?5Z8@JjGKotA>r#v|&U?YMJF52{!zc1FcrjBu+VuJ_=$R<5bnj zc`)JWVREGiBC`;C%axe-m3NXB#yurM#bZ;gON!U8LtV-%fRHM;(i!V0(~KLmcuvRh zE_Tge3CIpIl0@nQ=;+iTO=8olJlTnRW7um@~TBsBV1huTwExqu8IpoHx zTs9r|oakBsb_LJUVdlR`2v3M+owDRRg(NDmcS-wq1y*-p3!fioFFpT$KHg5Ni{}SJ zLN7&;LG4FU+fu4r?z@|Z%jl)t70sknCB7lKI++@9j+VoR&|gVN@@YoW1oc6X)RKOD@2I{X^Nt;$aoM~)bt zzi>TyHoHYJPKsu6Q(j|8Qz$c^cUfyqdH+p$y-mjqhI%nQ@ zep?KYfoShO-kMUNf*1qEydx`|qV^ph5T$_hCJJQ<351#IDmGR?NMmklBSKZ>K~#e! z54w(_=;mizJJipHtP6oiAtB!bt$yYA2-b7c9NbnTj>fotRk_P?gkTO?0e-BJhJiDRMSzn<@9-);$Mm2?P7*6{L${% zZlyWNQ)E6$p67BE-dv}K8kIn-^$=er_71L}Vfn7ftecoe>?z(v^EEw7GHGY)_knVr zV1A&9$m?7Q=Cb>UhydOa;}<JjwLD*m*mlQpOFJPx=dRBto{fS20u&x683v| zBd+#)#M+oz$cWwNaE-|jI2W8yxyB_w4;4DuW|=c46ZxaxHJ^S)GyN)~F!H68ec;jW z8C2hgoe)|HQdb{Q`a+0>A)qZ;!;e=iZotaI`gB||yrVd0EMY=IoT@r@Ek=D{o@9kLnn^Jo{; z@7-FtrUxoLCSwO8D`wN`<8qANt2A-sE$apn)KA17vt}DTQ*xo|a`!of#tCR%PlxMr zEfk>&u)Ni&17hm(#h-pNRdhVJg%@N0%xig^{-$NfJZYv`%8JDfSwNJ)^QVBoXL!Fx zCQ7+%d>-r$jTnM+>}&c|Ly*F_jLbwp*Zas$o1V=Qm?m!~#dmH!?$hw!c|OCuFQa1m z5n|hscEn+(@i09OqT`I9IN85v-neLPEF+LBqA~DbCq<|@6r_nMD7FgE%(JM@8a6#J z2^8Wgmi6;!PJXZDXVm+Mc=(eIBSQpp6jLD%SB2ROvc|%3Mw|q{HPer=9UfUX{A$MF z3nAhQVyAJ!x#9=yDMpRm=cy4$3mpr1Lk(=k@A`2>G|f%iC6SU`;RhnIj8kKZYW?c1 z*|YUrsq$kv>j_jsUPlln%O%8SLH3x)l-jl9#@P^0T(w3<`d1y~B=J%!0H2wFWH9e!-Mua@0Vf8N5dq@B4{fPpKHgo7Bt# zaROu&r9iA;7bt^q6-DW1`ZT`%(B*eDm~_>o6r-<{UNg-2G6t$=_{uR}#>bi5RTJ z!Ro+oOiw9D-?3|Yf0IPYjvHFAtQ58+5_oo5oD-9As!=f1LAlLOhp3I;>bc=y3uA>1 zRFFp&coo2F$I`;lGk5(EN3Eby^pmjMez6*!GTQZhzznjV2=#rzj{KjtwGfqLZ+X0@ z@{?;{ZKtbik4z+-UQ5sF`J(z*sQth^*lCBktxBeq*92nT=oQFYdyz*W#no?-5$`|f zMH9QqXT`(VK{}elxcboDFK?dT6kaFFE{^SDs$323p=_qu$0Ck_;-D)ZTZCVGPlZwT z*owC}a3ubK(Wl1`Pow`XVhxYPW1iecHtj=VgkdPk*gO+%v9FDb-mK;6;SjM+%+r5k1gUoi&hmvX};;sk1o!^zFmY6RSOGAVwF8!6fQaN zK%YcfcsQ+{b+c($%W#1aB*?JAL(SD-Ehd#Q zw=?ELW#3Y2eNG+a0@RdE}G0H6DTl-Zqn*!;j7Agb=#J zLW$n`d8Q!RHx<*FTFCLEfQodEG#c6Ec_o`5TN&w z?j6f3(JhY=B&R4E8}?OHgv+Q%7E44F3cFnBJ$0Z)jxuaq(u-hRFj-HJf*l>lU5%Qv zo&D@gr!p#(uK<2VI)?U--;dvg!f_aQR{Pv<@mE_=P!Ej@)Cmmz3YJW$-(#~ZC|4a} zf+GraAd)cGL~u$(nlcd)p^Y(j*M4IUEo8I)ddX*>jA@*3 zm+m(G;~g5mgZBAdq+_SCziIG@lSL+nG%ePmnz^A0h;>tPv#8`Cp23Jmh11r4c2Ie? zmBLq_vapiYF+&9kp4I>!O@y*2H;~!tx7nOzRZQ+>W3+Go_2=y`{mKxeeGt^+L@hpl z`0sc%Y518=KLu?H?1gW6Awp`m2;}W@to21raexPmbtJ4_s2+A)Q5@7IV(~&e)cc1Y zB^DfmCvkVI#}TJh(?FZU-}C(O5nGFgez5h=WVN*!aZKj}Gf!2%fZ^bP5<`e@JH0$T zy=i)0no3_o3Ep$4Bu&H51KL%%=0ICYO-7o_(AJt>-^kX$nBL9W4rp6}f${LW+36cv z8aojg7@L~g@RFRh{30PTH{vBxWtCx+u@g2nGnepiFjn-CRWkIjG~_fQ;pao(apM96 zSQ|U(6S-Mi**J2!@sj+N%LQD&-DV&m`a8tQl9xnHMxIF6*1?zvL=U28q!V>BcVQ;s zLm=XDFf!r#A|m!r5x_TI5;G?!J1zzWS65ehR~C9(2U7+nPEJk+MrH9ux3wX9lc{fD>+Hl!LIOM|`j7Ql+sVlMOT3NaKdk`R zgTYPTj)94uk-^%U;oskIbP{y|g#2?s|Lq%&NI_cXO8o!AGgwvY?a@av2 zVW7CI(QHWNA!Gdl+zr#`0vodGMSu_1_wO`lcY;NL{ZIhX@psc-e~t9lb<1c)+V z%9IHV#8}ec&&U;a^d2`@khAFUd>7OwahAcjT?~ zolI;Uta(YK&260B{_}y7xwWyPlm460nAkwf9L$UyOu$TRfZYEaq+;yg2sqrEOeRKp z=6^gfGUO5iBV{WZ)YRq6~WBT`%w{779lmQH@|K_KF`B+MOw5$k#J{>1J8#^5% zhY=Htp^1?(lM(x0HU1^t(bmMtRo}r_&=k-V&<1dzzuFK{{a3fv+4~jEp1<|5TXaEvo-RV;+Y8mrZ#74)`}10N(q@Jpf(+v|{)d zT>aB#Z)W_z`0Jm0@qcj!K=l7k@;|cg|C;N6&GkRB!2f9Q|9aQ|n(KdLf&bCq|Mjl_ z-^_*Zp9PPx4Uh%70_Dt_`y>uf3c(nB78e0~efyo=Ru~7|fwz-TcLW1_hyL~l94s~c zZ5XVRq>L!+E;tq>42a1wvj7Z?2uxB$P|0oaWX0VH!??Nc+}7E(nVSY)T>=8!AC;{W zN?rg;L{PMmW%)x=wqIkmNyR2xKS{z^j4$mL@*1+33LKNaHtE_ zm2IkP{AAW#oE$hn$qUM4xTt7sS-#3FUW)X-3 zp(;>?B-Vk8m<~)KIC!mpUMMg)DUZ~q5hOD>LFGSasesL08z*R)?TYlUTtRW=ZMUKd zLCo>m$!ILHwj@D>e>uWbjc*c*%(yJTa1t|j1LWFp2AL#iDXv%zZ{!4ofSQ398aAbr zC)RT+)8d^B60xv>c~xY;suY$^3}WOT=D-$?xUA?DS*UV8?C5uS1f$PWNvygPi*&zR z29v|&*FD)msB@~*IK-a&z+!rT&!Js&;Xr^fyDrH|>vHpI_;G=vS{{Vp@saoav*y(4 zYClb7g)+s#(M=@C}ro@>@tQqW_QvzILdH*~E19h&FfDY=Q3To`j0$N34lRt? zd0mB7+7CTFidnQQZt^Rvtm*x)Cat@~)X;)pY;Ls+Ixwd@Vewjza?9owE7RAfw(ZPe zaA+aVzzOSNI{3yE!A7k z<;Rb1D5BU1-!iAytbM}wxiQk+gUlk8gt9Gl zOq)veG?Ut6^v+7|(aGJ>v_J5-&S|<%Rj8tQ#J5VwxF|>(D1ZSt=eqL0VlzE>O3jDi zO^&;FYlCaDndLW^8?SRIW43hbNQOGx+nP2V*5}%(%%rY(u-B^MU?ZpRJ!Lu;-K+_5 z?HREFenhxR2qnVSXYz0Ym0magpvhXUXy&*lQ^b}a9PZN@uO2})6u7D>7p^I!t!$p3 z(E8;jY0WyVx##*(#ZEBeHUjO@pe=2v=vZ z5|4{!{bIR!(9y<0XZhG|JN})lk9uPkplhJhyG{)=<1HFIz^O;_AkM81KJQL^&hHcO zQSA<6d9~vEbt+Nlu0&+YRyfh@YuPI7t#x@Oh0|61n!bIt3tB`r@2ljV(w<`Fx}Q!* z%^juIqCgJG(+cGp$|QPhTYmN~Ymi2r1KEuzLSAR&kn!|VXkhL`bFlrLz?Hr%^>bP_kxm}!h z!h6Em#u=%&mL+m#8k0$i8#LJa<*50g%#1s~kz_h&P-QXzWFN>uko$Hq@X*LmN^ zrPGcLJH3XaCvW!;b7#Nk6q(lm1Igd34BJ#YnjC%ju0puej~z0;U+aWJC_2Pf{s;M5 zf344R{=U;Cik3T~yz#=)Q}f2=+pEkAStYmsr?G&EHMI(ow#HY8g#mRZ{0aETkYME3 znb-)@4@UR{cABzhd4G%pU zI`lC>zQdWSV}}MKe#kcos{BjyowD7Csut!?tx9SDoSh9D<`;hQf6d-qt?;YUD0kbx z&i}kVhWg|3n;E1nEEu_HRcBDq5oupc+jeDmxEB zM+7+hB$`kt%tj@@Ogw@r+<$lIn3Zgc!AGV~9c&M$u%z>Or?$58n4>C`n~bDR6v*H$ z)|!(>0s<{a>F6S-3gytTu*4p3PVxms>JShRBxGcuW5yFYFzf6Or>Qg=Y-CDR%KzNl zh$&!Th_-i0D0zXlow%r|sfF?;Vp3D7{b>M6-8Kz*QwQTm*ZWRfs_WEYUtW%mdytBM zFvLIYUwiQI@cdjlndCrg%w;Q6q0!ON!HE>B)u7WTS1Hj_Fn2z(<6y*%bo2BytWx`- zPi7}-T~rbl5(3MWft(>5B9IC$S#3IB5m~rmNK`hpH%Y zFv}sc%Z~X!d?MCto-7Y{76k)cUXyo>g|lsR?mMyHB=ze|ZH1Xn6~ty{>Kye&;&Wbv z?ap8-E2n_W(Sm}4A`MKN7Y7a3JN#9jd0>!mmGX9?2zjNftY{%&k;&NERVRODM)@7) zH4l%{7!75TMdfF18JN(Ugd^4T^!5yE0pAUQ+trr{w)?lalL+i=Z1r6S8QiWWgYi@| z6&zGl@Z~C>0sCuB0Xq{K9&XqlL(FqCZ-~@vfX`t^bTnIDT1$Yt86&ii^}quvJK#!W z&>fwxGW@{hR7AGnd`jU=P|uA*hQ2_K;kR9TX>-ZFi~-Xz3OR7~?Ns2>gb%IJ*cI{a zBu;v&KxQjWj<0wo3>_W4>G@`9rNDZnIUzY2$K`mwGcTGiOm@yr9AxwB*@av>xuY9} ze|a5c1aE4*Gyii^XhcN(m8Fn(uZ`=U&tGt3t%bfd#E2CX(upMi(--1IzqH9==B^AG$%6d_v56tp7q-ww4#k)CP5YFQB z2D`i8%TjN3V`58-+~^Kz;Vmd}ud1qwz-0})KAK}O8z;)m%Tup4dv7|H`H74yP$5t3 zJpn=FijC=NtNZa%14`V0h@BndNAD|z{3&oRZ*L~t$RCA;=OiV0$p0!2h zb`db@f%)DK>8e#3Kz9ct8J;ZG8JU`H9UKe*tLyq983zXkXIH)z&d{#c6NU*mb#L@X zIY1e34yS$88q+cIPoJPmX9n?XTRVAgm%DdI(o%Rm*;GnroH699(LeHeC9Bt%#HOZ7 zsj1EEoMn;Th%hX%{biH#ro{ya=Rb}zKv08QCBWT!BQvO6kGh`jY=X{8vQksBF zz{O~{F5vTYF8*+JAZ%kppXGG{&(L;-2B>BT=wP!{YNd0aANK3@`E1^bZPnw5ZgX=p zG&FSm>R@U(ndvJE|5HqN2ufU9+R$>7=T}TwhW9u){crmo8mhPX`x}yn zc!`9L4&hKDUAGzNVp>rST#qeHz1j%=b0X~qpycaw3?p}%@lc}ucK`bV=@g*`n-#Fo z=xEcYyK_W*4%Dxm>l$pt#QqIyK54QUT<6igPu+EyPfuPZ!^xe1>CKkv>8Plvq*FoP zc7~EJC&WLV1Cub>Z;Eg^?GwSzi%>78KN&QH)syKB!JK@ny{s^nA|&mM!WrS;?Z2nuxII^fh?rPn=^@HTKIz-jm4lg5Y%n>#d$KjJOQf*ex4C6Ac?N(8J-A7}+#8#! zx7KXfqe&GD(sW=I63x+6m{0VMfU@~y;i00KH-UwCSEN^X zw1zeo7AB^sloSFiGJb~@TaCpG^&+rlednb*OV#gjap3bUt_&L+8??IZ%hxQ%gBWP& z=-31Ej6Cd2%{z!?CY23@taei1@S?zT2 z1ptE38H*l0#-C;rBy2Xz{Q=PL{goPo^5RW`e(bo;iG;8kd-X4d2@c`6yO7$6`K7_VoPhzgCf-$!dwESSZ_L zQp4naVRYJdtFyVa<$k;3TD4$hlQeq&_;~s9c*bHnif=NS{;kKL69{Vn@+H(4I;Llw z8^L{aw1$W2uH(*uMleisSKU>zwInKchAfx!p@@nKR#THZ1{PNK+|khW%&!+vtajyD z3_?TwU}lE}Xp0sX>8$;+BBJkGfu{M4e%8c6?egS3xeK9$iwuo-K-sn{bm-xHEIc~- zA3$A5Ss+Si5Vb#iK}CA__`4224_$`BXlT_ouL5MdE1b2D{PaCuzVMjT z+y}#H!!L{JRK>TSWGXf0l4IHoC@2g#?>f6*NEJ@0Jiu3fKjFxbwPM3Nnyc&^8VU!( zG%hnU3h;{VZebvkStgJOZN4k?&bPRnulBdB(*O<^q|D4r>10NKAfq#^S;&UhZ6)$w z3QbU|XAY*ZP~1o)?4B_LUB4)E%NC-S&V-Gabeq(ipERr)fNytZUQ8_f1Us`ggvXdBCF=4iy(w9k0K_B9N$!Fv6tOn5#3tLfH$?)OB zhr7GG#$YTd+0f93xoYFjUESSyS8bV}A1{U&j;u9bUS6ygYX`0yR%~2@%A=!E#PVVR zSYAIl8L_Nv3oa=EJfuQ0uwuky3vd^-#>i*+D(}Y|d>%K{HXS*6`53RsW3G4a-fh** ziTzps`kc=+s9cvlL21*Cpi+vNH_@-E>n$oF0r$yt7=(*t+jb4Z(C$Xj-X0~kmZe(0 z*jsC^`+1$s1S>q~bfqPa5@D&q)^){AT#KKOkPxk&^$LJO$2ITUb6~YK_FH1XhzD{~ zgBo7myb2i58M5J(l@zjtu6ZcSu!Q@4R|l44c6hEmf0hcJ0DuvTCglB5Vhu*!EZI(J z3j~N4;6^}BoJg-dSf*Zs!{@~zC|C}M=#)EmM6cRB$od$(aU=*>&XEHj0|P@sI8HaGeOz>sbI|fFVV>LG0)nNixr#_RVkffv>Ni0+c^<>oC7YOk@{ln>E zt;Rp^V!}!a81)2vZQAcmc862G>UQ(9CZ;HSok%*FNRH%}kQ|IEL&VIMU^s1jO)IWb?_ckn!4yoHpI_D(f>PlTt)fCgkjv}M3BBGXt>7+K2`?5QZo)W>@!B$*42Rg+cISVS|KzdkQY}|!pZ!vo~O;F znoZ%a4c6SfW$H&wPE^sO8Jh1uzEY5o1i0Pr1QnIUE!jrHuJ?Uh>lzwbSNUXWioLmG zOvKep7e~P_A>bFi)C?Rnuosu%K-9fL5HVCH!xAQsrm~(t9mv7Y@U{QwT4_A@1?Ijx z@9Xxu+u$lw;g06x%ATCloN+9*AW$ATTJa>=>O(PHsHS_G^IefzYbQwIbfhaT#)f-m zIr@$PG0%1_YTgQY)x#26vw>Ve%Ziwm*3b5rclqQ2U?C9G#Dul^0&@(jrOV~o>&g-< zFHNZ*`>I;+baAxZ5FPvZf+;_<@t_0W^A+dI*(+h8?b_UY^Ysb>f6jHKZyUJZfYHw7 z7`l4xzPReo#KzZ`OrG=KR++v&I2z@_NO;JAdtvkNUbA|5nX9WqOJ_8P-LB$^?v0k; z-EAQy&}%L-hLIn27dyWeev7jCT&WWa z7J>mq0@j5MPV@{IVtIL65FeIo_7!g?v{ zd?#s%TFd3`Po~%HijGDd!jGbUA*U;uIu9>v?IGu+7mDdTaqis_&w{QUEPAiZ-@m(` zj&vVWxNp~JwI9&RHDOgb+USK;WmDlNz<3{z+q}+uuxJz4PTwuHdE!Oka}Grj^ik2* z|J3Ug<8ncCg$|F7j@9)Yp>7~7oe7pL?CI;1Ffrl#ob;k}zy^zS*!%Kas8(&n(j9zV zcD?o4>1;I^5>~d#R1Tr12kYfL`cOZlk6f`C=qcc2<@HQ>ZyH>oM_8suZUL4J9d zXPwxvH;({agnY`VT}ae;xh zzz@E42*btibpPyHAIr;u9 z89&!@D*|K#LZJ^NZ}iK*co-o7C;>Jn2;h8FQw9EY`R`oJ8zJfD_^eQ(*Xr2`MkHHiT+ubFKHIi+Nz_)m#>N z_0ctf%&GIiHO&8LH6kYi6eFpA!^6_d%*>SZ^ohNwGZz;ZBpe*cKo)I|2(P7pmqf2k zLPPTo$Su3Nx_$r*kTf|uaIfpY2@M~g775VXf|{E<4Jc=~PEI1lNJ&L*06);3`g{E!#=FH@km2m;HOb2&w2vp8}cBZkwtoLXm4CyBROF4BP zI7tQ3SjZ?~q{|k9fQCuYNfc?|6#>vI;f{=HCnd8pE_^Ta5Cv7y7?2_6f=~QhU|)2> zwQRrDs~|7$mrDtFGgUS4gn!`Nt=v^THrSNEL7c58b zu&4u|J{e)p`E)sjO0hr!1x;vLW*y?Kx=Ezd{GO5`X<$I2SRnm&FxJ|vG|Ogo04e;@ zljzJ0*R=;PP}so|0R$Pq{+tg`WcBOk#1KQEzPU0rRq*3P@t8%YWRVPp@UC>K`mLB5p*KoS8YcZPR=HfXh*qQOGD*-0Nr&Vk&yTCWr0|FiCQ0m}Rxa6)aIH z5y+1K8sZX2B;U+UJ(mK|1wantwh@HAKVL;!s#<9RP&uuaquet#m0StDS?THcyJJ~n zG?lGFOZC?3RR%#zTja9oYJ1#~#~eZrGU!Ol-^25x3AlSxSWE$yz%|YHd{Y!~PYF3W z)BB4ZAYxJoc|E4xc$|NSz6Y2X0|SHpv9Z1lyX+YY0Chj5v00NaFrX+a1BHKui#AZ& z1GRN?qBoFZskgY$k;e^OM`u_6HCSAFx-{VK`-^onU?@C)!0_0tVyHzW01y4xC@Co^ zA|ldQvhshZkqLl$|0GR|E14A+2|46z9EDsp5V9|~zOPB^>v}gQOMP$5iUqf<44?_K_ou#9OVWg2y1_0~(xNRC?HN;2h?lrY4j2|od(B2W+m$?qp1;yN%{OYL;M zuaz-GJLl$-q6oMzcGGMp0hV~K6<~O(?KXq}#unzQzi2pC6_DiubO{iIcaM*bug|wF zHZD9~cXs45seuDVn>Ay6r4w<-llkwg7i%|~_A*Hs8DoHrib+h|P1RlCdVYTX!0*EY z#0)tLOB_IWWqO`AmD9920?N=vM-c3ArWz1GnW zkZ3B#{zB?c^F!$UWJn;?DbmfZIjFijv=l+}o}LGQE6gweuKa0mvC36ww(K^;x>Xb~ zntU_?=p(n=IjZPMu;ai7oD*IPfLTSLvH4KW3|VFt zmVu$6xX08mjq^W$I)G|4CI+=Bx*EXlZ@}4d?843Kab@SdQWp{u0+ObS_fl@0Isg_$ zi}emD;jUkJ92^~wtXlvRg!goHeuaG_NjNyvxB>tHvVwx(00#@~dTU!-{+A}9l9FcV zv+KLe%7@j8PQ7f;+tcIuDxl&8%o@02(|6!xz=%U;bwu>X=0^t|C+>G!uSB3zp;!0D zPZ^zm&;tWQ-Fo|3fbYR(zdK!d8)y!bwDO5WipSA~sI?TRL=}uU1gW*&Y6YkYs0vD% zV2nW2wDK5@6U`eY#>9eXzOf#)lTd1|>7ws{T z#8GJJ0FD$ICMG7pijXZUt3|q20HX)QCl&h&5bhM2ZUOF>yZQ}F0N4>TF~t^UdJNVL z11zO_vlCUGSfl|!qHpi*wTx&2Y%ahDGXhj+es=bHb6YRKLdM0%|9(9CH3ratZ}i~v z7MR;v+wJ~J3p;@NyY)*phP@FVFe?g6-j->pY_4)!L2+qMdtply5`OvjZ;_WjA^hVN+T6eivXx4Rhc`3_q_6mx7)?sNwa-`tOXDf0Jg-mxk9;*PFw_UX%#?d19Zi- zXV4PBZ2(OZ0TB_X_V#u}LY`zGY%oNLNlCxP$728{`U=n?fT=e2C-Szpw;Rjz{$VhM zjag99!_Bdf0Lw>*3xtKA?(F0>&~Xc0!6PjL12kmWDJb5aF0lZFqbvh#o_?Li!FUej zTjz<1X~Md_qy#7)L!JOOoeWsYY`G?j?~9km&G7(WLm8kQP-8M;08kHa8OmF)!1Lxf z^>l z^hObU268C{1%=gWMpJeZ8|7^I+YY;WWTTFiba%%Sbwcw8 zGZkQnfN3L(u*)m0Xl?_t3h@1f8j5#Vv_upXA%1>fU~eu1@PP*td9Xm^Z1c3`42FoD zJQz3{z5|E7kr5eyH()$>>tD;XTd?8=l=3Lp4SvI2dGG>J0=W8?9mlvuT>`!C%niWV zl>()i2ChbV;`Fo<0CZd{XCs#YOWRC6h^_1O9^mjB+{1Fmb4A|)8PuS1@3q2)ij&zB zKnbFvqIL+Q@NU;jAFO4_n&uxBnKcE2&NQ?$6s88pvTyYyUNfIehjf5_1&1@+?T&fj zW!mSp&=yo_c-qr_E}V1^SMk=hSxn|fi4C-*^|!29n6J0S1A30Mn)So1-j};05jad; z%GO|Jbs7zJKr6JUM8g>N3r5dL?+2{#{AK1(M-1wwlItq2=3wQj-E?lyh!v zK%%qLD_^>#;|wqniVrs@p9^I(+@8++7Fyj|fJ+BWvC zZiLrx>WD-dkH}!P+&p$7qM`j9@o?5M^c-JgmKm|ZmfIoo|Fm)K@ld9F+@(;_MJ_dN zDfe--IfG(zCrL@6WJImxwqv51LXkZ#rIaNJ$rd7VDVH%Waj5J%YF3)&h`}T*LsUc} z=lk08*E##|d_MEO@8^A<=llEKW}KYzq8Go>I$=J?wg@)q>fW~}&&`kNVeK4f1YX714%q3H?o!NT=P*^?j(w4yU#Mr-d0!DO^Vq$1ct~;GI)(NNz5KvRetmsC z=||Rs{63mkf5*BrUL8VyRgfYMzs%^)S)ucKq7kPQBz@?jlFQyTRgp}gNK*;RmYu!n z?|h)qARiT5I8$AzvX50>Hq3IV~G~t z3hF7DvuDrxu)6v0&D3r8`cn+=>Ojvr6?5-)_+uZVJkS-B*IY7fA$)0)EsKk;jEagH z>ubz`8a(#!Fx4zsdh09mF~>J!_EJ znLy@ObUA?@4;F79b-)0ST%)Q%CKvB}U+wMciv9cxIQ~bwl)Lsg-T2KH0thU1o)yWl z-_O^V{&k#1SODC>wAV%A=H}+7k(-Tovv?l;@o{l@TaaMs5(-A9Z7Zdu`Z1PeYh-lk z<4iNS<%LBNUXKr#=^k+7PIHGCRpxdsgiUbBAt#`n2rm#VU*D?pzy=i2*_mT%b{y0h zu(PpI-pG&DozJ0aXO1>oQS16kil=oFH&3J`n|J=3OfBdfnOn!`76=XLgA40khGaVQ zJLY#!lBwa*u~SO->2^EaYOEdl0+fBFS=ZHV;$L+ShTy3>=ca||15Sf#8A#T>4!Odo z4S(WT3+j2Hp!$H5$=a!i^UA+W=y1! z-UhSvT24)UeQFo#CLU*j#6UcXHm%xPZ;8-PWIF&9!(oe&k$f@N@!YxZk!=ovpc0&= zvXWwLV-xm+-;x|_u4@0BvUx|Ht8%F2paLWPNnik5!C+AHsjxDGKv+^>gS5!8jM zMJGG#qX6HX~5JYc;n^D@TC@&8e)H}4lNwhb5@jRAg7g_|+(SAfPfJBkX z?LZEyX=0L22h0<)CUGKYi+|IJxE@5;E4sT(L$OPk#7tHaZ?*2-gvr#ymZK z%I!J6t85WP)uVPyCCE8iS8s+6--eV&1uvzr$Z?{#fq$C=p0>m~*lae66QH5ipcQBM z3`N)gAe$nfQ?>wZ=kGtnb{m8KwC*+GYsk~Y@9C^$^+u2}#6j~Qi=TZMXdMAt?&|7- zpeSbu-C3=2ii!h5;VMg9k(3k*?t9et8)Vy5f;t&@5ac++r^Mj4+ z#Jr!;m5lNB&Ye3)6gseZe0_aIhzbGfH+tu9+2@`TN?Pmn2(1pLmN(q6ICkt9N*l?D z(o+Xoul-=0wF8BdiN-~9K+;kl>u=cICt%49ep}%>b%s~!1#v-`O~5POyn$CO9lf&0 zbED)(6&0wFh4OpL?@n`Dues_OrDkV8M;k(SNz_7jZp&~2*~l~rA#@_H%J`b#nGKQ$ z^Xu34_9rw}7;uR;+hEn@q@)u~7evS2R9z-&L4aeN zkig1=vvYGF!3zz8UhCg;&bf-N5H3UTayVqyc7zRmhv1uc=_Xnml(rX9TgE9k7J&AO zGZRM>FJA^)<$-ROu0PojF}+k+*b(}KVUzRqeO*}_gCs3MV-G4|3!HArrjYs3d2h>F z?>ziEv^wm?hUEP;ikoG8)?-5%Jq$Z#!Y#jWPzPRS)(tjgwoA0?>{f87R=W zxVmaIc)YsFVwnJQ2n7qSDL zGcN^o3(96pW2dat_rfXEy&+n%WH>H{63l1rKd@MC$b3l+7@xb66fzqUFj;-{; z*!C*?rDx=2djhM~{={m{oaEG*UDN=z2hX)XJuzBM1;2)Kjp9{ucI{Q2L~DC{2~a{t z=*#w&7V*^IV)qpkcV=Z?);f2%xC7vV`JB8^duLgxdvQK&&oO>oI_#%klWq$g&=deB z=i<{+PsyXYh!7haEffu?n<$jZ-jHp&ByD&T!1l?RnZ{9bolzaSjD$FfFPMSVG`^#i zRZOM3%H;Gkp#YajN_sOavCf!|bkl6~D{I@F2UoZ>`%`4suDy_+o<5&S(}^kW@W&VI zin+3KavBM$jO}^-0zpiAI?pY-xnL^t9mev+&xJEfH`~MX#{mq;mvHLcqE71gruY9g o4M|^Ro|(WJX`68x`*|UL4I#4IZl7(bwa-ug0g>&6_5c6? literal 0 HcmV?d00001 diff --git a/img/python-wallet-6.png b/img/python-wallet-6.png new file mode 100644 index 0000000000000000000000000000000000000000..3a25a1579e56ce70fdd0cf1829d582dd97ff6f3d GIT binary patch literal 11391 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 literal 0 HcmV?d00001