From 0a40ed4957270e2d183328657bf44fdc9f145c78 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 7 Jun 2021 21:27:28 +0300 Subject: [PATCH] Updated on 2026-08-14 --- app/build.gradle | 12 +- app/libs/walletconnect.aar | Bin 0 -> 140927 bytes app/src/main/AndroidManifest.xml | 8 + app/src/main/assets/erc20_tokens.json | 6 + .../main/java/com/tangem/tap/MainActivity.kt | 42 ++- .../java/com/tangem/tap/TapApplication.kt | 3 + .../tangem/tap/common/CurrencyConverter.kt | 2 +- .../com/tangem/tap/common/DialogManager.kt | 55 +++ .../com/tangem/tap/common/IntentHandler.kt | 41 +++ .../com/tangem/tap/common/SnackbarHandler.kt | 8 + .../tap/common/extensions/Navigation.kt | 4 + .../tangem/tap/common/extensions/Specific.kt | 9 +- .../com/tangem/tap/common/extensions/Store.kt | 15 + .../com/tangem/tap/common/redux/AppReducer.kt | 2 + .../com/tangem/tap/common/redux/AppState.kt | 20 +- .../tap/common/redux/global/GlobalAction.kt | 3 + .../tap/common/redux/global/GlobalReducer.kt | 6 + .../tap/common/redux/global/GlobalState.kt | 1 + .../redux/navigation/NavigationState.kt | 3 +- .../com/tangem/tap/domain/TangemSdkManager.kt | 8 +- .../com/tangem/tap/domain/TapWorkarounds.kt | 5 +- .../com/tangem/tap/domain/extensions/Card.kt | 6 +- .../domain/extensions/WalletManagerFactory.kt | 4 +- .../tangem/tap/domain/tasks/ScanNoteTask.kt | 6 +- .../tangem/tap/domain/twins/TwinsHelper.kt | 10 +- .../walletconnect/WalletConnectManager.kt | 324 ++++++++++++++++++ .../walletconnect/WalletConnectRepository.kt | 94 +++++ .../walletconnect/WalletConnectSdkHelper.kt | 240 +++++++++++++ .../walletconnect/WalletConnectAction.kt | 79 +++++ .../walletconnect/WalletConnectMiddleware.kt | 156 +++++++++ .../walletconnect/WalletConnectReducer.kt | 36 ++ .../redux/walletconnect/WalletConnectState.kt | 70 ++++ .../features/details/ui/DetailsFragment.kt | 5 + .../ui/walletconnect/QrScanFragment.kt | 90 +++++ .../ui/walletconnect/SessionsAdapter.kt | 55 +++ .../WalletConnectSessionsFragment.kt | 87 +++++ .../dialogs/ApproveWcSessionDialog.kt | 35 ++ .../dialogs/ClipboardOrScanQrDialog.kt | 30 ++ .../dialogs/PersonalSignDialog.kt | 45 +++ .../dialogs/TransactionDialog.kt | 67 ++++ .../wallet/redux/reducers/WalletReducer.kt | 3 +- .../wallet/ui/WalletDetailsFragment.kt | 9 +- app/src/main/res/layout/fragment_details.xml | 32 +- .../fragment_wallet_connect_sessions.xml | 92 +++++ .../layout/item_wallet_connect_session.xml | 57 +++ .../main/res/values/strings_untranslated.xml | 40 +++ 46 files changed, 1856 insertions(+), 69 deletions(-) create mode 100644 app/libs/walletconnect.aar create mode 100644 app/src/main/java/com/tangem/tap/common/DialogManager.kt create mode 100644 app/src/main/java/com/tangem/tap/common/IntentHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/common/SnackbarHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Store.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/QrScanFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/SessionsAdapter.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectSessionsFragment.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ApproveWcSessionDialog.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PersonalSignDialog.kt create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/TransactionDialog.kt create mode 100644 app/src/main/res/layout/fragment_wallet_connect_sessions.xml create mode 100644 app/src/main/res/layout/item_wallet_connect_session.xml diff --git a/app/build.gradle b/app/build.gradle index 0c5338992b..54f56fcd1c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,6 +65,8 @@ repositories { } dependencies { + implementation fileTree(include: ['*.aar'], dir: 'libs') + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin" implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.appcompat:appcompat:1.2.0' @@ -75,9 +77,10 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:develop-11' - implementation 'com.tangem:core:develop-15' - implementation 'com.tangem:sdk:develop-15' +// implementation 'com.tangem:blockchain:develop-11' //TODO: set correct one + implementation 'blockchain-sdk-kotlin:blockchain:tangem-20210607.120025-19' + implementation 'com.tangem:core:develop-36' + implementation 'com.tangem:sdk:develop-36' // WebView implementation "androidx.browser:browser:1.3.0" @@ -106,6 +109,9 @@ dependencies { implementation 'com.google.firebase:firebase-config-ktx' implementation 'com.google.firebase:firebase-analytics-ktx' + // WalletConnect + implementation 'com.github.salomonbrys.kotson:kotson:2.5.0' + testImplementation 'junit:junit:4.13.2' testImplementation "com.google.truth:truth:1.0.1" androidTestImplementation 'androidx.test.ext:junit:1.1.2' diff --git a/app/libs/walletconnect.aar b/app/libs/walletconnect.aar new file mode 100644 index 0000000000000000000000000000000000000000..129de60a795fa574c713374fa7e1d64fd4acfa40 GIT binary patch literal 140927 zcmV)LK)JtAO9KQ7000OG0000%01y_7*p(^(0B0Ei00jU508%b=cy#T3TXWnvvgZ5# z3jct4Q0ZptQI^_mNBE*+soOKLvB6*!QB^!#0;ICCmqeH-o9`nyDt+IVW2! zou=VcxtZ#&eOnIq^$%XcK%{<{*x_xt`Of?O*j5DS0ebUk&kl?LJl6jrllrEf9;fGq z1O(Ea%5@wONq%C#g5`(W^{uXUY@!iIKBg_1`TfxK_uWq#@UeWhCc%>OiM2+Bg3a* zATcE9Vd#XaxNFLB^a1{?*-s+zM?F2??K>_Y1E73mG;Eme>$2%~va-MPuIaFIw+eS$$;fZrXbixy)=Q zZ|ZUC?z%(Yu!+G;ruvSw8$CzC#G_{vltSnErCC|lM%awXtfn%3d^h=j)j zpIp9F&+yI9@z8DdZ1a8H{qhthl2{d(EgM*;pqakM+}`oEZPPVP*^hkr;u@!&?%B8* z>K>GyALq#)FT&!{?PkN=ri+ASd3e`!T!vg#BtfE+K6eA}S5Gojcj%g1(7@&7P978T zzTWK{{(oOh6of40H?G5cO?w#XLpeOtDfi{@-IMzGvz|8lr~UD;_Lq4$)RV7=V`C%I zKeSxK{pKnmC-GZ7?Qg}RHqy9{Y>T>UN&V{U%_W(uvmurYwfM|uDyR@&Yxtm0M#QG$ zYOHR{sT-s``zFG1^#0H5GjY+D|i=|7Hacz%!OXeF27ZfM!?wXEvn=u78|%9afS zs~*a#LULSD%WzeXeN#Sv={>_x`F1Vp;rni=|HXU9l^c~zR6?Zv$<2a#gNVw&!pkP4 zu;Es$;kbV*W#E6TM@km~q*9Q%n?MP@XIOk#RZpPTs4>^+9(!LWd8)t-+aDhJV!!&b zt!|a=X4{c|i*s8KBd^=Yn!06A-BZ{58nNwMA%Z9G%0t~eh1HrR61vx4vR_wEQ-NnXQF zA=Yfq6A60NRXZkEKmG&NTwrRQ>`hgTcGCD0ZyUM3((=bo?8 z2Ud>9fr0rV8VX}wH_mEx;4`|GGC&PoJDVmkyL&-mwYQ{UCDR@cY#?{;9bfghIe+V! zb-nCJIg3AcULSVxTAv;@(mmP#6V_O&H~#52SO;#TcI2Wl zh;5d?G&_yZRW`^obw}Z`gL+{(m1)#-LQRc~efL**6dp@3(?U*s3iWpOf;jkn_w#m? zTO*vJ^t6Wq#;4~gmhq8yV8gC6QniGUD=PGgqb83p$Dzsf=FdZq+lKy zpQ;sda1dau3vA$AJ@6E}J6jtn-Tq(M$XC!uuQ1Sf&x~`xP|-kN3Ar&0F}lYDQkMP4 z;I{1~tJe1HG_6Y@Lhr=F~g6kl!_@5kY8hetf; zUEPuiZ=F+NCmC55Mw-XEFjAk)!(=DT9e|it38|EjK7qsObvZ2lCvsg@NFrsb`*+>I zq^k7QL1BjSK%RCg3|^%sydH>09zoHMJ)=KuxWT}F*-wA)75ziIZTK?cJ5&21RzPG8 z{1n#Ktv-SdCVmn2>objkss=#s>R+sq2L7k=Kiz)Cn7R#M#G(++Mnb4u^uj{sF$1U^ zT%&r;M_t58$n(pB%UHn!j^AjzD{(JQaTB!GOW@XPr<3?!wM~B7mf-o zKehv^9KWNJ3{G{T11C8F+4n-2A|yg?M#yAg02lTeq%h*xFx)o|#Md8&=GdxJ#$cX7 ze58QHe8brdLVFmqUCpXU_6fAK;_8+!mm|w84YAS+%PSJ*IXVzuHdTGd?))ex zp`eWXgV{1Ts#c3pC{HiWmtkTZQ|mV`knWP|%ajKDvYr#y&5;$mfjt**&d%bXnpIJ3UKj^QPzQ;(Z0^czna$ z@nq{k*_l-$KF`^>s^}qkN{jbb2^`*hy<-0h&up<8~@S=7c69YEB!qH{wwKDx3h1Abu*>g7eez)%z*R$E;pb;WTg) zrq)lBr>@UnDqEC;)8%a8h(?`4nTI081?P7m!`2KZP@wlcgvtuBU9c)yPRRWn$rh@Q zMVX~HPSDbpRf;C1l(Q+vO(~}kYmcMo<`Iq;*f^1Lr#xr~t0 zp^#C{sDTMHUyY_Jd=RmZ3HU9zITw*O9hniV<_DRnTy*5I!<@hMN3sr$k{) zB>MyeQOsEztiAyZS3Z3Z&6>v1Qk$uO(J)?_G2#so!jOo=sij|sjzScgT)C(z zWg(@TPPMo(BZg8415p`pg-N8GWb;Zv_KwR)(?TvQGGw!lO8Wnce|a!MSVc?o6rzZy z5t>bALG`{4Vlmc;P`1iX3Wq~N35w7ydTsH;sk(qi5~?9Qil%*;QZJJ_H!6h)j$U`J zMNJ%KTldbX?*fosHhDwLUnJx|2i4zVD1Q}!`Ka{;T!_S8CScw)6R{2~v2 znFimpvVy42G;{-z7irJ~pWDZv$}q8f*6l^0LPR*f?x_Ze_1B|K%M-)M?9?%0YPJO2m%Y#1Dn@TKU+$r?^Lz_o*L(xBsfUf2k5+(> z2dsTCYHvJRaXTKoA#q+QrNBPXw6KS=7M|zhp*2n7AcgDfvS`Ii%n41EM^;JQEukY^ zevw!gsko&_d@*eN_5(!}inJVMw*@&|o2N(u*r&V-2@o|@)_Jl-Yj>cv*Ca58e)YFp z1HFT#6Zw4F)T0h-EqIlNvgo#3uJ_Xz)1J@Beb=}R*KpUzM!=p`p5knIPiztL-+|(5?@1XW) z#!*rNdUX{*t0`)Ai3P??WVei9b#$RT0>L zU)l%n;p&^5x~SJcVCR!qBdJRh&^i4G#*Z5M)pcO#4ctW{?($6xw=bJ*a^VIzUIPOh ztcD2r&FC;^Vd4vJ=h(TXtTJL*OSerqRNvXN_Md{V9Lyj~ ziy<%SGncY3UcP`QtTz3IUPN>kMkJFxCShn& zFT!Dcsz5u~Sk7R_ZH~hj&v%L1=5x5M8YYg9WC^r}2+{(&!oxe%mG~#SK$DnPq%=nJ zOEh?q0;PnN`i7>Y0h1hSE0)C2mc9d6w`iN<1W9%tdSFaZDj{j+)@2u3`Spo*BWo^E}VW$sWM^W8ZKb%zewO%`!N(>e~hAbzts7JPyK5^iS&^EO^>lT`6xF)7jK) z^kpP4uv(cfr1J!Lsp5y-7ohHXpH#vG**ZfX>#3ed?V|8)cwaot8T42Nzb7{e=gbOiFhEgzo zH6(=yN!lDq0taSrtE@M1kMH#3$-)(EJ#ZEMa^i%a!r_wnl<#a9Yn>-u)693hz^x*I z0L8-vWmnNp4SpGbTyN+OSc}KmalEjklc|Hys zq-=FmlJIAtlFWD@5cD5zC-KpqHb*U(JV=OWiGae;iWKk06>p>;~?|XyyUl7U3TmjSoK>SH64oULVM1u_)< zw3LhtRT^q7EHo^+XnXlLaMEjR1_?$oHV6@!2A@sA z50KpGJ`H4uaw=U?!9BRVYOLj&d_c9QD zm4^4&{6TyVA$dH%n#VE(V=c}6*NbLr<1Zn1s z(*RprXC;AMR#AB_>GZ>4tCj?|&%hHouyjtu|GBZCf)Mx{+~RN!W_PcWF}5!Vj1V-X2>y zixcr&AD5}vabItf`$OzIlflo21ZY379$pTPBh|7DG0t?dP(_AHlyOlRc-u!8$IHh# z1hG}lAPB#K7Y{*4Qor}=rhOHN1n;irGh(`1nOTMYtV?j|>HDHJ-7V_5_N;(_*n4FS z2<`OrqGyoc5=7$y&f>Q3o%?A6nBt@5B2!4d36*6oIOm%608rhxYq-B?@RMU7N+jHh zU!*jN2722i;i-+I(NqgbMxdGcrDk{^QTSGtD=EXxM#M1OzOnN(Z1I-c5UXs)c^<>| zZl+M~N_PgvIrnm@&ufh$0m`W|7`2V(r-uYx=fi9BH&fzJao;?(TaZ!=vz9T($UNu?1|Kd^SEfgdYAL%N8rEuPtV%$NxNE<&7ioDSSSZiyv^)_koF}zp`0;Pj>VRN@I=ntX(q1Q31EI3Aj8{2F%3kA+zCBJI z{DeaXZFr<|e^cMTO~nTW71t}ID`|A`*fq7+_6=~J%5{E0oJ6%f%%XajVv(DRAfdQ} z_#P{2E}8np?;Z~^vkDU5gq26W>79`%r-+!*e-fH13NyO!eL*$OEIeNW%wmOhdL;X$ zl|G?NatnnB@Q?UTqAClbpxzo$P#N5>%Qq=cq0_rJ#JpcVSN_!PcK#hsoCP&FBv#hk zW$RsH5;3H)5%|OME$Ae?Tz>Ktx5LR$-!3yliF>HYokDO!K9iI@lGT~y;!g=?aPsu~ zN8^jm$pqsY&&iyu0)&4=PlG&Ga<}Dm@`>G>+fxtA*L!pE#e4j|M~M1We|I+zUEt67 zrt#9(iO2cly_xV#?er+W1Du0}Gsn%qrZi2Eqq`Nh_@jmOzity3;Z$$pi>!6+;Zv*U z^7ToF{m_&XylFu0nJ~b+1Dat;p2G;*=Lw8af|UXfK(sCvD3;$I)cE{_D%-by1ZcY{`P`2XRA1m^PnV@;G%8hNx3~S1YXgVpFDH48^5@VB- zg-@aV6`@prMHE$D0M%)+Q#oN%FA97*J?1GZ-07r9r_3Oy6XTn*LYq#CYRU{~dQmLX zi^G`yvIwS^1uy+IaZ4``Svnzym)GU=4q50Y)*^4Q&J3D*QM}ZPLZx01CH2Apsh7n@ zy(}#1q==}$AQ;NYLh(NvZ+&DF__!KZ(j>D!a?>he>-lKbRRtNW=(Noa3438Q*h>Pz zk~}$?;a}PLpav}!uL%lK_PvJ0x^w=&Gs8BwCju$t>f-z1siR>;KO~p4wXmmdZ zI7EaJN&DO-cR^Ci^BSDd&aCDoh+RUUTSmajDdBC|k!`1hrujMPqfR4}V_-7FUveW~ zvV&f-<6UxGRi{O{oaEF>k8L?2tmTx5mb74&uA+({zq1!bmmdtR){iD4?Kw3ABB z7n#gu!NW~m989Wkgk(1`bHPx=6P68S*PJlJB{Lk-6Cj1cbTUJmPK|0Jcw`6(g;&uR z^J!d$iXsE3GGnJw!=|z$rm}*ilH;WEL!>gIqcQ@cPK$|34u?7+5-KwYDn0(`l+dTd zsHfb3r^Fx=GR!GeR8OCn(SWK`VyP0ts7{WcQbdnKP`3LH-8q`ZqwR@Db%Whq#|`+p z1#$jp-Q@Vc|BobyZv-s8|6h>)w|}Gmy!yXO!d#RJZk1m(85S`&M5V0;~^85^)WDZCI(waEf?xMa#j{M z*MHMLR~!BF;>`G2&48P$<>w_H+HjHcaKjDVQ_0s|aXG<>nN-|f0aM{by11kDU0JdF z;~?z`Bp<0c{i-J`BP2bE8T}}S8XoHv5{Xx8KJ{3y{hEi$LF}0piMY|Dd{%iTa43^y zct|ajM6WBK7NV$6^EXF)jkvrlm!Iq9=S_OkmI7YTKu;#yp*wu2(uLTQh4?U(9Pnfb z_GB}4AJjI-?sB~s7N-Tam9e^s>UzYU%NvIc?l}zSFr}|hA7StW2-FFFoUm5{?bZI4 zxwV(>YDc$LJ7MO&g^#4tt<`Y&80;sAf+3sgBA6!Og|2 z|F5_6&(~)Q;Cunh%f{J-XRW{`qN(*(eM0U8Ds0-xZVd3mZk+7V0P(BehD>0}ojOwX z3CyRi#}-9^H(8Nj(#)X6z?$rQ2>%M-j+;MKwcwCT;&JN0e)v@W61Fy&62SqtS#>+J zf}?G*9^e~~f;5Il+G0IUtpA-i3%>XUTe;v!TTFQH^xU)XAPdU29>xh?0|l4qQ^zIa zJ@b2uFtuyB{Q{TlV|Nh(H>FvOoS>>CZ;2G4^=SR& zfWgs1_&g}e;`c~E%b*L%p_LWlNCK^}5MlAi(2B|-iC(2Om5r7MGm?W{++LD!Qz(~i z(s=9RG{s38aB$w)A>barc!(&XwLH#oFNd3#rVu9y0gyy&h}9uUvM^I;rx*69lf15f zUT8@|A!x^GVI>I%u-?^eHPf);ouWPP<#uTscEgjr<%nK+sncYIQy<0WwD`)q6q_b4 zz=h9^EWBiq5lY688|=#`nS?u^!hd6HdHTN8GD%=;7pv*{Pi4Y%QoIDIas0HBcoCDE z#ZM^fDK14&+?ebv+&keL(;rK{h)K>uR3*j4Wg$pyc@dKnwMY0VS)7iCNDvwWhROM5JY{18OZ&L{w(dX9mZ0KP(k`84(wQe4K`| zvA7X&nYj-gIFg)#BO)`C6+9v^YcuPJtQc4sv~wAe5B*V3m3>4g1e#U0JX}WPVFL^3 z?Pdd?>Aj3dfxs@BaF*7~h#*LwX#8jS5%jCb=nDoMfDfHK{sB+ntEVIC)bGG z+*M@2O_r`+WY7iWw_)I>$5mv^1@vLyYGHVObrl#~bWYG1uIz@1%?f}?BX}?EA4w#CPHwNfBA_>sTTz6ha!~>Dlgy zgAHFM)|-eR*gPpbZzAJA&H==#ZjlimD264p{w5+54p{}6yt;{qgrD}vxJ^-kjdK$b z4}vd5>n0*9Hg|5Sy4PZL77-qsZeAcX-%hYPi^vh3$CJt+B1KLH(C%f-R%a1Wn)DKH ztaJ0wtC{}>w1iOHA@Ghae#El1c{Ovof#qhqoq?;Cp84NT5X<=#7s$&}&^ z<-zjsokipczIgkg-NN_bhr6caI$#y^j;ekB9J9WPIY%9Wx}SmbB64E#z&W%KBO)S+ z;sYyk&Hq&XCv;-WNs4)OD|itBF?eDz)x||51JP7z5fPCz{ieMxD_X+Ir@_7EoUw)LQ{WKp%=1Ai%V4B;3cu0WZ1x?FgG{6S zt_pP(WsJRMdrsuVx{5MJth#E))5kFZiwe)Mx@U*!Tky zCHw~K=_6}*I>tWEc-)uUNm1OP#T35$E37+^7%TONJ8l>^hbO^lu{%r{zAO8;Z2HL< zsNoTf4km!bE}-rrhN08G-h6Li|A51s-!0KAnQtE~Oc4U}Fm%$pah&+X@XbVmaL290 z5e*Une#gYlgKzrS4o|xf3GO5F#ikRS2d?JI9UeIXF0Vnx5yJ}{e$8s|>SB)bySkXy z%Bv+1@akfY6ugp=g1LhNzm_;zD4ZS-gjL3HI^j=&3!a zVn)j)zVv2}jJjGa!nRrf<>i8{7QiBctJUA;(pC#?g@n$k;$m=#V^CRbSM)-RzazN| zq5pvd&5ve!r{+o-3I971FcSI?Bxio;PCZq}6RynpNh8F!%DTJq79ji`iCh5u4-}>2 z2Tye)FCal%Py!FZmn&>0T4?*-iSlZZKOFUph>m$5td@oEwp(rqc}!Ti?ow}r~R9XFO;{ea=( z+~Y$#GhF3>8T6}r^M`V%U5@Pk#XMrm3_QE`jmp`1WPsSb7@Qj~`xwK<7ORS#8-tm- zX`Vk}BLn92Jtkq)y!(-X0HZFuDo3^bw5C;WV_q`#%NxGdTa?;9BKOHE!plnxMloe? z#W8?SU=hs;;`a%!#rdJu?|Z`DL_73G?jseS$UK)Wz1@MGm&Hn4QNlimb%heJIHlsnT6RK>RCv6+Os_dTH8`E6i#oyg6d)CK|;A6QSS zx6k)7V`N+y=*E9hn1T2wRLhDkh+st%kS$@LWTX_pL|c$CfuIr=7!0n;4%W*m$c(VS zJJ^GB$@$v-)OUgfhUbU4)Q~=G* zm!W2!so>yTjf(o-#SeV@jNRxOX52J(iTlRA;^M#Kej8Ls+Ko0ejkv>xCd6;832z}%`^cfxuK2TK@;!(z=bn*Q6kz;!z+bxz65{$39 z@?*svBS;!dsJLJX%yl&I77E_Qdm8w(#lvjj#2@qbIFM4qSjvs*fjYJ>T7pc5XgLsc z{%8rpXaJyf$P4oy7uZU!BnT^rZ27+1H< z;(m7XX8BnzKiA98tL5i%WFU1aA^6S*r_7U5byDwkC&3u^B{zswfMO2i{ui1FVLbNc z1@Y?q%bgztXATKPlO#s)T4*uS$GZKFW=w!2)+~3d-&$S%8{8m4Od567w0n4ky3N5I>h`_=U_&F<>Q!L>VVFj3 zx`yv)M|Ow(Q)p^96z|St0ogYxqbcCWnfEB83E-i~*JcucU%lZBKBT;t3CTHybnZ{a z@~(*D>Rl}2Myll-q{fTy8|7C0J#p8u1}<0mdWIJ5T`)`TMyO#kjB@1j)R_5-UgYR> z5$-(|)G(QNwLDPcq^|R&MnfcjDu-R|@pXDY>Pj+noq`7n(vGf9@ZhAJq016HNVx9{ zU5((ujrc+Y3*~e(x&Z-!WgmNPI;MO({C_cpm*|bLZwZrv=7T7I)Nh`BrkO4~+Z`2o z;S{m8gv4bR>s*d^R$WHg=u(Xs2hua`k1E@UuWgrx8PkaMqWh=^{`Vkwq1pxCQb# z1>ftN$l6-V?uHTA)V^^kT3BbBk!|hiMwM8>%`joF3t=R0EO%HI6!S_h5uuY8TZL`i z?lbwKJN`TSf{1ndp?Lz`#>fZu^RwDZVxVt7U*9L6s^cCJ$P>yR>a3M5)}4KSV)zA6 zO9KQ7000OG0000%0Oq%gH3Ikn0H#6$01E&B0Ap-nb8}^LE^1+NwAy7>#=%#+a;O{%H z_kQkYmV)%_H&8D>Z=p!ul0YlF;FP<*f`XEOhJs>x`LD5^4U@B@i<7gPp|v&0ndy&T z#&)*0AY*4HAr}j46Ja}BQwuXjV{1bvr>OWZauDD<@N-@fL($O#3MY2it1$exWzrw9 z@tCL?mo% zo8S+m?8)Gbqc1@Gp8F$bLeBgaaws~RO`@epe!R2yazEjDqw)DPcp~rbCEc^X&M$1i zb~EYPB$4f`*{8IWyM1=Efl=HaF$Q4{V$|gyRDF_^L2G`h8pi8kwQxS=meJU}Fv{<| zEy;{UE}|06VTxU*J266i4aC6fhn}(apqF_)%(4f(f_Ut1zQIWq2i`-B$F{?6-<%G! zndl|*tBrY2TqEvt%cU7+gk|B5)iZcg7@%C9i)(~7U<}e|wFrI81+1r&+78yp2*!vStfZe>k#Z^86Ggo0gg-xh7TDc@qUmImJddhm5_ttUM-l5;!8Mnna zo$@=K+;uxmJf8KOcETA5d@ykrY7tAQ)^%L1(5f?Um8_X%m}PV3yt6hlGqhf;1p&ds zST;J+*z8W!9nN;lR;-29-S#dTv`aF&*GH$ghexl9QqK6`>@aqJzB77+2op*}gYwH{epp~48GndL~Mnuk@dJGaD}wr4Sn)`+wu&LdNF8S zjhTeU(OGdqRphJ5qHt#&*p)c}KQQQ4IXn6^mDWQkv5?J8OR>!egqfCL+*l;#lH$lg z(gS^*#{(o0rjP>Ew$h~3{;r9sxM7o&1KxPuCPsgGd=&c?6eWr8Rmw0%q`e~~-#XpV zL+-YekyL#<0e1-j1O0n80eJ^u%o(e)Iz72q(qd^Ph6I<3;zt_7P|-;`1y(AvxXq!T zwnFaX>cFcPy$uH_#nV0 zMZ8m^I9A8-Io^Jc5_9m2m|N?|JJRz|`nGrBzsNiaG7X3YS`dih>zVDUzoE-gqj1AR zTE_x8qU2c{tMP0nn+OgCrC=e0K~(zXb^b?szavJq}Bjc)PAF<@mg<>L>JAs0##LjWd{LO#z~O8O`%l~H4M$Et z86u*|e_(YE=bAUi`Q{b!w*&6t?;{MoVVN$S&osftG0>xK@xts5I&|0A1}fKmGQAdfQcK>pt)ePUktJGq12Gn@{;VlQ`geCvn0v zPEjQVhmxm$InNN>pvU=-SmvTF?4gR8!dT3hSfFn&;v>Va_yEf(ujHUC(U8)7GXX6M zKXvY4oU-?wZB~L-YR>xy;{a#B5q-!tgakMXOIG#GixL#b$C- z130flOvuPbl-s)a{TTA3GALBT$Mlscf>Gzoy#Zk}Y~QMAu-MckDm$Ok8$F(ueJzL& zW3djNR3ySB%(mjf8D_daKPiw)_=I{cono!KNt$tp(fWi*J*Ed^#fLn4*q?{7#z`az z=u1DgGafu?llVsb@)dLI15^C!vc?w;Bu&LxP>0;|Gpfz(^N+km~c# zi({_Ljk}fD&62FwxKzI-{9OND$gF`U#YFCaz@h&$7?dMQr%U%)P&9HQUW`PAOu`1{ zNo)%iuZE;-1yGea&|YlL-h>=CjKpy@VW{k<%*GLDQ}&tQ$9;xbEh|>0s1Y_pnF2j# zD9mmh6L#j%lLbL|FkVTV)E8vtHUUp(YM-{H;VC!(1(rHiBtMngmTAyI& zCpMFwaJ_wrYSy%aR6@zH5baA%h-oQ`-x!miBpnGCv0oc;S1qBRna5 z=!A>cAp0O~a<1QjzdqR20UbObMSJG2z!&4EC=h>WAyC37HDK9Ns%9z7*bU?<;uzH4 zj<~5jVXo#dnVZ+Gl9g{R(wYM|hWjD*u&k0ioTB8C1`334tlZA_9ZllO7=yv7giuar zIGwDYEa7kn$GBThz6AlO^L-`+`+GkHN~7qxy>ou&Vy{i#_2BpCQED9|2#xwO2~)p@ zf@1t1k5bake+*MLiqdxIikMF)XLztuug+g>RpbXp8B2ZzM4=7Q78<~_nfK+lA#rkn zQb|}-+HHQ(RO^(lwE>Z({SwcSY>r32jBz`4UuF$Go}3B@_`NF4?^EDd3uEj`V-j#! z!B!G*E=lKD8*3I_p%g7BhTeYzTSt-9Ba*qVPZ zfUPfsmXfk}BD0ylTOR7Jxr6qPkqaIQ$3uksRQmzRTI}1-ZE8984caxT#QbV`%%zjE zD~|Mc0VFEbSAg(Uw&2qgF`;Z?z4Vzv$DJ)`Ns)I^!h-1}eoB6(&KBt+zhYg&NM;E- z;$aUNNbX{#eo+UZE&#HLjx6weqx%m zLyjQ7E)VZgN`<)l5}l@gS+QZVm9pRkj+!x=tY9>M>o$-e&>s)1BY%awa8BW760^cO z0!DU|ux+#8-deL}dAE*DyclGz>$aP=6@~TYZelEMUgU~?Bj0-5LFY@6XjQEkLQr5q z?z>^Jh(zo-DTiMwnYjrS5=LRMUO|=dsM#WFYvPm{i{d9kOl7) zkh=fPGpu3HTgiwDD27c728 zUOfuDYYkrU3Gxx~UOh*un>bnwr-qo2$)Yw&+jly~ZJ{P2Ivxy^F>sd8URJmZfe4p<#uhZq4!q7zE9jeJc-#eu7 z{hKsBp4ud^aKeE>?(O2KwoB>F{*rS+pfek%m*#bliMvB~G}WdHPb!k}vny{Efbwi2 zH-YvTS2@jvQ#wYH6H!@KY1L$(C!SZAMI@H(kq>DNH{Gs4Yhv13Z=ys(Uu=ipZ-|UE z{+SM-UlbP5cxo{ornWZ9a!SWw5+_kxf`D|ashF5u2AgPYrpi8iiG`+y!j=`nr_R9G z!>(hQ_jWn2MAbqh?er}}DI`B}#2uW>7GE0**@WzqNhNfP&eIYJ%N7 z7ID3u(yF5hNr3njJ;Dhbiww#|QogJPs|`K`Eu0 zE)G}JTLqMJ*zs-b4cgRq#>ekPJ@6%*!=wy4I0x?q@9{>*cE@&pd(%nXxY;`)7|ev( zi4Z1Njun{~kDP#=M0>x=U`es|spkUXoOc>c>%g7D-(jnX)mpQb8Jj)kWNBPS0}|S@ zgN31Oo@lxb=i|>Y1#q_*(d8p0AafdCS8%I)b<@(NA)+*DD^VhO%9zyAdXIFQYM~kL z3+yB#F%8PEo@toh_irkbO6y#Hf~8)M8|qIxcWBJZBH^Ga1YRP=orkS)@w{u94v+(v zF5#pb9%ka|IlHk9uG7l~=&ZWfb_SU7)obo7pa~%&N}`svfx}}(02*O3!Q>E#XVpUW zg0W1i41nyeaCuRatYlc@DdF&6-&RLkO{vT8zsrGdA}x^a$`Y4fqNMdpqd$ zLzjApWlT0ISG$Bb>(XdF75VWQDVBW3!V<&;@X&JHXp{cma8 z4cOukUSQrhh$s1EvVUW^KcVD@Kj#?jdFl8;^S++;^sa;GRos>S;4Q5Srir(_m0ax) zy<`u43zCR80Y0h*<{Q|e2WgfcC$5{zVLiG@`-c6C>-(MNwl1_GMcCnDyjPX9QPh{Z zc`&LPu}Zj;Q*mPG*oIu*0G~G&TW%8RBt%+T=sD9r!`>hoi`Z;^q#|_LVw5~D#%P}w z<^!LDQLWrx38%it8+a=gXZ}6ZpuU$-2F2;1PsTGxYf^r#LLyUxGvqMb!7loBB=cI0a5w+2IsiGEu$KXzx`PcW z1Kt9=t$9DYt>x-H{GSC~+=Xe)z6cupPY7cDiy*I8$)q@ny;e!8dGD~eab zh7Gef1w}c%@j0S=1_D27^i;Z$ON*R)Xz-V@VFd>vY_8Qj4wXXiCc0=WImu4Oia&3_?KuD#ph zcs}5{);Gnu1-L;mzzr7ma@;#ieyf!07h>2damjDaF~Ww=1c!-B_X7cSo*OOh7jf@b z1X)aMXxqs5TF6WlQn^to?4-TL)htMfCbAAINHj|f2c{ZmEg%){br;(($A;!#%Lt%C zN^>e=&ZH#dI>x9%ln5z{Q3eKFlg)P_1tHVoiIhIsl`u1k27IoUp-m;@E`+84ttdINM;@@=jN8WSF`e&EV9(XK=fLlxr(M?aX+t__mT44NXh zs+ALKo{T4kV}aWP({Ni?THcfn$73GuQAWmTAPk;YVl+#${~`j4+qwh6($DDXZve{S z{_n73iYO#~TS(BwJWb%zhk~**&4=c>k2#4zzH;633lvHtKvA+q%Tx!rj8X zw^Fn5!S7Y<5Cs@cb?w4tk>i>I725FeLxp9L$lSer_`pX55dWlgSowGVJxMGH3QIle zA2S+g)jjLz5$xTf`ED*yk%B4tg)UWx{%40`bOM-@fz-O+JE}8;*65FQTZ*hmfxe=& z)#U-p8b72DNovAE45KWo;EmmNF>SE`#WxIGpSU1Gld{WV9b5QB`^joaXHDlB%VL6V zq4Z6lk4vX3ugGx+-P*_@vu!<$x6q|YHk`JF3kq-c)k#{!*0vHlsO*(@DCO>3HmA}FDhb$ zR4G}k6}_p|!urFNx0>~!RgU)FNOS(UaP^BVRSsT64EI7u)~Gd2-6P{cB-hGmLfx_a z7kA7oGim|@W<9pr9Iv0H*JDra1rY>4sJ!O4l%`Qol^j|;j=5#pXO4Po%%E`b)<1lj z83#9MtN#ia@92N<5B@+s!9wsfaHBMf{xSUO28fhW+5>m27VNp5^oib>LU*SZmg;@{ z6C_oyD7w$+a}?T}icKt4*vz$h+YnU;ZLv5EACRWX^3No7 zHEJ^UDAykj+L}w~_%jmawWW#8t21$eC@6+po_`CXJ&AT9zIyU{Vu#z;@F=EX?fw2F zJa~N`@iRVDNX9|#=uZ5^^Ow-YKzX)ki?R1Bqi7JJ7_Zikzid2g&f^{)YF8ilKX)uS znu@m{GnzQ23H%Ns+ve}Prj9rl>NOkhK?4J#mcC} zMPzIOa0!OOWXX}_@G@B>@H08ME(!l^i1rTpe(pat%CY}-l#|kp_;?Pjr>GW`UtN{* zdzK3zg^BvzhGhQHhSsz1cc0I`sOPYKNCk_(^l|HoLG6!QrZ=m*ncQaGch{FGF*#7* z?b8gq^6|TowFf`!3BIq~P{yiCEY)~6%zG!1SGku~Tq;52o~`1qlK&$(pE3$-b8xg+ zqx6C@ZT+V&5L^qe(VA_?e%oW(cp-ew7TO%)$my(G+O~D#gWpaIy`56DRZeOiIgs6i z<_O9TKhx7Fa<%$f6y5PbmiWD{$tHlavI(vos(+87+nHMzn}Ylma%^pu+%i8W+P&;&5IqOY4!Q?1F^4-FC*K86F_O7%(!9txY!%rl(Ek%Z{ zJ#gTME?#JdRtYYJ@E%I~8plzZUAF9jPzHK*;qS>EjHGDB1}+Gy4M?0k$A&Glu33sh zF7dykNRIa)+I6|jgeTk8l=qmk9v=AyL#$r+!g^O zFUOH-fdQ@XV-G6CAZ}ONj{T1)lEiTdhRetdm3V3~uneX|Kzzk1_O1#xMTKHYtQ;@S zKwBLbjsG|)y_%K28EUZSX?`Ks@4Q8YxP zE;2|jxnv3(@T2tW){koCDM5lradHOb8IX3(&wygdK?iJ%VdtAr!{Q2l9rDNa#%BAC zM=W}`CTVw0WxQuwh7Q*4rSpxMr@$QB;(b%9dK{+(Q@i+v?_21|%DHtR2h;}){c6A& zAJpw(x6^%uXv0LANE`p(j2NzPGugO6u`FCR_Pca{W^~-p_u=acqtX8aBaZ*th<-Zgdlwut-HKM#C+$t(`H6Q_6*)OTTvWQgI}F0I#_R->ZTH#g{#N1 z-*WbmCTU{~p2ZK3fdICdS`AnZFH?9%O7Vtod?72Bh4QcvJ?o}e=skWE>={nLF zo1jC&<|^98mwJj%CI@gv`y+~ehjUWdp4)ey=)1UezxAU0kwrRr(S6o`%c5!u)weHM zB=UZ7K@@z=psBQ~ChX|i;>=DT3c?k==1dZet4U7Q)69$f%C_7~r3wOx9!Zdu^>r#O zJ4%bgT5ttz?i*gA6^3n)9uS=m&&1z{IuT3Q1BJd)pg7McQrRXXTJz)~uJvO8Y;J48 zIKWWme23M~K&2=G=19)g5cn;-mib-IwT+OSY7VH=;%2a~)ac2rg6BSJ%k$@wjFGm3 z&8z;ek7;m)^lh!e<0fUtMD0?ij7D_(R-KPr&&nOO0TF=rXn`FDuqpN)I1I4rNY!}c z$kn(y&nSOZbh%3ZY5hgf{C`3b=YLUD{d=$Qw=9Aw5DSIJR%m>W7Jy2gZo%7Z{Cl2L z8|W|m>p*9&fFyC`I%zY>?Zmj@{qVFy_eoL*q`~+j-3~ImsLuB`=%;pNX%f2Vp}?0N zXqv>Id<`_=@o?Exs_&DiJ@TlfjYV6Vn`lg>y%R)EO?d&374OW(N*C^TzDvJLk;l7| z2%iOK)zY+Ih)?kkaY_C;C2Xu1caMl=2k~zsnXE;FNXr!sFL}4LhLbi$>6+5Jg;61Q zuVd`Q;|=oC_E9dSrfq~7K$aYp;k(xXW7L)VQRbk3fW zLvSLyL1-kh$}gkcxzG0O{%17s*GQF_bwi2BsG1T7ObZ;FeG`f0a5f}5xUtjPh(hM; zT?_Q@TTJky(yNa3+)XL)0VolYsAa*$;i=>RjU<^OdPtO5*MiMgU9%~Ntms==6ODrP zO%#qF34}ghKJ2L#_(rYStoY_QdS)J@mi1rnsjx(6o1}i|H|lgZ=(+wey+j6A5QE3P zEOlKu_~MCZx4&oHYQs=Ty5Pl=E7I&43RGgXTIE{i1+nTWh8>Jt%SQ+QJgbz`xpIn6 z-}iq2o%hPRcS_ZAm)%L;OY7_5c+^h7hZdd!W+PM-QxRL40}ST)Yr_3Gl#<}zVCfoh z)zglKuTYblxv7d9D$);gIp!9Pg$b!-``HAuIFsRmiNr@n(lt`VX5brMLTP){GB#Kk z5bYPK7r=HNK`x6H!(fvpzke?g5U7~X=#ZQ6!DS0w@*w$dONQXOVpJ>7SHb|Q^nQ=X z_p@VhpG6Y9T4jE%{g^ol|2^A<$bRlfo7>Ep3E*YUBz%G#3!EL8Uf8;ldwE>kSFIF| zb+if%d@nuHvn;ZE9VU9k`TUyeTA8;y6yx`n0p9?Y%E9ur4QvVW1fURk0x$F7F9k`) z!B3s%#naSlC@Aj#xhHjVkgcqty}gC4nbRL=lHwoazJA0U>@oE3QZN+6JonWEDc0$9 z2Ab1gWqj{1jS21fjsF}q#wJr>rz zp-DR`C{sh=*jz#}BgzVc13|w7wR6)~j0s5l6sIg}v=F0IMhA&LA_@v=_v}ajGv-Y| z!N-`_-31*hUWMbN5TI7Qd&dExcz*XvTuL#nXR|eURfs>@og5pNW#;yVZJZyN5l^>0$k8fHt3e9HLX`` zXHVwl@SFg}E--KCg*mvbMfgHz>6Y;(Hao|-27!y?4SJd!-qMg($94%+<2yUc?5-{I z)=HEbAkru#T=l2UkjAk_4b7noIIdcH+)usE5_5XWUOCNfn)4TjI`R;!Ob&}xYtthwfsyJf zIwIrtWR{{U4(9gRp&v!y?Xb-D6TCpNTdlt?6CH<1;lL@453%6a>wW{@zTEVgVG4X< zzK6l_T*G8SyQb-sTC6#PHB>VU1D4na)GjKnCauQFNS>G%Q|w_)N-|RW(tCV^%vay8DZ!`~KxXHqGYLeSSHR+AlA7`TzNWGyxeqde}S5 zf}ETT%|QRpYu?n+&PK|~&i0?TE=vC_8k!@jDrX4wMl{1(Y+wx`Xe^nc(z-Xq_Xf?! z3gF)xFU*~)zr5R%BXZ#lR*y1QUOtzPw|B?9pEO&M+Um;W&gggA=cwIsS1j$BH7$nF=7xOuSn`tNOD|o1z2%Ojcm5 zvAYGbpc*xL_iKsbu)NPlHVLs@XImVL6%iL)=ak?^I`XY?EojLyQRn!syxN3jan@Eg z4Y-dRr#c%35dKx_1HSvNUlkIa_N%$&u&tY_!nXH~kQ!?Ol?;>gii$wOM*`h{T(Xc; zaiZ^w^@+iG0`g6z&B1d{*-xmm#TH244+Krz7Fy3~v(JZ3=iJB|wXoxTm85O3NWy@% zcxkj2;GwGBPivOT^R*!D^D_~p%DKoDNY$>jH;~7CyeS=ZQEBLDD0R;6*e_i4x@R!o z@QPk|_tzaGdyxKSZ@ELCBvH*!-tzXIa1haqPwbJ+8b)JgQH1r?jdS!KNDe!@b@7as zd1T7V^EGBBAO72r;x{J-nSJtO2F0dtp)`fqh4r(SJ*P`k+-pygyz7y6RhKR~J3j{9 z=P}0>BnMQUT8zVU_>9&c(aGLf%xahSO{u<9JxCM#IN>VD9>AiHX83)|{~|F3Z*J!X zEX^QfAJb_oLH5I2*jx|`yGUh_)ww7REDAst5DR%P?Hy1U(l0D6{sYldAi-dhNHK$J zN?4s=B0iDJUWoWUYzVk?ZD_+}@P;N|F|xN;g;)H|tc@?uO_(2bB|~?;JnE>PO~JAp zmRL+7mX{?P|9!}c-=`_Lo#QKaPtWV=RSczMn=u7eO-rv&*(@3&es5D^8tJ%o z27qAzng9zO$WKxpHe2)6uM*~~Gp&z^f5sB<91=Y6f<^z|jOCvfEYk8AzwgK!6L~N_ zLJ>X)iUkKmR{iaUC0{TcPujk>Z?!qa(a@7A%`2k*aN>D$q>3&Iakln17Nq`G z?0Y^ho&0fB9m?YtDMEGa%x018r4&OOKKiK5NrjdF)W?eO#~krwS44JeIj#qS7lQv- zZmoNk;>$;=>2+g7Zo1Hns4uh=z3q{66~H_cOtb*GX;F3Njs2R4 zxv`}(yh9#dsk`)83zz)}J{KQ^Ab;)ygO{|Bg7@a!N;)?F!B2b;Wb<+fb7LlUIi2 z1WIj&xB{C%@KB;c9K!Np%bxSrDk80Ybgwk;rQ1gWRf$F#st%)`0zkqRmDGw@kR-k{0mw$-C1dJ#ve8!Ow zr5@J!BVLe{Li66>(2v=8*)V0T_qMscb6vX=&}nliaxlb8ektH-YI^aZ{T`V&51GUQ zJw9+i#1O-#Z>ogbaLb*0)pU`wA~5naiol6OnZYEXG?BE5e7gDI`A%|l_%fX`5oSY_ zoBm{VG}<^z7-Ozs@VQjBx?`~NOtz!<`8+b24Wagg*>kpIO6RYxfXn!^T|GnPPQ)ON zOC0Esd(n-(kOLS`TrsBc)o^Nv3PdK|G1iUEt*gA`;wjISHXA-m@0|uhp*H4Ix`k+D z-eaLScr)zF5#z>Q^r-5APJlCkjk#IqxHQE$w6KYhBX2ZHzfEIq0*U2_5^JUI%p(@8 z9RH1JnY9wvVc!`kXQqjoi-6Dyh-9CHW5WO~;O*;**wR!ZcdeSA%Rkd?khPlUu%F_{ zD;AcIdOdBLtC*I}#^J_M?f zfJ;lD7?AyzP?vQH-Sb|}WPfSHBs;=eLF)rh$l_r7JDB4&$VAmznt=wAuavzBR>7mu zaL(F)RR9XMk;ln{^KGH&Uw)M>hk zQxuP>gKGv+cKM=ydl+i9%_St6=f38F1^a|SNL@08JXot74Pg(+C23oE-+_Oj&Vtg| z-Z8Eo$WU!Mnz4>Q85J38t7*>4tNG*x@2jw<)RAt*K>aO3sfgitL8()o6S5N>))8lF zkIrexkzZcHk9+H14(?c9Ly++{_aBZw5CfBVP2oZMZuM;jq6c~^>mEA4e|V*qX?{b) zKWCq#%2}3ZgrZ4cJUoU#G3EpQdfTssG8ES^9ZUpnTzZ$jDGB#6`U6HEC|9N`ZcTT0 zu)=GQEMuofa{8C1%)Mk4C9P4sk%6{JIS$~YG5xh`g@v|ovJGgPGs&#Vbe_nPy`(%c2ukj*V|=}Bbo9g|b4d%EF~`J$U~P~Kk4E8r^ODX>j;1mAWco?mmRn$X(56Gk04 zGWCE+%h^J0)GjhQIi+4Klq2Sw6jGt58V{-^_{!I|8%#QX6V)avg^^rXa$uIHd|6KU znn5Mdvf=m!;}AiW=K7BHkA!+8)CdipEb>o3u~80I)hKC}?sa2s?4p zU&;R*$!CW8bAC}2lNi%6@SHi`79)m1_fCHQQ6eDN>i11Ps?U#JF3GlLjQc5hzQC~l z5DLA|zzdPtRwTFt$w8ZC_Lsdrz9OA42o*u93AD%@XmfG4$Xsa6 zjhk&PbvS_gGp3V|t>1pWU`qf0lR5tlCg49bjmzVUV?K{B0~(k&3mW~4Z6p>IfQeBS zD$@W_1eEsy7^EM+f6l|Jfnx>oO1qlQet-9+Hb0@4WM=hAY!1%FD>atu({ zL-+3Z!)@1I&y(*Z_GeOWaMXJQAAG98r;#M|`P2IMGg@!v{gf(bXc~W_wAs?uF=SKr zX-p<)Niw9b3lapQ$`p2uRQUqIDL>it=)$$6ZW(yaj2&`kBs!Sn`fZ)*uN)sk1-5;S ziY^Xk7I)h2pBLVV$>v3F#HJs9RFI_8wcy-cCJ1$>vkh)xbAAX^L72Rw7t9~8NeW;7GSfVVV5kPcaK(xnM2a4O3l%F#(S)v}IVD_U*uw4=yq zi?oE$ULysA%8I&1V>8m$=r+-%81I3@u`MApK zwDq~q2_=Kx=BR64|->ZhU~&O*kS-_@?%zAqoBoB`qtoEt-?H2!qQ=hwfrSw zFulbUEm$q=-YH=WR^OSRTck`^?D>fA~f~r_8taLhM2ektFrA~jaXXMGS<#?9=4Fz z+x60@p_OfhiGYzI1{S{5hun^_x+i~jCw)ay%5Og|A6>B)vd;GAKO`yDkdUP02R;42< zhJbYRN#+<264Q0kx^Z~FUbyGAb3~`2S)+YT<+V0&A>z>2v2>_sHYzOcm)OaCx<9G3 zxqqs}+|f0xK#cU$kycfk+=_Ad8*iG*$2K!qCQNq6cH*OjRO$5Diy@>NeRnx1C#oHlqO5CiTA9N5G$7XkLY+8cs7f?`s zke)&9XB}1e$&~3K7!YzAdf)Gl#)H>OAeq#SA;v7$8FaTAvZMDIvs@&iH4+NZBNDoL z_m{S}{I8pFakquXo8F6s3;vsmjMm(OjC`T14;t9ktN*NmR06PZ|ndkHV!>4HVy-0*I)K>)W_`7 zKVFDM{hNvXbJZaGLJZ(*ddy|&Ff48%d5=4sOo;-r!clFEjt#|E2+dPPFA55BRN|znaShFcY|W zcXl)TP;7l5>`!wq^)6ha8ZufSfbW{;{vP9=$y>G+-Dqpmbom2MX%=HdiH5hn#*MX_ zK|YhM-6etuJZj7;mp}Fn%XWtF``V%-NU5M>2?L)PcaLd}qJD=`(Xcd|W5C|+uE@93 z_>iK)Z9{>yNU=#x44Jn2;oNOan6^k&7;6teYo#RZ7LnK3S+5T9_$nl05T190QV3ea zV-4e6ghe)O7VerB^w#HE-;PdTkRVPn|2dX`tJD#UATNb<6e`mN3sHykT5}Nu^?JX9 zReetWVa-(vzJv%i@Tg}a+M>Ye*ZvGk(5LH_vA@tRa=!2?Ip2Wx8D>{Zu1pE8?_1Ghd75I_{5gz#vNZ&laA^Pk?S0v%XmJv z)iq06)HiR!_*JImq7S=l-DBRn z%VO2<@p*#IeVejr$bY7bA-3FkQTToEIMy>4vj+c0vyC1`eQ@baMfd{iiDheBZ*b+<-P0#( z21X@t&h%+|CV+acEM~sc+lq?C+N%@}j+~TLs!pkdERLd*^povn$|R)`k_cg>G|jxA zHb`uGs?O*p(2>J)YABWiUx=07wRig)8Ho-o?N_{mHIOY|-zZtu&S|0fFZvJnl9d#s zTVw;Dnpj_{;#%J4KG#e(6qxPY5eC=_o&9>{KKlz4{(LbMw*dYdenI1O-eGIM{Dzp6C3){G z!(RrAH%38ur@yOK`aa7aRV(3l)q1w4AJDc(Ge#2k6$F_^kQR?NJ|KzHsq7aF*PPqW zRY=YtsRguCEp#gMU!a0){bzze2Nw70!&Z88pxj`~nIp~sSV&&EIqjqf3GT|=dxXPZ z-J$$3Zb4q1*z9}y_|;p_5r*y*N@BO(vgU%gC$w0A$tkKRXnL*XTcfKP6bwSD6TE4S z*@Wj%GUCuW`!u->>M8M@d!V3BwQ*3vK2>_Ji4g>y&H&)SGYbd~EF%{~n1$~Oq7&Ir zOF8{e=La{cCw09#p)P*i`f&YK!tgt4!?T7ouYMh(aqz*`&x{ty)D~vpfyA<-SC$}q z!ZH{F>FjN;I;t$4&ooGA5t}Z+s9COtXribFygfEYyghbC@4s{{?lUW=#_^JR1aox;A`fl^I3c{sO`cbid_6ukBzGL?4toj1NzQ`Yfkzyl4 z(~`E#j%eY4*DGYDnTGBisO)SG}7m+BpX`vG9+AwI-(9dkI^#hcQ|cFmnWlxS2_2t#X#Ao!ZKGV)Y$(0zi= z2^`JGeotD8-PW^-sVmfesrSW!lTbhCi8)oPn8kO(C2^}!oDi}8yJz?l8QTM4&DfOE zJFJ8K&m{&8-(Y;hn8MFYs*t@U+$YcaEP)Bhr_M#Sh<8Bx z+-IIh2CkFX7!sZ#MzC_K858)zqF%tyIUD?hVKq%~JbCqub&YCeQG97?3S{MAW`9h~ zgu&SUDighYl3(F$N75WgH(dj#F5hW2+8V9LGTj1GZ2y|GWvb9#db3$bYBRd;uZurf zmzvf8fFHL1&X3an*w6QW^^?=n{S5WnkLch1oc@a+JIBBIvH9(1a0BL_ei$|?80r4- zGx{e#ju4SUI4|$Dl^sW(p6w&vzx$c}S3lrC`9b-opWGwe7eCmNmHWT_488b4`KKR` zKm3%*>0`N-{@qV;Fs3K?x1YFw`dJYoy!;nGs{iog`)5BCzx^ovzxyfs?Z=AeSK^Bw z$Yj{!X3IbQwEfMG`HLTuKl!Qn?MK(N*>911Q#uDT+vFd9cv+#qB@i4L+mIjkUPb2e z^Vd@1a1EQ-tC07?4>W3nZZyVxdr92F;@NmUxGE8=`flNjBx@cu-&^scnS`F-$}lM6 z3hePnIV=}MduTfHi$0^JKQ^nIn$v`aJ3D_4ZMc zzt=LSE`JXVEyy6W=^xl62?o}t4P>Jk-L! z#;@<@`*i>G`ZJd`7>A<9(n>(#zH7T(X9ZdHWw4{L>G`sw(_7TsOrYs{N9%`UU&J6S zp)R4<%;8hhUdU&`ZwqUzF%oh>o2~B9rc1JVeOHW6skO4PW3`TwqI_p#Hd*(YL5G(q zF&obUs+x?>Rq#Pbt$y|jv@gJ^&bo!9hDmv)rqGzQ?Wrn{{pbTab?4!0{Ai@fN!A<7 z_aoWRa9^gx(BK*g4?Nk{DMersBJX@(-YQ#VqAZagOu|f2I5mq@m$&&7Q!9>TI*0?@ zx{m%MH04>4^?W9+qyo*h#{M{w@c5mn%8e155_UL>V?89A)Ja7OX-N{VC_Axm5 zX=;;r4F%ShUVpyp5X7;tA@|aDc@t%EP1^7XnPAqxZ#{bh`8LpMCq^v1QuYeSN62I5 zxT3PK!652Y->%KWmx|o-jS&{Wm}LO64q+K|(o17)+$)<-5o{WMWG~rApGmrritaeM zCA~uJ{Ysj2?o?fXS;B9blR&Ycd_|nMWaj|_CkK>Dh{Jt=$$()xllImNqUyk(aMDAf zbs}a}1N))_qt+g0<<3YS?k#|3j#NS~`K?;pb5FkN9~)V#v4`vJrCu+yf_)jf@iO?+0w2 z@w^T!ved7$D=+5pl&*(4AVH#Rh_x=GJtm{uoNF)7V_81$Rr&hQ$R@>1r-tXVT(P0C zV1;rM5MyAnm@zAm2f?{ZIV$Fnd5t^gzeH0ciaYx67E0q{mH01h>H^9%XtY!gt*JU` z?z$oI=+uE^gAgCaYltQ=1(f*dIPq6Eza*BS9@874{>V7rNzI=WAl% zsue0d(|BRfL{RZY5(q>Gi!BW;X0KOBSDWXTN!FK@G+jkU!f-|+3f#MX1qO$o0(L(f zp1NweGNM9uDC=NKV>!&gwAq}>QC>q0vUSO-Cx|aL0fKr!8Bn7?HVgnr0HLKg;QIMz zRp~a2PNPPloETnD0;*Hnp@CguTBz<1JCWoJOAU}N$msJt4IIqCrGS@e-o3}wpSvDL zx5!17KA|Ye4JX8QXR5+!R>5 z!*6pfaD_zD%;G@P82GI(`^Yv_$Y6xSxPzgSXiV?9s7<2ov)J@)rI_u9^l@?}jPlqk z&lXB9hBVOoyD}{(%KeN`)LQhCaX}xo94OsjkszO6n^eOLg_vC7;$=Dscb>Ist>(wr z58Bt+%LcbJ-@>wO%-11~K`2 z8%epSx)FMym}Cr<9(k0W8!}xmMTU?jzwb7e$on}5H!z);C%40ami(6`-FqzdvFC%? zr;3|)oy@Y4SK6bGru!-y-d5SpUfZ)7HUp*)^?V~t1;=KXM#gACPM-0vqnn!shn*Ze z9aM$MOJIz5$XUnw!@nE?XaN-9I+&VIe|fV(l+%a9{~zSV`rpYbNFq*Kpem^QJ!*ad z$SF|pn;-l~CB&d$uJ7mW<)jP0Lp(FLf~~rjD4y#YnUV)tHMgUvB6s7ATumbE!v98I zbbpf<-v5)l)=d5>FSIv#VU+ln^>v_on>RRWl{8g+ZrXxVI45upt<}K^v1*~6Hn)gs z+tM>O!miD&C>|=gt&84^43Q$Ul*uKKC;;umG-lWvxf+6$uQ{)b>bWYP*unsOxFlAz z7Qw(ij)|g-R)+P~+LOtT94tp{;(+bV_ZYZTt&Hi=Kif8))<;x{K52NFu~`i(v3f49 z<|rSa#M=S0t0@wyUC^PzB{)&ig?jqXNzlV;NnyHj5X#fN83TtL!-WZb?o?G**AwH$ zB(zaOz=m-Y3~L>bPRQuWz3iRL0Xi@bQ!n37J*qNK6IK)oD*4)u#$X>Pw^KI0xp<7X z4b1k!`jERRtge@VI@j?)Kbl%PW$_}e)JZlYMOiI>jdQ9geyu?ho(ctL=ajtRT92Kr zc1~S+2_A*izR5kK22$pCd1GG!kZh=Y*VB3s(w1@>_xa*44KKfNG^)B#-C;umcC`hz zdwGngSoTnKlP%?PWGZv1C#_Z^2N9 zG4q5jeIZt>$0||2ybV;vgm?>Ta?>+iMmqkv^fUowluP)sY@GK~vOAI~mSR~+T;oKW zBLI_&pyZLxJ*11MEc%96Y!{>WCDNfSW@DWqgGWr)|0T7Zz$`%zo`hbgG7!WC+?H#{ zE^ZPRol~1B=@xlVS z#)7iW+DAfost?9IkIzbGij1*|?B}NEOOtPS-z><<(^pNNz7E>>44-GQC%K~!JPTbB zz{7lZ^q2KXh%A`+>`h*Y|GV;H`-i;7{)@b#JJv5st4l=Zc^#sVw&zSP93R|lAwA@VYW!lDMh zsmrGEkV9-zj^Id^OR|S?n`Y6oftDqXJ3A@0=?M&uky2?szL#c%^|dXC%?#S8^UooQ zL;XYphIBfvntJj<>w!TNQ`Hk)2>3-N&Y9FA2v|%c?MUfm*4fhCC;%t+bK&<9DtMEg zEB)D!DM|H}=wMPMJ_NG*LDNUM$BEQ)auM`LTw&SOL^ahe7*JIX8Yt<)J$>-P=wZ$x zm_U(_U)k@qKPR1&Lly>lVNXyR*lIvD6!X3|^i1 z@u7&DF65YKU~{MyX_^)0On^Q)hAth_^@%U{IgP%Gz^Mf5sYR1XSNBsGMMPuDul_z z#gj6fWWAzEbV<3~V;G9pCkBCw?u#{kyihZ<*ju-n5nQzpE|we-^_Pki?K*+!6V)12hG} zZVCDe;HT+-8Wm>$jHRN0rv*diIn#9_kuu%e@7Tvx@f!Tw`$X(5@M`6`PNFKg7!hVA z60!#+cb0b?vm9D4pKs6K=GJhBi7UKdw#{bud|SPI=YtlCpn7=wgNy|;aZv3l%bXOk za?hn8`k|XVWueg2Qdo}dg5YA)3|#M7W2W|8*pEa+@9+(8s#-<&iHBnD3fyBTZk@Gt z+WIWj!lyx~IMf z9P{4=iavCxOL+;8V0>2YD!ER(_&<-L+SGycR8RZ{Cg%9-0dr_BsyTr|W&(*Vj@i3v{4c#)cXcZavJwc2u|)=ykwwocac zjF%zMe5AL22tbPhdhwE=gSV=`>nhpDOYEl@=DU&ahG$J34Kn|( zEIK2PAEh7Pz2k>?_m1O#t}GjCertUJJ!=Ca%YQWcN)$D0u!Me3C|9dKVrljMAo&l> zB^lLPq}18dB)^h1u{HCf^#d<_dk=dST)7pFBLiHAXxII8zVewPi1r^4@Et(KYnbdz@=NRFdCEh=9OKu8*_+9A=iLfpC5cEQoq&|DM&}g z`k}Pl>D;^=2HATi&_bE83w5|xZ_>ce-uf$3X}3i=Eva{|^%z>CLH^V1?6^AhT+|(X z9TyFUvR0UMp*PRBOiNB}-5Sko1o)ze{;+q)_f~y)IQJl1C9{_9@-ur+k_~{$XrC!T zN3?BE#SsU<<FW`#EC!{B(1C$dGYX6z)nIQFl%wpo!D?zH70~_HIlj zurzKEa^VZn8ZyTGNpf4KtQS~B_C8fp_mw-_SE&!_W&2oE>%y2w^=smf9YY*ixjh!I5tc~tO^I-7N1_Yo*u_YqdRB4s zdrjJR0?{i1Al#MOn?}yuoCr`zwoJ9^EMrUd?o|u2S{CB?)I(ik*xgTgH1xcKQrjO% zkI{8pVZ+m)krt9$tz)==2HFSy2JU>4$dz``xG5~9$<%0c;e^yvQC~nizKglSd?vv3 zp@2TPQp%w6)cKQ&SC)C+tQq!2T<)$(wi26`+5}U!$8Mg{h2zkM0@`{y~qLSk3Dd}4` zN1QeFLKQ|&d&23RlaYG{OOu=Y#6y^pS52k|>*QtQ`3>qMn9aa6&>h+%*CJjmSBnxN zWTqU`yUuQR+M^A~?D@TCFk#yE~7^}KCd?rz8`MTXQ>vL z6Xq^wT$Y`zj9?RpgUYFmyp;;;BdHaI!zxX2U7L+el%42+c;+~Tnj*=4&!zjHjXeCD zj3ZpqsI}8IA8Sdewnf*|Gzg1ji*PP^ShZ*tjithzE1Y@4N-Y;kZCG$Rf{c&~F2ltUF%Zc?N(n~eXTd;t7qV%B z0yqd#m2VxP*`S(iaiBTR*wc)a&zIB zn|ky8Yr!mE!)+CQs1^BAsa35P`7kNbAa@^(4|t_R?sOHiLtT}Wdwm6);hx+VW&cnb z2!#i|*q?o$uBu;FA_;gXr#}JrQwfjOi@ASEB@t9Ho7vTMaFd>ibPS_$XQ!8Hp?LMV zkTGR;MpUVph$`)qX!mTl+Qf0TkW;BVFnLTT6E6H9UvOFUFU}NUj__Kzr1BU5TR^10 zf`nB?#~DXY%qj@u>WD%pyOQbUw|!#(Qwv`ii&zZlR>>DkmYa)6DqRhC_)^H`)K4d9e2lR6hU7}f{;oRI1| z^*Jj0Ck|j2#HhLUSLlNF@c5vmq2xD*Dw66SxiqEPvCGEzpbZq-1RfuVDcW=$l99W1 ziD1OBNQQGzm$9<+KFSEBBTNgl1Zhnw4GvG0&Ikw=kp-?{@^D#^h#Zf2^jWIvCT*S{tlv*Wy0xv25F5ilIcV4m!C1 z%28?7)H-4N2`y2oz7~A;^JL~+b<277+Gmjj7I8qh3E?z?QN1&dIF22#p3uG0ig9^N z%6HWQ8>w`1DduSjA2@9GbM~0YQbr^Ue}8i1aQRsNT$H9QO&BvtKDm<?W zS!#)1-8nQPqD%7!4gnO9GG^bVbLY=vJj>ZZ6rhC7cyurJPMj+oG=Q~e(gLHe*4%YD zTi5|VcOxKgF1N@@D=C$^ZZrs)*V%AR$9VQ45Y!efu zJkqlG*|2)%D!2y2IzwjNTQ(0x+X-?`;SL^;UT`L66Y24FqNgeoN{cK7rBYb6m6oo2 z1=1%W+a7;wJie53`Z&Ti`rs3X33#1q?=iA|y};8?jT1TpIh+M+Ij{U!lsX@LEly&j&1e68k^>AgI9l&GZ>4(k(r|4oei*eQm` zG0tQ_1c)!V6{=gDZcd})r*}HhA^v-unYJb6=`n`$b~-JmKyKF#h~{;9!&(aJli7yd zPcL-IUW+J$`}28w?5zUX=Tz32(p&Rx z&)V8ef*Z_6SPlR{GGg zn-2(23D0T&odna(JOmYQBv5=K0r!7O0@pv^9kI9i7YeKtu#}L!Pe=^oz7mg9(9VXT zE`3_0$z81b0!1FLi;3RB28)-THo`)+&{9|Bfp(4MBjn>ZlQKJj5qzG*J9xgl0zbL$ z>#b|N+q~j1<~BOC()N1)Ec4FDl5x2XWK;MB>dg>bVjB?;C-Sq^R<@QZbI&1|D~Bmf zEIzkV^f0oXnV?C%nSw*DLM|0mh~U<3sO?Yw9?BVyB`jrtOz!Z&5>0Y`ix$#^qyvbj zXboI6oMIZGe0g5HP}-$PG<9kfTRqNCmdr5^$+d3n&)&0TfyERN9Tyqw_f>No`0~ND#K|-gU}s?o!aX%9U5rV;o1%N12LhN3ibNVAdIyLL zc#hh&8EzU9`k~9%?oz)|!#wg!_&FCq(-<9huO_q(qq3%V^h7+TvDbFLhVPs1hdB1; z0CM1=6Ncl@k0Lc6PUMRl;N{PAk6{WiV|L}5ND}L#Hz0I`*M}W4ma->CsAx>=uCG(B zP1KPnzPpyINh+vI* z(kRMVEBAx@-8=jFS;6jw0}jy`aTlkFlO9^IX%*sh(JUGx*_Q_mKG&J?OVrL?qI0H< zD~O!4sHB%-z37F#03?wXv2sv~$BBdrnhed6mPPtRR4)2N+;Vh@sL-m!N>C!B@*=>L zN09*=y(qwcdoO3={N1IbyztREuTh+p1vwl!9U?2P(J6=gATJ1@}McN z+#L+v_!qe*&vj|XM(_;oh=px%433CPpZxc;u3>cpOo42Bkl*%AWbm(hnBV^Io#XRc z@2+b!B22GcfU_*28%d;V+_k4s>(Wgg!gDH?UWM_cdE?H1(E@G_QKc9XS8IxY85A__=jb^;0 zhdbAttZz-r)ANUyd>8lf+*Sv!YDlnaH3Xv|akqSkjDb6|Ai3bpUt;YthtCJ`Z!7?U zzkA2?pRhpS?Kd`7{|Mjy4ug#Ux*xDetEZC0BNmut{ztN;#brNS1&Z~z>srM{Q?Pa@8v=mo1{0$%!8tpcUgYlh)ixyeY{_hzJkf1SK*`A7O<#V-=7|k>Q(3|GY`?_cMQ1= z?*Hf<2gjkMjl=s$gBW>{`}uezMzN3??k;+X2aUUlT&RpA@3g3WDp4-5EHJ;wLJ8O~ zm(LhuT>r8YPf`g`lK~j5fo6vcFkCnwrfsg9jn?qeidqrlSY?h1Q|lN6em7T`PM9taoDg zLM3<1aFyyNt_v%Gb3e8#W%zo0dXL6Wk=Z^GTZD4YQ$w$?$sdLX!2q?Jb?FPI>F?-m zL>+8lQo+^Hi0>GYy&FhUs_@Qa&FMiVvW%ru&Jw6$1Mc#3#;w_8U8{L4TKQ4CzkKnj zjZ=(zTY|r~Ak`)bWBw+&wUf@P^__Zfs>rXB9wZ9|m~9}9IYJ$^p>6Q5QuddAg9G%F zzS0e?QZ_AC@Gj&Q;yg&0>Ah|WCNViMF9s*_>w}4Pqr<&%hA`pI3&Ox4-iXr*4EGl#7uCldT;4X-~_=0fz+LlxTO|+c%*(F)H z*GXiyF;4GFae5c3fA--4&kYw-EY!yauuM$r*(diQ_RJLi9*cR@3U zzyI}LVz+t|JG}P|_M>H=ReCj(6dAGX6bJ1Uc2cWs+Sdb;5x+ylT=Z zC$qwyLpJMZveD|F5G(KI@q&_D9I!>?C<84=jz&ih85wUM{;UAKjKHSIBQ=V>-~*A9 z$I?pu9Z`Hu5w*2h^VVG`rRa?cw8-2bC!}xd1*-{zw}DP8W};2qGx}=GJg$ z1jF#RL;a2#0dQeR2re4IifY@yajBEr0hb^3mb=kiK~#Bm z=im4=(?Yqr5`K8K?rj^xnhhK2{X*=z`t*rpHT#RR!RftmPHm6vI)I0Lc+(tQ<@ef8 z6A=-|+J6rvt$@N3VN7`$Q1U#F2kq_^#MW zOHb#q(K<@!!ov~4KSQ|1^23>#-0w#Tqk4xrs+gywstS%zF1?5UwSvm;(LNlp`i0M< zn2HMm7(h%?cUh&+1ev7{&UEvrwbj(;ljHDGfAZ6b9T3dMNp)F!N z1J`fjEOYa*Z{^0(H%QLc_{h7HQas)}))sV%iR#Eb-C6iVa;yg=lG&>jx0;D)sjlSc zzLFUJ2}A#gnB>#5LF*1)F2E#&%`{x)?l%BpStclj8L4Tw_3n#6b7blrB%iXf`t~et}}$7NR{>-g|mUN3d{}Mc3*P zv}iLQmJoXvW|}yyA5>g$*m%y)a?Ui!=qKD=S0S-XGJ>d9a!RO|gZ+H^D=uzH3+vWa zCl2CK7oVr6%PSe50>22KCw#9aB<~gM-&NjD^Vz=gP34+z4qxCuQMr(#se+NU;h%k9 z&%n{_Ur+XzC~Ey4?c7z)&9!mAqrU+84=^Pe)hk3n@&`=B%+ggIP88VwzoS3y`9CZF z!k5(x1^6~!|7n6~+3w#fzsWICX;B`Rp!!N#gTwAHx64hI&+8p@J5(m!@~z_7gdX-j zo-6$4g z*;{@Jn=QuSNx^fiQAU-t_AKDDgXUIQc($a!Dvzh*Et4!vHunn?KNE=TMSd1qCiI-~ zV-Gys8ZQHt4Otig7v>17-=@7ii#Qer@Ea|hq6*zIHLTyN^$AkxMRCkzF+Q-<>?iJR zDna#|(Fb$9)WYpn_}pt=Ev5{8LB*Yil>^@F7}8=gtC3C(&z9+Df}1C|8_yrz~t6ryM>j;Gv1aJe|?v zfYR<6AMWX<0I>Sx4xuZ6)$tsL7I>17mQ*Ai-KwFlt0C~hAaJe8_CrtWx^UlOfa@6X z=7$AMl`odtw_hX^)dye!-$D&iaiK>vG#wVLuVLk{f^~(*k5osxgAm9+tbk<0$5%Ny zn&WvnhBd&7%j`o_S+v*pY-iQ=DO3D~ID|N)+zlCDZ^tkw)96u@O}(YrNyuDr_9d!a zk6*7>z_(Y)NxUp6XJ7-TBI6x#V_eZ6`6L3jQxBZ0z1^1XtkTZ3aK?b~so!rl`+Y zz*p1!GA?4!S@oq82=R{pz>T=LvFfmH`zZ{j_spQA*zuQy$?NysJNa)ASDI!SYbfT}VPQ^FD3ZOeET1^|W*Zb7MTy$TnYdwzpV{a~3esx}Ngvb6qn{Ty<3 z1eXBK_cT^lc!hICJ3R}4L49-|*{W_qx&W^wGWznyY{QjvHa(DvP{a z+Uc+M&%qU%5ujp0VafT(&nj@Dkkb$^e(61RC_cCO<-4&|iGAS~ z95;%HgN$pxhkr<#hT71^#n3kx3?~q5Vdkk2OR4PpGQ#GQoXrwzrh*aMXG5&Jbk36Zrxm(^EVg?Lj=8of7{U zl?Fj6tTJ0iHVf)1`8?ei=@{ua+=bz`J8}af#ytK|;7*{bzyBP4i)8rRi%yl5?8%D$ zWM8fP@mIEEQL$b8P>2e=YC+Y~LTiyj(z=X{6O#RxUi&tIEQ|_6;|IT#DL8vWFX^vQ zhieDs2PQ$Ty69#1@>e>i2CD!v1~mm-I83gq2;R@5J{S_mw}F&!PyPAhwy<+Dc^P`c zptQgQUy+zEF3l7zUxk>w1SLgkOl1Lmntk{)!>2`TSKUAx*klas@x3HoC6u90K7kM7 zZ6?FXkMKeJog6Jo*h09mb%BojM?F0t!f!lXcM*h=TwNd?y2q-N_m;84(cmzzfo^At zla@bYrQ>RCq+2hsN_mKkIgg4o zdqjknmp+D{(Vv`lM9M~%W3I;uYDbpC<3IkTXj9(v^ic8t)fc=$;D6`~exC$ax`X7+ zS^w?}{(chtzkNY;&wt|!8vgDJzCy~NpKwOTTpKS*AL(Q3@Tg!0yoqHfjEI?&c*o>ej z`l37gr~%*tZSdVi9U`#!$vpSYJOtkb(?ak$5i7Z$!O*kOE$$HkMFu%+Ng=s2h>GMK zRp1)?jxNTa9C~TD)2f0!h{?E$?u`ic-^`F~7;}$%PgaF<)D7y-Dna{5uV3>y_n9!ks8r<|6Ro&dev#qn&?X7^;uy`D87hMiI$7n9OcHW$Nx7k9_#c`IyShCS}t2PIti#y{&sIRq$I``k*06g@(!%W*?IJc-e&#>WH zP}egF9M^G_|9u%jc5|ymxB%@HnIl6iR(`-3ozaw(f6-XiII&A33APT9DP%gi)N<0Nwflo_(8T)@z59wHRpL9OZp!Dvitnca)RH?adypn%7a`3kM4P?Z zn%Wq;7wma90J=-;B=5hi2|^4)m{+jIS4;V^?gkN#vDI!4H!W*+*BB|iV)jr`dT}kt zb1X4YGPASHno;2?WZy-Lmkdq5-Q~J$BLDFDB$bt`pfH3)YkR2HoC`Y8Hd=1h=)8@G-LljCwTsgEucP5a*%l=!W!6rXbXh(%q*Sk|Je*k z_-8$6K5~;qT0D;FE97*w^argz>TUhF*wow#Lrp<`_`#3g<^s6S-SZYRoO(38q2DMV z_~r^WV(^04st)T*KYu1VT;9=abC~scx*xK6SLE6+dwn&|%!iyxx_ldChqPk`3N%@! z#e~s;G7?umw%Zr+BeOEMeg5L#Y&H)O@|AP%}hlkdL_fO1DfUgW@&K zp1p2<=)+2{+3>xN>H)3? zx)6d2x^=bVxPwgK{wK{zfF`+>D}#(in)BA<^n!f`Wl1Z}0hy-9@JAlxzyWn5&R`H) zN3!pXBFT5pN$4uo>`&S{t^qP*0%_}PZ%aP(cuFPqJX4G7v{G|=A9HhOG>aDo!?M-$ zEA$UNV+rxP!l*I|fC}xW#6*>(z-bnNqGoSFupz0X4lJ^5`T$nQrN-STZq(6=XF>GU zZJ!m7D`o0)a9!qm%JTa}lU$HZ;b{SHm}Vy++r+fdll|Mz6@6<<_Zhk)^!FkAaA}Y9c78x%zH1yvccz z)4_92RGtWUDJ5n-Dr~VC+pSTHH63rbe5!*0>YDG-;5>OsP)tMuaS*W8xiC;Q7B^oI z;BsgBdM8a{80^VX&4~oW@&??FxOn-2{Bb0Zm^^R-d%;F z>`*lCxZJN$ZZl|2EHpOpNf{%dpv5fwt39|N>9C#bk=$_3EB29CqqJ&7H@0qQtTdD4 zN5~|fc8Rs6j18U%fSv5L|MRbIMFJk1#XJBCCWbNqiUn2Uyzb4sTtq8gKyX$+D z3g1TGuq5Sb7ARsAqz=rkfE4RF21iZoxr*<}f;( z;&4BDKYDXm7nSvXcpbK@xtJpo_J?fSuhvtu{9;XF(60He9cTku=D>~^ja$oE3q=ek zaxAX{eb&MXnVMonaU{a8{`s{m7+OOMAHR!f5#S_#An zYYVs)#`BsKHZjsv3p(1MpOTY-!xO&ZniThyIX|M!1N83l^^Vx#3Lt)XSORfLU#u|A z;A8LkxtbE?sn;CTvtvgILOEm3(zIoN7Ty47$7tE%7A10<{CZAAX)x?2&s=(J8Dc?G zN*@~2UiCGm-cc5bJ;6-}UNtsV^kVU-0nG=uy)z&>mpytl6TX0>WCdr7G{Y_@l$P=1 zYcOrV%n&sy3rX1HfNfcp6_MGtcytQldWpt|MXI1>e^)djPuMwkHUQdQPqgn{cFBE= z?-$(}s`gZ?T>ieqPS8)lO~>34@N$dp8kJqgcCknoN-%(Iri&b4Y~aDC;@(`zO7xT4 z+JY>G3)_SVMT*;j1Gst42~(1!AL99qm^tCeSHVWV4IusMiWlkd?m0`B``-87rGD1w zpi3&2rNId&Qn!Oi=v6w&juIHxeypk^lHwS|H6}ZK$APGn9^r$;R_8&fNFPd0m^atK zpWYYbbQM&O-kE*h!|>N{AARX3^%JIg=y zSZGi#Q?>Nu%~5<}EsNi@6eiYO=mBEj#rur0aEEiZy5$qFb822+oetXXUur%RAz!~4 zVR{c|?V=(-(b?hAqv0dQvElnupO-u6c0?A86SvCKJv#XZ2-eT_&ftS_{I)WjIx59} zV?mj5KY9`_K(3u_QT3A!#J|HaVVQ$=w9ymowez5zY}=3d>o-d{b)FX|O`@);lL=KU zT+~XKO(&U$pGx;QV8|P{d-qizD!V=kMYFKO;W>CcwX1Z_ZErJl4KGd1i!x|(;KW2A z3k;%pRQ6$fB5ThoT`1hNxLX3S58C>5SnAJE(oz zzyaU=*0R*kxJ)xWHoWkp3~MH7sJG75wFe1q)jSkk%B&%yk~*UVbSRrh>mtfyaD|%! zs9-W2aWrYx3Cxjx(8AAkhA?CSSqt-hC6ZLu#P5mn!YEuYvEI93hcT?JPVeoRBei2H zOdZFcx#Tw5L_|4ruAf`tk}N6IvjSS=N97p}I&LbP*s+Jhog)aypWcP7x78lSN91QR zIPkMTj@Ai*EA#3lp^KYjf>CSqklH70dd(Aj3rc<8;(Ms}?H71N&IVXl$a$At@^mCt zzQ%LQC$E9w&x;VnIB9eQeD6HA4&W;vAs*2xAs*EzZD15hlofgh@s7q+g=*smHFA-1 z<8no2tIE8k)%N2H%LP5`xg`Ak>tO0clT=>u+E~bLr15utQW~-Tl2#F0=OQ@&auEOv zHw3e=H1X!%O>*j_-J?};>8^qh!i^*Fl4qXS1oDGFU6sF#WRma{ce8h?cA}zQoOB4FX1$8wb{ypn4ZL}5Cb{w4jyp8HR*pnV$5+Xb?lk{J(KCRpfuj8dg&G^i zSj5?sT-{rxo&&1TxRy9)eAA-UP1pUdefIlPXb~CW6T(KqixR!nLpB1*CFND2asOq= zPa&esj!Yd@uJ%3XZ(6+VchH)5D2#2%IPDy7LLYC9O0!89PwiHk6q~$>2!-?ROW1X+ zj`UY3$PwOMsIQG^xHEES470GK#{wIkvdS36p!D8H%?fRJGqzFfF-iZ1!SJ6L*Y=DQ2T$qhV4Z7ofp1AfCb_|yng>3 z0!p@qdXE2nJ68TrJJ#>-tvK=uNFow5Y|Z@65`@8zm}O`to7a-noEF_0FW_ZQAE4yJ zzw)C-J&|=U)WoqBAx0CFjHdkg$jt8W%k$W&&j+$~@-9M&8>wxu)`!fd(vsnr%3Szb z&~RC?(p*K*px8vU*ILZRyv2Bh{F7zuY5wazuW#+@?wUe`ptpyjq za%7>$omp35vm8zF&?KmUUZ1PL1c?@3VF>i+$cdD<% zO`lSFJ-xH$<%_UQR**lqkcEgwAc3%{hv5PI@XY#zlYy(DshU;pWK1fw%fkAEzp^37 zP?->i5Qj8lp5^6%ybu(t2SgkhHq(KG8ntkYfwg(%Ep>`M?eY}VRT(U`NYlzb=ulTD<&Lb1OMm4?me>`u`nm5afT^VQ%lE$f(_7?oH4r$RW(j8} zF87>C^KqF`tI{H}YRFzcR zHvMrTLKLQlm6=;eVN$)jOUQmw{R=dzH>}CKC8D@ozA9dvRu^#$aO>E}xY^T@=1VN= zmhT|vjr8uMf(du}!4g6=C0La@J5upH!l(}!C*vF?LJ6ypnjtUr)#dVgshZ-=ozG|! zW>7l=_f*yWEY}d#Hrm+_lq@3BkVUZQTP*g;0^H54JY5`_f@}t+g6_~wxfWY$xdjQ_ z-R5LMYacybn;#2+!FO!PQ8(?Lp8hl3dpv_Y6g)f;TQGQFc!ht3fSZTqLhc&`2;Ly@ z^*@5Zf1ikDC>~n>r5yWJ%7WBt&JI6*{*!zh6KttM%=G7Nh`(c?hLdGOXVs7O7v$ec z^3MYT_@h5@`atGiWB#;YJTEgCALrKEULn7ShyV_2kJJZG!45o5+#)N1gMaVDDRRvr zP*C=?E}=s3puMk4pcHbR`DAccF&i`J5ahIcs$MD^Nn39+<65_{0UD?|bthOxQ(Yn~ zRxQQ3J9)jxMyE>A=oJ->B#sRygW zc{!=9CP7j(ehQx4S{kL6T{po2%@k;I@4Z3o@~gnd=0jPJL}gaQ`UiWRUaw-PZ3XT* zSV_&C4>vIeF1Q)iCEz)W0M;>^=~kL4*s{jJTX|QsQ%1p=7%X_FSH5MpQ(u~dVJJ0H zt~FHj`IEpl@lA(`%<<2OS_E!{>9OE?BTCG7M$6*(z@_QL%Hs$IW9g-fak=Iml>B4i zI=pO`(dzH#bg9|U)C7T50@XSn`w`_i_4xhlAxKM_r7i}eA+JE=08}JujTGDhmco=6J0)S&>lLMU|wLCKN!%m zD60~1udiS-+;gB-zG7ROBYfbC{Ym?I&+M5KN61q-#2vVwN}0ZX$@NPr>UTNbp{&D8 zwWJ?0xy*-SEGI1YcpPRf z3ysZnZakM3oUh+Q=X}KMxK5+I)0mtD$JO6Wf+I-_H`BNhn@i~Z5e>> z8ZcD{p6mvsY|}kA;r=*}k3kBP`iSv7TbR3AFT^3f*v$PnYE%CbFKLjD-DjXUpbce_ z)(<4LI0Ux__DF6;=g(+W#tFMlF6lu{@cQS;OctJR&pRx;VV08UM$GN%238dE>Uv&c zC+Ji#ONOmW#FGE1#^;?zjFpE58Ut_ za?5_(n!?J6(MhBSZQ=V4A9_LMCXm|i6VXut8_`F|K#cSx-)R_>8r2f@x<*>1LJ12Y z$S)pcEYE?L>V1ylO<*>mCO{&QhCi(8%B-D zjB-%V2mxwDi>v6LEiEs%!$s~d_u&$1En?aE7k^k&%hy(om?{aN7u3y8tS%<5kvTXJ zW=S57c7Eb^0!L9{&dRHSVnYdcN9#%aT2&@B#5x#fi0KrTY)Aw3iFL>3qNWVaUE^83 zb?y|d+GB}aVaR6B)Z((#=2DJ+PFTkyXF*eGb}Pqf90pg^?n6Ka(a=qza;$(dwsziA z5KZlaXNS8^6IwPM{U=I`M0BMBcSI-s(4K6z3GKjhTLBIL+L=g95m@q~v3ya7@_0aq z87rZE7kYWP`X>A`-&*3=rZFns=mQ!Uj}4$?%bwt*jRJI`U_9bv*+g4Cz65Qw)U(-) zxnltjMW980WEDx7odC^NDB`{1kDf!DS@q^LFMpU>gn3_|%(|Kq!aae0)oS+f)!Bow zwQ4Jg7a*)pT08r9pV|n?=Fl-ZQCUS zC-gKmo{w9y zH2FzuuLCiYH~r0%C05|>cB{>Mzl2y23*`o&*o6r?W|Tjk}pM&_g~0OG+BB1uZ6 zM7ivsrNZBn(crQU2ZMDAA3&y{Mo?v@Ad9V(Myec;84nn4ln6r0lbJJ^fkco6x@uk?}n|4{Z$L7D(d zw`gnHwr$&(wr%rk+wN)Gwr$(CZQJf?oZ0*QH*TE!bZ$gFRAyG@%7+zM^^#etbh{f@ zHWUm_AFscPHc6{m*K}>l_bnR@eDt-X5mRth&Zv2FfGnASa^AZw7zKlbvRNxbT;b4jRIQ?Q^k#LV>c=0rqS5c%uax#hi8w3WYW3-Em|5> z@X<`zsY6*u1xM!Kd_)`tYle(me~s8!V2kU%d|g zAbuj4C?WAnE%TczYd1Ep(|qOQt*T{`!Df#(uiNsS>sJWx@oQM29MhYZ`7%OSnyGnJS?4Lxyjpm+e0=hyYy{)ZMtajCD( z((=b6Py?`-a?Zj`F(?HS7u7T)M24n{CLBU=i3gzH87+UvJ(;-BcQ)3Kv`wB zRu8ti2c|t+4$98+vN#>$W?c?oXJO2)M5^fgQJDX$XmKm1pDIG{mXBk0u~Bv_OD&cg z%&l_ybXVZMO6b|8H2->7CK3>b27kv%xD9M9aleyicN31c^L9F361URkz*S=}$W|jF zv9YzWVw&f2XqiSlvWopMp3g+p3eO^&8#UT-$GIOiJRNaiVAW`EczNUMXWVpUJBClvIV;st&%QS1S;>a zKTZrU3qQOznYucoc<;;cdWg&`7R6QXFX1o4ST}99eT5tK#j~Efe_6?iQdUc~Xg3b(#Ev{y`zO9RH(8q3bXQ(&gn6s|tEWZmY61dn~z9L&mEOM1G5p9f{^ zz&PTPj=8UHZrhGs>#(otUajHxzFB+*Ak@4Zxx5Ewy56E}$zNF#i{uCieszgk3Sp?M z<`Hyq2Cd20Lun+}T3hxC%nSxa>Zki22o@O4l%(r!Z3#Du*;^_3Jnb%;70@o`iP$%aIE8rOkun^VE(N*VIj!xvBGlW?nm zePumQI(=PU9cl5Z?}}vahMXnMDSPA5qH!Lxf=dxkTbd>r!UVD!8q3XyOFS!gl_U@) zJ7q#cNOY32UO^`LG|Ye*7f{?~=7?BN;-1;twV z=7kJ6+!`6#7>2t{VMu53TGt8s6dA3b395Ra^4Vswy!NO$yV)*$0xra82p&KKLF0Au?J^SvC*uorodR(x~T zolR8i>&U>+c}L_eYF#J>>t~_N`k^mMd3ia4?!-z)w5A1}+X;r|{Oye}6~g z4~#`aU7*-fK>(T^=F53U$HD(ccR;vDR*&=y#YRg{gzZHh_l+LzxhEKen+D<=C%-~o zDjf$FJRqOyQUFrar?&PWwCSM?%6k3cd9r5eEE6C>p04JDjYM$hm;HwH&2Ynco1GYu ztJGES9xm`=ukHyWn?u>+ieq46 zD2nHr@=x$|d>{#yma&s0!Ki81v!p=m5abg4h(&{46ee$vDMVxYy*_h*VFk{vh@_&< zwPg!EU8iP(PBe>~Ily515Gw(Vqy+^Ji_Jz;^)(Ai(`7Pc+ z?@l6jQ-)#Xdh#tq&fqm;^D>_jYAG+a}@#z>2rGY)q## zqS3cRRTlxK^ix#sJ-ea%{*3(|H@^?{w{woR}4B%VIamqZpmHm zkj;^VzqH_;FCwF94W;g2f$M~vr~rL(lD2B9Fftj(Ev;X}0QOv-pJe>*AbN=_iV!w> zl>%r(xmEi8yNsJPI|LuprPQ+1dX=o6N3pbJU(2{Krw;_NI+-q3y4*|5tphCI!D&e7 z7jU4j;~Nr@-QbYSqKRe=EJS5g1sv-WgRM9<+raCqYZ-3MuSmaXWSq9AMY z_t-DX7NJ?G52M5I(H%{auV`DOr6>~UH18P zcMKd3ab0^Nzk+o0en1BI7}9hlrqtKK?h(q7J$fEefjvZwS3j} z<)cLA(+{2&R;9X?B_k6PbcLw)mX+xmz$(`|+m+{q8R6QNR3r>Xr=oQa|F|u)Zwd|B zfhot}qEK`zVN}>M0cgX-OWf06lV{U4Y!?2`>0w(8F~3g6kBzFd0@jQhtEoF0B$m&N zlt3JH>}>~)(%2&~Ro!NwTX&elWDZQqd zxXjG%0=EaduT}H%2|cI!RnfxQNHGXn?QWBsEQLtB3%c*ZA1zwtbphQgoB4yyg5g5; z^}5JZYOmHGpp?q1UHX?Eptl~NHGOXym6!ZTAJR8p(zluKnchDN>pvd|dc_m`F7>lu zUH_Km?lT4l_1}iteQgF5P5+4=*Lpj!E<0q7%pE`DJjXNfpkQh<`WYyqQ;_|v?>6FE zM=t>(TBY{E;o%n?H|VhNZRTs&SN{1K4E^w$Pn!y z3_q?v2Ysy=rBu~=-GuVCHz~pK5y_i5nEf5>yKht>FEi}v=Af(t|ID!I-F;J9CclqZ zhzlLhU_XTj9vpV3Od_6lLfG?=3lKlncRU!uqqS$9?fyH_&h1bh4CKM<{mDC)bD_ zZ3qU3dm{%lNgW9cFKJc~akJ+Jvc`WkY0@JYhW|JjvR%`0akJ9pkk=z(oN;nbkQf<8 z)|f~1N}{@2PdodrpyzK&M5g>Hi;gu>u4G8F2^VnYzk=BusB2g@v(ZHWOhB{0wzL4QJ3oF*@WqucKhmh zSByFz9I!72Y=Y$y)+Y7^P3UZvP`gKy_~zIX5v91>5(93qO3?YP)qPaoLNek9`R>yt zR`h^kj(yQX4Yyg7i~V<@&|7@ovpX!1gUdd#?Gc&@ebul_rAP7Gmu>CBrm#3sqt4Q( z)ZVccXsavP&hZ|-2g|T(zKCY<%}$E6m6my!P8=^MNEQ}GQXQC?>VBnqZc(hUKJOd7 zK*Pzt43yFJ98Jyed*1QV^48S6G*pXHV6b z+07~!kd8`h<944Y^68KeCa_?HnvjXgcO^X3@iE_dAa_y^vlUxD-D_1OF_t;YQON|x})Y+`_jZ3KD6f#`RZ&g7k zZQJ(C2PHq0DAMsU7c!7LHg|WIk4o)bUiVXIJ`qM#et`rrC~|SykG#kOzqhEDDXHv} z8KYOnpiRd>;_AihmMIzWEV%Z-na6PgVll2H6JvFQFn9}-*cGbAoIYnXXx47_2t%tw5RlJi^Y2+=2vPKq+Fq7|x zaLzN~dmES?O^`b3X~bt-;_j#)&a0lS#@C$msmSK*Xs^qLuH5n&=U+ z;%|3IBE6^`X&;)Clh_&4ByKh$E1SY{ds-zn~Jkhs9n+VHEvc2a=c6WY5 z-G7nBf5&EsZ2m;~iQ#otSbySC_+|dk-1w?;JUJGiO!s2=2^5r0Aou;1`?mWgaCG!J zFtiPa1S3dryTl1luz}h)ruYf_X=PxB^dbuWeJ7b~f~cqo@-{UtaGX2Z4Tp2H)4>zU zaHyYh73~l9(46Qu+?$_lbLJbu+li0qdo9F&-Xo6H!<~2RqETh1`xMv8W<)Oo9uz>P z$x5>G5Xs2)w}3i2vR5aM&G$A+by%=^^stbc9&C5Ms`{_DSqd=@Sbb~r&Mz5B^xiKP z&M;B_Y1@P8RqIK1cp%De_p?jSceEh_IYk7z8cio6?!^vQerLFjHAZp)U7kUdle}1p zbS3RIHwvGGu#=>i)`*OCF&hM!%Hcw#2|ksX{jdoZWwRwW>eF$^WQ5a0 z#$F1p5ZKEN%^~PIPxFdF!M%`1kQ~cb%A^hWT@y2WFWm++79Sj4lh-E22p3~MZAw|Z zQqB%n&(9}!Fdx}`R>my4yGbeSOEFJ5)ZmD%oIQK-ii8Ph++Wzq4)co%9Xj%z{f?l; z{8I9HPie%!`|bs}qVxqBAvA+^eTt{|&SPe-v+VKo1}?6%uKn4LatYOAEr6KO#=$)u z1756y_jvDvmn>^SuI4>d%?H-Pnjwbv9QyW{N8Fw8hL{U3?~pup#d0P8tOe$Aw=hZ< z(@!N7zQ=+IM3LKajOj(3ja!Ifh&l8L^Od*M6 zgVy7WrRpK=;Y?GBu%!|q@7>JKB7AgV({&2Wv@g>z@g7mCS7~QPdb6I?E2y#BjYlnlrXN*C zJ!eKZe00)$Icn+cR8z+I-^E{dWRyaM#6kHZH@v|B<&-9mO%AXC?WbLxqKtdBx8C23 zL*C|JaryoU1u+Cta-j72lCNudY*dl3GZOytt$!WG3rD4lt+`;>YnG31n@l%AOxlkSe0 zr8DIKhTT(OJecW%h||%9p{l{1VT;w)ncsc*Y3m*=3m;NKj@iVHtDz>fagtm4X=|yd z?UXfkPRhCr7(6xyALJq?c=%}T%v5&T4Lt;6COR2uZwt!0Xa*j9@sr(r(s=E{CN@#j zZc~vw$`7s?lV(U^BmxChs6%^>1J}&K>j1kj!oDOGRPAD9e~?8f7pgJ-Uqo8l1 zj8qo0kyPnJq4&0_<)g*w3BjSaA*o5C2!ECII|uI5P5=tj0&2JS zctjY?{va>OFbghLPbrirR1_?W;L%3;w-aGI*~F${h#Km_N_&oMAx5{ih)==LPK}Bf z>!G7OPMA-*@EPgh2Yaa6rTu$|)kp(9UYJjfjtq6tV?PeyGZA0}zX$Cw)<%wWF#=ER zm`_bJG1*0pbd`xu^-xaHbsO$rMtdlNq-hs2wGI#AhK=a#lAO>frnSC=aQy@02cDu3 zGhQ(=x(OTEieo&n={Ee&&Ym^RZD`0_iuGie&*)nb^+efg@V^q20Y4q~BNh|WZNx}d zk@%FEm?3{B$5dwl6N4nsBiDSI+vt$DFYAf1=a9D^>IsbJ;D06LzRyMTDU5$M3)Yk3 ze<3wdPbUA_{v$yd^zdRoCNnYVqeQpYi%+SG8uqksOs$F-+sXhvy3D89MPb=(;YNFC z#-`at40uc1-H%Mqr=^l1+HGM+zqh10_OM_-KHxL$;s?L0caOi%m`_cj#68xMFcrGX zHsk9ac=TB;gWKE4UFca7b3E(K&vnnGd+q^k^)hR(L;QMbu7rW-xML=}CFG4z07$pu z1yRIXTUX_M|Bk&nf1y{XP|-V)Ps}9| z=bI-8wuZ#z$KqI#bk!y2ZY=QW1aR#jMxHmTIa=H`s`G95udbN0yI1xmfFw6A|hU+re59QK9(nVH0x1AFB2hA{3( zT+JhprxV;(H0Dwz;m+i+5$l}QW!*xL0!Y{ z8@ohLLTYsCdhj-1g?f{FIX8oNhwz=_1G9QZBTudKv45vz5>;K+N`wBy6ASY5GwYqm z2bMRHvW2}n6)dmNr0a51jr{S?!Y5EnAF2KDn4&`!bq%o7N98FVku15|gHs&=k+k9f z42<&kgEpp_$_G)V&pRX>mN;o-DhXH|Dh~^LS`dshZxuxQB+_-M!&OOrItY#hZp$2V zYqHorxnoYIWFhd~f&9LXl%H5`<-HL0w6^4^4eO{iku1a>p&nSR;s?FYcO=H(59JRG zWFDMf2v<_dycxM(z;azOKZTV1ofjrpC!wq-2!zAyo&6BxBeEY<=FX$<&Blvwt%(^Pjv^xbVnLs$sU=!I|ODFn+%n&Nd4Dv z^Ui^Cj8iesz=G8DnlMkS-T#PEC6$5S-@@!R26>Jf7u60SG=nAzE_2qVfm z?Fi&}1BaCO-29~|-PvcCLo3wUt}oqfWZNmS2Uj9C1g=3MX<*(c!6}Uv#19g{YRU>6 znDhl?lfy?L48^yp^9G%`=>2FW`sxd(mL%O$C6!cs{yA(&*M)g@IMseZf6NqqyyGG^ z?K@GoT&3+dpFvS1!;O*2sgP70CBsROL?ak=!yT1V8Xq|CNw*BGfpdSul_kl^w+cXy zhu$46Tex5N@?QJ*vp+Y{5FG>ETnsgy@3%wTb%pe^#|Gwbi$+QIV{qL#Cq&d2%%omo z-e9rzX+^;X!$`Vd%yrSEGPLDXi8Qrjs<_pwpz9rUJ#i}}^X1)Pv7U`riR=C%q0EvV zoUE`m!iuxK9GZ+y?j!nqx1?|BmTb)db-Az+MFLlj#C>B{fqNI3f)g;fhv^8*}W&ZFWNN-@uno zLcj=MJqlE;axelY1II7MyhJK1HhaDcO7dk7H^bF@Ksx`qq*Ce$^-pSAT?oY3O{Qqs-$cTv5FUZW znnkxllo~xsCpL{hyq6#{WQ$(M0Gv8Ai%TRx*ZIpy#{}j_9<^<7KV;Zi_rOkx9l7%M z>CiOttpAzYPn8Do&R8%TcrgRdfH0s9Ra8D6(rTr5%`d0+8c$2bl{CAh&B9L=Da2cU|D^mF*Q_&r- z^8Wo?IHY!PYS|*gVwqR)$m*_nhexnNuLw=lDqRu@m=riNz(2U5Pe_xnc|w)|EJY8Z zzCVQc1k>0|$NZ`;|6{HGJVVxQ3_o#oTuh{F)ZQAj=|YaoBVzv{||M$hBczwIHr~La$gUkZox%OOAcjdM^Hk5!*Qk z+j+=*jtJYir1Jd5*~~rg5l2YdtlTk>HhNi$$HZ&hhzYB)7-FqL&eb~EhCy2&a0<8} zdP^#qbN=gl@;92TCD4dT*N(PCpg8{3sy?Hn_ew+w?u$=|DMNWybsaXo5F?yE+3<>d zOv&0;g-S8*U~(1vjO5vs*8*;=y!{?~VY@jlWObhTSSw?rCRC~m_~M>iYepAzTBYRI zPC(*4hCTWYZ08+%Ths#0omv~-Yu}eUj%ZhJUh#vr$*-THg`Xh#JsXQWj`;PG`Mx#6 z!`JY*N%jWxhBTG5VJ$%3MTn|3O0+$F`Lq#~Y@~a4NTd;4cSxWbq-QiK_NA2xoT!q` z&@?%f;;Jm2e4InNkcMd^-IRQmwgE%WB*;MKp1svPpALPq1wE2oJB!%B`BDO=L}K-Q z#t1j}x+|9v&e5OgOF$RM@D_XfZ;#xL7lMFCenF2|v9nPnTr4otx_vLvE5>!~+QjJl z4C-$vRy^1p356k$Yj4Es6P0pR*M{T7@f7)UlTz%KtvCugE|OW+BO;IOD~)LsQ<=d~ z*mXhEtzd-ZMNFU}EC&di#7(#zTn;z=2TJskg+&DoW_2fbBNki}TklXLhZ9Hgm zCZ_!a&3y-z$T+E{n`oqf6cR@2N~H{sCzMD{HXx-!T`_xSy9OKbnAPicOv-;n<+M>bYe_ooVO0`Vj$#D%Ad=gFvbV?v=eE4SYaqU-nDLn<<=i+2>Dk-`yDY)hvz+EN_quY|Dl{h>>$G zJ>N3#w&3JoJK9>poRMX#9w4tepkHj?|AJWK)3wgM*B3{Fck@Gsh zjnd%1>Z^*|6^pbp%CnC0723-GO6UdM?}}0+=8v{Y0P7vP#!~6@+Hm^X*o!G)Log!v zK}_URp4k2A2XS6$C~-P0_`yx|%eTgqOdq+QnrlCQ`FV+KlwXrXY~XD@r*TTU+h?p&Jet(95MWj%X66uN2Q zgNcB91p)GPP_KhMS7cjt3i%7OEl|&XA2U{4Uk>}s>6TGfp3Vu^(C(d{AYFjK$hZFA z(kp5{WaSEj72=56o`4LJIw6FyJ0dLWdi{tW5_A~j=e#N}V4v*17rK&JBrQeeLQHSB zlDE>~CoUny1jVSHVM4?QZc7uUp7!)*ibOnqHbk`LJ_} zeVOxhRsBe`fi=dP10ZhrpBvFA^qvCr9%MTE$~+3hzJkZ?K8>gyf56c`_^(_gR4uj2H+9&QfLvK5eK7`{18=mPxIN zQo7Q|HNprx0aWfhCHtV9V7a4z?*RpR{q)|s3kY|DBX^8A0SJxv?gEp8L{*5P4dt9s z%ZA)m!|_+F-a*;Xu?C*)ejD@iGFxX60KDDmTJYt?PATrlUq5Nn_4n-`ni)yLHi{95!T0Df}gWzkqsv`Mh8G<1<>)69yYx%v-mQ+fgU%QPye|}n= zK4Z57Z`Z^Ple?j5u9Y1se4ykS2?ANJq#ek8Ovjx`d<|X?LtlV=O?D;E)8Fd9m_KX3 z*guDMBs~xm+gHoCb{$&2l&f0&!kx}p)#vcTG5f&wD(3dvEeq@+J-bNl8szy7%ZA8(LVZ0eM*F%w&vFE@>6tPdjm97%T zA{^lxq**!kr;}q$F|`|%jEgEIL>7@^bis?Eq@o@?L<}E;Movzmr=HT$&uZ)BaJ1J6 zQ687SX&YSOCc<_1jTH|cg*5_-S7wk|^l zkCB6iwBci>$Vp>zj8W@xGF|HcF13tIgP3H;$f>rG319f~JMy$qhuQ#H+oBrIA=9dv z^~uqq`})5grrjQ6Xb#8m@=+ZR?hLL9*1WvV#3PNAw)|sGL$PbtG#+OL)@^GXR;;!o z_u1v>hl^p@SqNGu!GGC0i%K>_-5H9uz^^->iGKz zLGLFeOz#Lzhjzba7dgI%1Ujs6U60}!kle&~!q1fXpD8H18NVipijEo1ViX6lE^B7QkF-%hXPR4$DK+CbG;M>~+@pFL)IMva|F!iic|pe7 zo@FZDv|w&%f!HmWD`{~10ahq&h9~8`EAUlvQ4;AVEuTpS+kf(FFF&LWqU6r)j!=K9 zkn9?$g?0(fgE0adR*3G(z`4wWaU#|JFaVyGmA<_6xaeN&OG-|CMzAL%_-96@PT7G6 z>siw9b$OBY$ikcI8MN?b<&o`Pi;G^2hC-R9lypamS}spQ7elQNQBCiK(6?*t&&k0D zk88ix!AD#h1PMLD`8lanyBk3id@}=r`JMAYH_DbfIMl67LtrBzikl#P4j%a!FDObx zE84lPb2TBH*U$~GOTFff^8vrdaf030%yy`DgEHRVJQkkP#Mb#g_mSB_pk3Wk! z;_>l>`&H%?nK8tO9M~GkKdL)>crK# z!qvH(<9=JW)%tM~ml#Ty7%mTyf4iThQ7pb3Ex#hWg z^ji%Ue(hH&fOj*z<~Jw2V3MpRy`Yk;Cb^JL*pOI)Nq|OtZYbgo^VC%;MSM;vaz%Qs zDVm|U0F~60TKXY%fm75UbG0MC(0;#?yZk0@FYXsNpoc|8!Ln0iBK)S7`+ynoOA=;D zKi~yntB(kWgZ`jl2aXyTG5rnYPAp9koXSppMh5m%RGU{48Zo0s!|he`_G1}&O{6r1 z_y*$qn>i{U(-3zW=+F>VCT|usgNuBQVphT_nsrp7G1|Ey-W2Pclq8#SktxY^u~Dex zH(sM4Xyc#}w3b|+GoRCM2&Vzt2w3{!E}XI{hVWV4DY)21;5$_5kka+v?#Tfd8r>-^mA&MoQk~G0WxCJi!8V<*#=o404b?}(#V5^GM)!0r-ifE*1>n;;InP` z-68Vv5dE~1c1DECRcnGWAtB2le;g~8gUdL39P2bai;Rp*TFP;RH?c9#c`9LnOTf^< zclf`ahLMkh=%=A{Gj1ALw~S2xPMJQ+&R9o}r%$tRHCiS%7Ll4sO3qj}jB6@zn@XN% z5!7@3EFXN<4YyAoYyli@8aQw3Id7T%<9WhHZq_q!G*u}(;~g@tsg&>^Xa4>pTFUIk zBIiv+2zRll%B4s0cLW7QGW~YsOE)NdXdbjfVmzPI2mJj##e*2Y0?|%`XD_pXqO07E zFTmiehU3@BxS;^gG>|A&ocd(KyN{y~<_H1fLTL086p7D>L@vuhFZTIv)Ds+u&!)tW zO%Vasd8yb(at?Jc@)Bt=WZ(LZhJaT%&6SNzP4Gjve9hGeY+}ag#5m;?Btzm_r+&GX z;z&ov@65MWf}ze>5!J(HCE5JacSADtmRjvr`U4&&su@HE9=6TvFMWtqG%4nRotO*_ zzDs>HHvoepS`5P!T@7tDwKG~uvPu-g1YHGn4K2)%64Hl~k^ zbhzOAn-Ot@Ou>Sx3T@Z{aUx@GQWBPkMZI7WTAE0^U{Wfkh-ki~3Z@94TrLS6HC?2* zvdZ2YicRZ^nl?owyfP|GM=%9DUG$fjsbYSzrizJaN_zTn{uDGxrlK`KEh#NMSRL4-RFup#n7HwmvR5(DbuzkY8XYP|I$8uA za0)t30Rvr&h`yGNX$zq$HBAy`Hv=8hk(!p#cy&|Jkad31K!pKiYtzz|vqjlrv8X9b zv0AD0v|MUNHa(n6$JB*$`tmt*<08PcbW$jrd~hrW$I<&C+jh7AZ#+pkYR-^7elWSN z;rxP&2J3Te-9jFey<)i_Opz&8g|<@Oq6wy`DB2-R@g^m8MG~q0rfu}b%}`-ZG}oH?5NR1D0Ya5^s0dQhMcO4*(nN|$Vckfo zR6KD4DcQoA6Dd=J_N}FYB!X(0!i*9rnZlJMF>6XhH2&PNf-9OMi;^qSLJiI*jlstc zz!fYIj!k2Vs0jvuyyhs2JDN$S1s#wjn&QTgaeh?(ik`m%0rf8nc>td>ckGIGMM!OjoCN_1NpG!SNGWAe2anEL}boro;{1e)3Igd2t`3- z!7Z?17YHRmTSrpOy~{@u*vOOz|FW_x48pK>Nd)K@5GB7$8B1c{EHa83n_0BU4z98R z0!9L`YgXch+*?Cf*hpbt&F}KTb1gJ#_F>^}7BnhCeiq)a;mXfB*m6quL9u)C4O2^G zJ_Y{IUgqdWk=x%OKtOV!KtS~W*S*Zr|I^J3P@J?~{Efc!z!)?0LO*YS} zSa{M7+`h0Tc#cwr)L#<_+1 z93|plV&R8+6(xaiDQ>PQBYIGjR7@q|{MTcVqq;vrWxA$m$6q~nU(1mzTt|ad{>Xq! zV|5-S%}$|5;MFW}R&sGx1Px4?g6=S4SQBWRP?*4{e(=Ga@q6!+BTnPAV5*{0MqvfH z%=-pKSzeA57;?qJ3B8=2bpfALR!Zp%L8U8%^Cur>te0&QXKwWGj`|u3+Vg*N%LqIxq(=`lj-OAC5&eXgghB#5FaM1$!zm&a0Th9ok4ZsVtkE&X|H;Mm!e`t*U?3o_e_Uk#zu@Bk zOx00#wsQph&nyikX}iVWh~Fa))`vn4Fu|h4R$#Ks#9861CtuIF`dcQI?2W=ON+3Q;WQ(!$dI0VlM7E5tn%?Kw&U`Pvx#1J6r$xvL@t3Mhs zq{|tlv=gWnA~c^D?O)Fe^4(wfM!!(sn(C{W;I==^wt|!rZ@qe}AZ-*_zoPVRMD_-tp(E%0T0&^ici1gSo_57Ky8QZftNm}d<5 zD!^*B&BhUFm5_wAs=FbR-DR7h^!&9HXM^8_dC`J^chR|IPloThZAOORW99s3A$5k8 z&+PX20(2QsAl=`K-}Uv%ZuWcT<{|dy`@I%O><-6&-`ar;kUFu@V{!7BlIU<-y^_GJPCKT)I*VwUz3)uq(<|_dwdGbWP%noqQyeFP(~Kx`rMIUzFR0Vg|Cxq)4bqE@)U;T zj{%D}B-Gn=aTY4(I{C^<5+Xibr-(TV3<&XI9?q^aE7=E&VUr<-JPI0Qjh0$CPC;Ta z?aW58j$u3q+v41#ZRmDW4V~`5S~NWTG8H1dCXMLk##L|R$c&1|z?oym4TgE8b9RHZ z^4{5ulBd{DA%P^kLy*$9QcYZ=KDxWWeUZqx^>#RT zj(kQ~yXVpuh6@;&XG)|izq*8OXMMs1nsucL<72suzgc{H)e<9d9Y?1LAaypA%xSm) zAM0E#v~PdN#K(snXw#1=iZusjA2a51t-4TJJT}I+8FVRSK$_^yL1-^m-fM+9G5Wz} zqQf%f@r5%R7nM(XnH!LjQjJ`lb&<~=6rwJKIS8fqVjrf#A~!{ewCTg#q9A!im@X$SpzX(4~*TD*Nn3;THRfu3~*GvB^Vj05H|v6!%~r7s^> z&PdKQti!{Qw+K}>r>MIM%C9+dj8T;kvW`e^&v&zlO}l?`5Cl)`Hm9T84DC#UE6G~+ zmyX^I1muZJ=Y;FzLd?_3duwd=af|_R9>QyTGOn+S{(q77j?tC1+q!ltNyWBPv2EM7 zZQH77#R8HO{}NS}q^MQZ@&RN(W7b zkGE&a=W+C=#GX&z?!G#+`E#3hpA7K_F$i%WF-v?KRfYs0uT54#9Ga>!si8^i3fyO~ zs(xb7H=6!tCW~~oog=*b$sSaVPrmTUQCN*{m_g?QGrH{FNi@?LcqD=1r=$`>QV~C; z%oFo>=Y9~*-EW1lyVmV9NA6K(?eAHJ>eAlMDP%nRKwVVgeCIzqs7whbwHt(G%~SN) zA|W`~mM%E+MY%W@tWnJ(YBMsS^aoZ9&IyWj&(_N`p3HH+S<>>f-O$E3MA> zK{=mc-=XUi_X{(LV&9Q9d4CLqwA-4=H|x!^ojdFqw!z8TKH~hi$EU0 zv%Cf*Qh_I8u7&c31$J5mKVN6_OBcr)fm|1#yu;=fFOFqwim;EaCibBTP#~;4%tF5T z8d&hQS~>PH9kENi639$G{_YFW4EizlofzK$Ap)*Ru81CSTt`ew0?3)U#HF5?*X=N; z;Yg|M^o|FWt}15ti5yp^DSkm>a4zTkr$wv)nW4afE6>DTzaJMUK`$4MU)1I3wSl+XlZcqE&uj)INnA-&qfGaCDPimMH z_p{C_j;WrfRyyC$E60mPwL?dY%*X%JJIFlQ8s@!b;?yPdgdFHXx?voG zXaDesjN5j2T5FNlDl5ul_f(QY-PgFA|E(EoStYiyTdTy|mrshcq z4ijFrs)VC18s0YW!kk%t3afo^oXvK`3>!|T56DW%!7&5UjELBkDbLZd!)5pRaTqL0 zUU@Wc|0mK3b`76=SSio;XvPCEa07beKIxS2%a!@&?};q}`_d8in*JY~_Qn-$BM&ZY z*8|wkNGVIhx9yAaGBIAdMVo?S4FI;G{-NWbX>tMHSKkApqDCVHDPuAky-FJkn{}&- zHuTcwYZf=BO@a~7&TO1#nq=@#uq~`AIPM_3a3^MQKm1)StJH)0*o99#Qg)$&LFGR3 zBS3|%39O^)a5z9Z~Ic5P|Dd6Xqt`Z5R!1HI{mkh%mi>RAcR&}@HR?f~6 z@fZK5ozl#DYd{gqrfT7M(ueJ{t z@yBuOKW2)hPty`HlIe*FkwB0FkwS#u2Y|IQ@2|2@A^_OLf07P7OkH?aNl>c6sI z898JH6rL-YZVXF`AL`d2^JUusx#uW`d8qM&z>p*=b0T+~+%CuC*o;?=gF1V?_`9Q( zRLJN>C$sn_9ZWP>`PkB=PkZZLmlK(9U0=_yu)onR8MvMMelsY@Wi$~!Y0L6 zIb^G0D_YSh!y=2`K5wK^tMzS5lLyr4!we|RphwfqTOHLnNhVTL1ID9^x$i=IG_~vw z<3tBVp)>%A9(xvmtkGE~5+ zJ-yi-QnQBxdyNMz9Mq$IEVfULWM2>3ma$wN2k&d@j)<=^x#pNR(}UstC%b5g-91G@ zw3rRUYVgURZN35*w>jUM&q&R6Na%(5tD^ZxjfX;Qeaj^MgOo%1yW5oL(lkf;4kS%# zsh1?)eL!?%w8#*daTQ)$uU+_X#2sX6^-OQcDRdT24vH!ca@Z2C~Hj!Dc# zWc%RzFF~1KY#t8l^rU1nBq$}exzcG_FfIP5)H|}hdILJQUO`jxcD3Kj0W13%eeWKw zt$^>;ilJ0A^ifF3n#1PHSYw=URir(QIe!O`454DP;SYc&-+_SS{u2PL?F@{Gi7jlc zEo@DUiA^2tY$Toj|H+1hiW7g$4*!~HYA~V5$qUF4QMG}tYd{jBCin)bL}5wo4+36q z6H-^hW#n2!-n{wF$L;Y@aHEeE{i!h69Pz`F5Q&ZJ%4&1zee>=0;tm~1WEtk5?cN5r zA<_539dGFov0RG4U1u#rN~J21NARi!n*V(cIYosaQlvpW z(xH{)%6-cQaXap{4c$1(VAU88?IGn6g~sAK-9!q;;G}e>aAr^Vx_0MW*~;im0i6g9 zyZmGJyuq{)VL4|QsL*MP3!wEJ-zFrwaI~uaYB`%I-Bks=$~@VzSbEB`UAa3^6lwz| zj6fyYbP{tfa^;p2Td8x$}E82C~^!beeiwb%K zsI*aBMBEU@Iz~DQxGzxFYx=xF#;Bh@O4;4eW6`2`JWjSf6)PMbvp(yQS?iqBJB1!B zvhOKud8b&IJCc?$adObD_P`Ssufmc__|}*kZ2^zZi|3etM{Gp*HCn1q<2@zpQ}_s! z&Zq7A-lIp3P$(ao>^FVFM3$e~TuZP6pi3!K`odzgl^8oSw_8w#*XD%t3B%wFgtgh8hWxF`OIe83{?HAQ42A)Gbtu#2d)`Lb*cCEE7;zO||eRmmMHTOmx&8L)!A{i@m_a>I(DYF*hCX#rTKBBMjmlF%vHgPNaz{9 z$cUlULPlDnAmK<`e+uoM1sZ1xZKal~Sk;zX`#Y|`2Z)lRK2hf%2Wo`;KX;&i1;{^l z-l)B}AupkRZmKU18?8X>h_D7jR0L3SB%9Q;Gm`vd5D;ap5`l%Y84<_Is1;+n93STY zK_Qbuy>XnJTxDS{wOT~OY~a9A9C9n!Z0F1f!n@;NPJjy=*#CH#L%8v<#WD538eg>$O4bBXU5PdxmEZ|JpyA1CIjr2$D^ zM2{8r9$Yq8UYZyP^K3gHsZmBHhwLunh5~Y9xfj7lvsi??Z@NLbPKN|WY33n)$>~(k z1iOiiC=yQ+$iqUS{r5D&Mb^#U+;Yzg*GWl{B6BhY)8kT&Zc3$Yf6FAh$TV-cqA$#1 zl4S=mxdIPmp>}7TYF{@eP9jGRf18DFDZucVLP&uK8Bh~Jwrh(o45*9%RF--g z8nmX$LkcLRGzx1TdC{=Qz3`6Lkoc*N&_!qIG3w-)8dnw+R7&(^Q(Iv5O=5AV_o0Vd zLJ=Wl_wfRpJBP*Co#R5w5}G9m7|pOmbic(FgnlFvzbWf9KD{{raS?OJ1i}#J4t&RK z%eQj)H9p+}NtWpwxl50ZVFZC4eYT^VN|!~ChU&8w_A#@0J)qY6!CLF>?(aG3DWg!U%@62&xu;1;gdIF;-ODYY1xmwPl0tr`GSop_rYUxtcuhw(6P1QTzWRp6n^ zN+FUrgGF1-#60_!hmV|^7~A78%P*FlJavQ0#~aZ%fic7!n8USY*9Og-slU9xh?1iw zed}0aGRJb~{%&d?uf;d8UEW{dTFM)B>3I8mLnwP^``{rt9lm%B%=6m&C{%m$uoL03 zY2A@3+|iE^SrXR@aaF$H&O{%c*>58}Z}Q3zgm=UyRG$A^7xht0nxwj9Ec_(2iT$j+ z#2pO>NjqjjXsKchxmLW{#Ab~!+_q-y$S~CSSXP3X+;uXs)DgL^3cJaAETOw>6RTe~*Kki$!ZjXP|fUnnMg+#koJ}-G& zM7TE3Dj0*9x1l%0_I$Zi%Eqfe7x=;ll0G?aITMn^(OR&Q4@yDen~mfp$cm2p-f9N! zL8g5u@PQ`e1>a3^hlun{Uo-gVkv6uz3s`jLVQ7Spd!KT`gDR15S zV~z19Xx9;>)0^=VVw(9#x(ELrYJ|KySTKG^?}-jxTu|Kp^NFsSBtXtRC_Em){xJ6~ zrOzg?7f7nMNhnLej{g`93@d;)2jP0U;f5xUW73ERXiDW3| zaK{sFnuv>@$#7)bbjR7lc0cSvbq(q1RVVnJ(LB&8CRjKq54T-(h|BBYus{iG)-`47 zWyYq+UfT}T5D2k-7?-9hjWd#^GThd&5-B;%b&Rib>&)6TmJ~dgEGUM#*f*}p8+ki7 zu3F=*9?BuJ9+r@5p?%rM)`^acQ;8|t{+h_bQFSbQsl8L~wFPP$O*dQAC}gFQFV0gH zociIHQyd9@dj*rX@eGG%egV&nHtkOfhY=;RD&Ui<`YB$|Ca(A+J=`xSdpX2hM*usk zI}nijY-6n>Q1Fc^lu=vEeBu@A6*R?f06IX$zpk|5=|m!((B>IAigS8cDvli+v4`}u z+UDMFC?|_$2rpZNk4_Pbgr0vB^rdjebHFuDG;ER9KB5P%DS!ty^#fw_>^j?+jd8tw zWqY&zd0be}oAIEBhnvp}Jj8Zy*!r5hRw+s+>g)HjiuIXgN6bxoYFjv*SxW$QpY^p< z!wILiPLvx%#;vPI_QmbyAi8OwS*RcWZPaP5Yi|N-E^gkJUo1c^h+hbCBdHh;=TOe~ z(zh26VkS)Jn7F@r?!;}DOcaHSUYc2?9x}Z%2!mO3&mE^QR2Z@x=ayNLAQ~8@*N*Jb zDB7KOHhzFxp$9XIL>Jj&O2B1Tiae>-m>xo)C(J6q)Vq?w75osE?0ZX~Zb<6_!?_z< zV(^!uu8t#$M(k*~qOiyBj@oD3L|fiGi?Q@r&vJr?WTDZE@QSiT+VHyB2w@MmO*HuI z?uZDV9<^hM>@KW`EkUb_E7mSoY`b+q^My@U;GceVP00Nh<^&lN_fK363Fd4!Ep!HI z4htzZvK;hg&X9KTtd}Yg{1r~oYDbw|dqA6%YW;YMzw4WQr1r|yX1w83DC)a75F?&8 zdYX0oHr?t18&&5R5FImrgkJ5eQnv?g`TTsc<;00mqU(6Aip!%)B=U1n z{=~zNWx6sZgOinDX-{(=KS@-i#;%Ht7QeA))b?`g%t}*+1-z{Bi#(6uqYPOiVL(-T z__%Yno`!^?jERqA8*fctDl1A}USLYYUc@~TdjS-MybAXbiu-2q@_gDAg{CBGYZn5! zK5o9eJ#S6z-Q?oAc4j+GIM1icke_4o8l`96cn^l*W%CK5@HxBjcP>WHhb-Pf)ht4D zvcc-Fncm--IjQfpo`Zu;p?#rKs5>kSR`R^ic%i!+Vr%I2cht~epaapU%lJ>P(9)?p zjCR6<^}hb4MdSOqm)-Ua2&fMD-`NOg;%;PO|JST@CprmRR|9JcV{sb;Ba#1l`%jl3 zFC(|ckKoe|CwmG&5>yi8+t7Vx*AJaX{>UpqjgyA|={SVk z$)g-<UKCZWw-V1{yK5TB(czc0@`RwI9?oES}4(ih6sE3 zPCQ#P$pZQQL713wQ%2bPp@=e4Z{Dvw)E0KR)uqBX<<8y;#YG$Y zl8Zu8T+l}dGa|H7zSzxh4Tp2bU#UnWKSJB6P=b$=m)TlnX(&v9Wo$DqIx2WEo1|P5 z)E*Tug+Da9?orwL~RNDyHlWuE(D1cSDc0%hz3EA@Aq8MrZ9@6Rt6Tt8iyF`9ba&ohkkCbaNGdY{R} zFsMib_afYKm)RIqSMAC{@J0m0hRWLUFRz1Q9ukyVAqMHc((b}f&;2=*~X!`zt= zR~x`#D*CqJf={v)T1D&k@_^c5piKZB9waP%bK76zO+ETsDU#{n^KCO0kC>Ko&=0a# zFRq>K^%^w5IYdu^~h9u%fkL1-Chnec!sCS$1NHe{21_4~r)0$xQ*R&Fh%Mf3KTc>?#7 zI*NLE5kwM0Tclgaxqb2m-oBUAk!JE$GU7i*sQBgk=&UGHYL>3AxM;CzJ5BX1>Rn|L zojq^Xno(R!!<cj6Bz$s+h6!cobvxrDEc1|DD%g?8JL;;cYXQ?d-bwe z`ik*IptH9#$x3TN8=||)L6V5#aoTVw2k{`Q?B&j3omCwfziYdLk=10AJo#1rTF|m= z@m<`$x9(^n!=pXhW6Mw58)SL_Z0E=1?DT=s8Sf~u0&S*HCT6}NdYN1;rwb<&>v>w5 z^!>582G!8%f=oUXA+=vD6DH|wWZxDRUXNcXXWUEMU`CquMIW;IUj49^Gcobw}vl3Nd&WE)G%0Z-a1`-OO zoBpKQSQm)d+Sv;rGl+N9q?{>cI<-+OAQgFw3Q6Sq2oX8@WfdvqIF2Rop;mY7K()CiFXROl_k2eQ9|ur70& zUG)cq>i-Y~c>|BXDqH^-5ZYz4<_nl*u*J1_p^B41isC#e1q)T;2r+}mp+a!VqB<$1 zSlfr&AulTbL4r7r{K8+$x?B~%A!XK_d)%334>Dc7#Z+$rIorn=&^6{S9~m9-K~xsT z6vt{B8J`+?q>n!7YV7S+r9o8IrR+*;#o_9*lk#%kMbQw3`75t7UG|;UQZNT@kHQhE?)z|XV&h*>^I4jYFVP2#+10JmLrzQ-u zN_v<~@MV}bs<&!n^f|lS0h3DXf|}9Zg7{;i(!7sl>F^Pf_9?>p&<1F?GKidAXcP5Om?8E#^|KK#N%CS%KC$(Q%$amz23~?E$Uo zbRG)Zt3Djt%w_&=&34Tx0%yes=--Lh>M6`A_H|n7u{_vv#eAjYIv<1Pg@V~{^UI=f<+Z}HEm!OgJO4;S z+F8z)ACUPTi2TGGf%%GHZY@ntw_I}+Y4~B>) zx33HcV;0-N{~oh_Ws$!@70}$YNi=Uut5I98MM;jFZZ&hYr;}pg)X=_JZV2}ToQQxR zd8w||D{$XEafyVMr*Mj7)9W@mlItAL!jSFRfiRjo=`1h5|C?z#v~Y*j;Ni9dD>OOP z2X&^6+b)8JGqwG7H=?cO_0+4ieu7BEjXKD0{=IxhE=@AAA-v{2#+OYjy4WuPZbd1g zLE(yQ&wrG6RG?RqNYW_n-nhugmk4abIey@@a&lxgB+S9H!gJNKs*9D>rEVFaiNL+} zr4RYG%4e{6+*O?{`XEa50Qjlff$}<%VH<+=@bD;8#f&xjF8D~qU#_OtA;$`SS?#Rv z?3Zo_wE247032T42nF7Q09wq1zSNZqN*b$V|Xz$@uQf)^(n zgsuFVl?2kJ)x&IkSDn&W!86J?C{69@_#waL?!z^bSc`ql-H{sbVinQOkiM5>;)s)_ zA?Jq;apkVhV?08Ku3wWg8(Y>0xO+Hg!wM@&>xnSaMX&9mc4oA`pG2g$aNl%luN*q$ zaXt11V|_8du_*m@M4(+AyZ9WtpJqA#erXlQez4#^EkCb6CmWy7XHJ$-GvypoGpQJrD-1(#k0hU_< zJMZb$eviyQRUY(hn&zfEH)>e!-dP7(oOsxFk*B)kK3y`;4Gb~24;{c8-bGC3Nu)NV zNv8IWIOzU62&k1-P^y12RU9xNAd&wG2!Abdv~V;r7PT<3{%T}ucvs@10)nRfT}!!h;rHBSs0Uhr%k@r^S+7{hkOC1RpG4Y-imgXGfPOjV$ATCIHV|wMESCO~g z4E4syl{b^0o5+l2G*bx8dAK{L0vXnse_W7Tg7;dLli%|e?@8fNISkVE#rhZ;Vva`f z`2Ozv8De9nHNzq*!- zUYQ3Qs)(-=OpLP&q7W_fS9ZXSJ)EU{M4DcyXg>tQgQpbD+WHx@f|cp?Q};EvBS1C^buDlbC>?U8=M2-h;SPQERm&C{AA8|%PN9NM^!dN_#wWJ)yqt>o4 zR~k93rLw}6;`A~(?l!sd@lma(3q-!d+t*;*d^da2aw}z< zE_m3UV5^lpZKBJa*y{+p=6DwoQ=o2-^J%U^sSk65+ckAZ+T3)@hRtPM@ne|2rq0=~ zzN5~*co3l^1+#Zn7ZDya+Vm`9Z=so&Hy);*7No{AX`p?^Boc}++l!uODRNu#(^uKH~74*kr=k@@R)5~?)HVZpjJKCa%wvA>K5kx35hn6yp^r1|#1@?mLB9|xoh4HQN z)zGli3lXsQB^nn1?7&o=!7~_A-YKb$9M9IeuT10r=o?F!=>cLwJAK`J4OXVmbVdVA zY&T1Bq{k~!LYGtG?_S0QJF_PtTXOR|jGrJC8b#0yuuBYqvizZ@7oy7(1uFh^+AhA_ zN6ru)wC_K-;(UC$j_Q0RSnF)?n&)b9$Y^&gGZVi1>VJ5mE8D|JBCvX3bNMXH7*x0S z;0QxiQqtl-g;A#w)648Om;ID@#Ix{#m*Y7!H|CQR>XVRAsC8NktbdE+CM2>zUB6X3i@- z+o^4M%>nQm7!J&Fs7Tj>*3HUs`<-VZ1uMy0`;2a8vbktyqY z-mAS$Vog=v3&FRl4a1LaMe$VGsXp zl^K+$uh|0;HSSfy{n^iGo6v&-cCwA=St=a&OAiuc`qwVjh}{nWm$N^e_*y{~TId{} zd5_eWLZ(nMbrs?#dnMS&6^>(rVqEbSC@EyU@SaCJ?6hvad4jMUPEJA>zcGnC5mLSL zG6D1OBd~&mGoqmXAr=o}Jj}Fr3SRI7tAe(0{9->t4;Ul_h6Hsb*pbsz&Fha^QJnXn zd3aw-+b4N$$pDlUsaZ}|ahIm1CVMHLX7UAR`i20I1x&_1v{lPE2L2ExYwI1#&WqZ_|#UAaluwYI?+Fn{T9r91{ zuiLG^Yhj~dQ_Rkv|3nZ#Ky3dLEmU%Lw6Hb%FCk1)*-%1OL;2e9wy6V+q?id8ES*gU zm4u+oQ>PY2_&dWSAb8?C!*QctLwH;uumHlPYpx2^3(J`i+v8v-*l8=_*IT+NV4!D-9VOlrJx zfaWo1vlgfQsyoT)*rZz?S6Ri|YU$xqJ%=ljnQOKY$1*lqX=A%jtp&Ao*qCWRRbiHv z4S)me9hT8A9kqf#bbE5RPMVG^YQ0mPDx1tNCwEFg{o z4-_ehH@U+P{Oa_$PXidQcf3`$@gx%fBQ%__%iFhbZ{R{RUAvTKr4y5n4>Ee}1v66? ztCV~;!tQvgt^vbfPE74cf^9oI$zn{xh(8nzuqKYCCu9JnmlQr9Dy$jZh=0zbwN^53 zkv>l&PK(|5T^&Z*Z3%cn_1S|dEy{?OEX9<|>4e9KR(BP#-q1YB7uRVk0F z4R9D@&vc+vT#}z=@IOO=9ZC#npbxAYMON-Z71Ge3R-3=cKsJr*?oY5|_yt8NE|Qlk zF0I2bq9zsnW>GV2?;LZ0N#GGIB^!*oybLq@(4(C2%VH%*ki{9i#)SnUwjry=SCs?zdx68)>d>l|^_cH~O;#&x&hQR6XDCZ?q zmK`<83W39I&aAvn{zkf1nveG)a+FLcmkW}Si3dzyKmQafW!E)E*G8O=!5Y?FiwE_Wg>WE}kE!LY*p z5!`!}`Zadkst?30$j3Qz@Dm;5J}wMx%~erfZ|oy6!L=54q)XXg#T~QIkI>+wQ16%( zJqHVPqmPZX7AzQ2LTm5@qBtIdW8HFfDUpWC#7B2x&S_lF#Bkqw{?#raEjLPUyZ4u| zEdQL3eR3r)q@I+Puuwqa*CgX>5-0}rdlb9@(hsSl@aZ`TJt!>CW9xcirWt^k`4F=6 zwpF}OM1Bc^Z#unPXadFhuSZ0QVv7PqUf4iU#8n=dHoI65p67UL^ssJ}Y{S8$M0cS~ z(VxHMBvwoGa$|g@q9jn7>BWo)Gn7kcK&3_f`U>^luTv+YP9p`BKYnZsP(IjNm>SCZ zB^AEPbjROYm%7<87d|~5dOab2z3Ek$MRL=HK)dmSF0R{N*42 zMZ3M%xsvBEXTSCv>5@9oLw&G#48rU5V_az5j_+T4So6f5ZaROg-xv75Qv?Qu3qWoXgygScQ1lNKE$mmtYt?~KBdiy)H2FSzp% zTU|{8(rP`Tlp$}p+<)>m$^G$zdEB@82xd!b>y{$nBvzB&W|JISX-nW=TilPAQ zQPK9ZIE}0C-!xWor_=2sfT3WP+TtL+Ph4Mf&!mVF_x;ym9k5oP8el^VX4epPpS-Hv z$%pfD=hR`?VvcxQc5Y+WtkD$uzk~0QGt5u zT)ijk?SdHbTw*0vd{RPp0_fhjuh%35LJJdjVQ-}5%=@;_9ScQPH<1>s$t;yd7W1;L znT#z1wW~RYcd_~uD>*8_;rpjL&{I`Tadmz(#d(gasFAmof>613upi;#^1>!40fO&D zj7mCO+^J#gocbipfA7m(TG;r+;XZ2Nc!on zMTmgP1jZ``~7nQ@}LxEj+YLqLpiyllcxV%5v>Byp|VkH-9#ImpaE^s6|euY*2)sGME|<)&O#H=#x0 zBtDEA9&rknG;IjHL*OLSK8&Rd&M!f~p}A+yn=hC#Vn5;!oE6`OU}+G1vhIwMPLXD; zmMD+a-2@u?D7o62;Jm>CK)Hj8k*1MCe(Z0|C@QRaEDo%Mf4wt3g%8f@`5g$z4jKrE z{XYucf0k-gw3JZP5I!f*ZCurjUDXmJ+#xYizH19RDKSIj(P~Wp>_Lb>rjI=TeLikF zNnP4e;C;&>r}~mCsY2p`JvjnH2IK^&XST%d|IYVnkVB`Q)7A)YjdVd6~`k z`FfxRB&2SDF`FgX<7Bn|rdUtiM+GYEX1zAu*~{o=-CTxF8Y-*y;IQPf4vG=jR#9pB zU8+6>Y6&e_g4rKJNZAby7EIMxoEmd8wL4OD0 zHbt=MIWn9X#%^DcjVC$=+sNpU>@q3SijRaDLz+!d?kw@&PI&`SXby%dH{( zfWbRq4O$;$D7>AiQ%o;rPiDsozi`tD*x7S03rx(asL@=0DN|V<;VN+jDghbSNf)e; zwMwNN&^b&WYn3?Z_nU-Ib}#a$Z^Vv#)w5@hThMmxE{2aE;aFSEjX4x`+oN<|*XWnr#h` zu<*tyXY`*e96b^C7xza^xs^`z+il{;n^4H#x0;jxCapW=-PHmZt6YRO)e5*iy!d>J zgWJAj+GX;;_hHR~qaQB0ZX%*2Q%-}^^F+cURXJFRx%nVew$z~S$Emh%xz}C`e^0hj z2Mk)^!F}a;!~yXJFn6Hb-(qxf^_*qz$y#1QshH9a@v6|rOewMh*!s6S2d3*5 z21iiSfwSgV)|jY7XLMOwC0v|tRA^?np?KM8Nak3JER(+XbrM)gc~j!()SI#_p}bw! zn31Xa(A$C7WZy9Rm?)M5EK3OwdT{HRCt=%EO*7NWu~TFmmhc28FCF2E$qP#KyfANQ zh-ry<8Se3*`-j4s5NyzAkZlC9AcvjH=BoG;v*Bvrr{oty<7zM{{rO=vmHdGyib9KW zXZ2PKGkscwI2QRm%K4;W_7ThhCC8bIpX2^y?1@{y8QtGd8lS3#_5MV*<-3)eJ+3+`@-nkk@AJN& zznke~!jD2}I{ZNc#`8j$C`PgINe}6GjWVlk_66oxZ66T3;5lr`W6T2MLG}1}y+G_4 z2M@v}!_SbXqcc;D&yThP>oy?B%9VC?t|#WF?>0gQQYt~<`wQlee@IK5iqY5u~T-kOUKvrliS-_%>12q@H(RT1+V~*BHE}^H&JW!3_e0TW^1Ra z+W|rW`u%Te0XXpnT~7iv9+3V#tGLEWfuppX{ve>P-6yp))wnPZU_Mb?lh?vhgEaEuJETGvN4z+~qW3Kp zZNJvHnox^zgb3_AeH?rPSG-Cbis%d+20xoeHBM>!VH`@a^!$wckUjRH7QZ)!A&4QM zA%^JI?tjs92^zg6R4^bQUATYe8<_u-o-3L-+5hph|8>_|^`9kpA1!PUD{OFSTKQkF z7y)5UR!&ypf>`bRisIrF76}8JzXMiWT$hwR%uyRH=keu5B=M~s|w!VO3gD31e^l43S$_=@4$_wKUjkOi% zVTGvJ)4s1US2z+RkZ~x`Mye-U$e6pXFk91r4*sGp8M7=Zz;a5Hs)ueM0mT;@UK>Kd z*U_UtQEy{#3&{1FZ#ZOxPG_C1_k+iZjBP z6lTf}&IVV94IWYg2W|QS*T-f@O;oKmt#BzTHxre#le4+x?`8C|tcX{QYJ!3Zb3={z zQD@XvrQpBl>D82%5}uCQI|C7Y1GT0FU$s>4B01>g+&}NA!Bm2XQ<5-UNi)JEK%4bj z!u9~AG16bQQ{4QD>HK7(+jEM3%PF}R;}|6h(3DkOt7CGm!j8^92|hpiYP_Gv9zdV- z9Q*S~#p^RuW4_-bU(E>P=;&g$Nc9pJaE&PR_(etP)PxL zKy+#9hgMgmu?Z5gSyL-pR`8Cr*i;*qw7xixv53}G#!X^-DKx>~@QdU_!I}&wcn-Xz zZ4y@+2#j>8+{BF(w^0;URJ{^CCZmw4f`IPX8dkt!jomLdrad@%+HYED*RTl9x(FSs zD-ge!jitj=e*edputHOQ5VHVpb&Eqgx~`TZBAp#7Zk|CkNS|&bvaBEF`hv*zg3z8) z8oas0;&vmWl64zfgMj4$^e2vG+H@O|thmiXtd1#CLlre+sFDr3kR%8hsO^UlFcQgx z-pg0x{Mq~WBmHnD5m*G}{UWC7nabpKWjr?O%7Ri>RIPQY*hVGR7V!=9C*cA6dlDKm z>a!-<7d+kSZ>I&eX{<4)09IX%^o0tl4$^0~U=4XGrLw}iTDBFX2$jw>R)1DmPD=v^ zX&SFJ!UWR+N^^8)UU3f$C9RGg0ZTex1!9NK839DP+0ZeB5%w=Zgm~JcbhoHb0kG~` zDdy2>YD574D;qO6p-69hGe&4Hf?VxSW1^8Lc?R-(GrU;k?~?C|AoJ?r$~}>? z#fT!C4*hq<`=4l`u|OkLpU>IF$C5958^p76*RpM23|O;SF>&I>?=f+&E(uAZ%n(!- z0+uOe!B*d? zwf~(-d`@@x>?iF=Ddj&OikSVV!&4-@qWstgLcALYd}qiM4y*hT{@-)ReIxv#_w+GW zEqeQg-$QhUMhQjr8vH}N3ex?VxCOXVrtGa>AvdG&^?EF#r3ifo7m%sg(eWJ~=Amc@ z?+U8*@8j&9oJwaO4mr4v2E0S_Wxj&1p=iH?ezWAvGE50nQ)TISe_Y78C9bw?ANG|E z>0HNz&@+td&OY=C?l%ZeQPFvjZXn8?s7QP4NFx=#>vAMrejyIMeg)kFt$eD88?8rt zd*vG#2XVFsH&1-N^x~v5&c07{N51B}ugrK7%CketPC;mPl2LxJxzYY9a!ZxoDcj-8 z38Dv=6Sf7ru8m#FF`may8P257%DkQ~&>kgXW_I_ zbwdtk3H57ZrFzArr@qhd&H(#3-qg*eBef+u5Z5q+ofZ2Bt-N+d!$^&^zJ3~klbOUE zD}xM4IlNDWVrxuN9&oyqV#|}iLS?${h#qCMO$KUp!yxY3Mn&Ri!=$aM>BN&2=@XJZ z%nS6Lk&@M5vO*cwqvT0tA&VkAUzcA0FP1T+P)71aMi45depP9^_+#-dd0C=pVI(L; z7zMk9T^O_CY*JCj3X2j1p^Q?M za%W1Bo}{O9|>@$DYnwmQr{h!o#ok4B5IXc zPSoRf)8H~Qi|n(XYr@%L>z=yCB;Nb^vzjLM!D&Qak z{P+QM>P-hxOzXobPbp`RM4MN2HNWjIFx87nprI)_F`}!vbQkKRthBI6#vY}YSj!3} zwA2q&gUMpe*cCNcsLtxpNV8J+1tRD>Qn&E;KDVKuEl?Z?gMnp7RmprIiWNh1`|Xcf zl`S31R1B*#t}q+R*aV_P2C4oe!`PQ?C!@WhqH<9s7^tyXriowGoi@9;^$!7QF z$1vb_lpz&u~LREtH_0bhZApt1RnWMJ&=yRxM6MEb z2r&>z23f*m#hC4#?L6^{ZAtT&u97py1WN_o&cng1s48SlQL1??Dix=FT$WFlt_)?D zwpP~4ma#Yh26_UHxT}bz#2BR|DMO8yx+*cJg4}iOJe1xN)%m$gGbjrFdK5C3mx5@rJ;Km5;4dVp_flauZa_;-rxo0iXxu*mKn*Ux%Ut1%T+Gp zu9vMo)Vu{2#Bfe>3g5USGH32X{KDWNFwD#9Jw(+B4&BR(qy%!_Vh zcp7EdTs&9`^j655nh^=xH|ZT^KpZO%@0x*23QR?_ zy=}KP=8CYYn_1}`|1#{cA8%XCSGxJ&+_03s1=PY=+zI|ko9VKGWRWgJ{V2WGws1Q1 zsuf{No2d(&;w`lc$uQk&up#LdFBy>8sq7-fb^%$jXzC{xXD!ui0=^u0`cGFK-uL!k)B?i&59c>X4)w~G|`soRtN(jS-%OibSITtq`E}Pq<;L%uSY%x>g&%+T^#9Mtj)rh z<#XzcEEPw#CKK=`M7biHrtHV-WgJqHP(avceVaiiOS|nBFWL5*;9@>Xhy+|3Zn9Co zJP(Nzc&hH8rG(QO@hFl|Cva37Y;&ufL-_G;r)Z6cX%sHHW0hLU%Hq>I* z;KI%g&7rFtWn|O!c5SEovQ%Ad9oft@v-Pw*B@6n-_-r1+F)4Uomj_9QyDEnZImoLC zr+govSfp%u>NPKwDfjmfmCNyQMm_f3XfYM=MB|Ll27Oks71Cbgt%bLVu#yvZu6l{L^T2YB|UfeWS^9%A`_eCjzBs%)QVW$T$z>YIrY=12` zpBO&z5*s)k+8$BPQ-_IH&brBWBJGT?*aU0?Y5yYCaUAsWUvDLU=#UQd7iQ*hJ+P^Y zxhZ@qD&cANe7S3Xu@m6=d;e@WEg-BD^*plQD^_FA->Y$AmbPl_9?qd?d4RW%yzI7T zgw2+78|mijiT-}cn9jq5^2AtB1M(hxouvpd%<@P^i&=-v^}w4iWR$_NwUiRMhbuV> zz}@ti_4#h%Cj*b|8SRtj&^u-$qHo3hS{I!b*Cm*Y^}Fh{%~?gb_vJLb!K@<=;QZ*w zJi;}1+pu@q+*KUC=?0(>WZ@vMS*FXiT|<{p1!GXHuL)Zwoigq*ge_Bx@ zVV#jw*YNB6d=L2k*s;EU-dc6+fQII2YtLD`+D@HS6M3}rwBiP{xuWl;1?Py(y4FCV zSI@aPz73GrULxgcG(5Y%2p`fztL03dlXko-@|uu5AVmL3n_?ebBfd<{(3QuDWxKp? z{o9gO#k4Rk&9kz#o-kY&BGPTV(OFWSOxx)gAv?=tv$Dqm&s*$dSxxsR;9RFfuEf2? zn&&tvu~AdrL6r~Qp$+=!J%h*UOmTtI_2<7*S6NbrJ#{I|Q z#OrS|6a_$|>F+}t-}kLWT~nzyrizuTuze?6e&F^7ihl>0Ohk8U@c^bO7|IW`C zhw7AJ?r*02=Nd&+_eHs>x>zS5j*tm-ycRb)O%!Gcn+lh6%B7%({<639lp%Fr*qf#b zXS~ustJ=%fH@h8S>R_LQj52gDVw&VBL+VXVS}hLf~AXjIYCrD1T`_~zQ?W_K@m(M0Humxpt&FQHUZ*TQ9)2si}_~a9Kjb^PD zNY(_rGeXXRuef>ueFSdYUA-VV_%^rI;yDP>Nc0fhOJk#jTq1A?!m0g0jUD7F3f+-k zc0o0IJBvZ7-5=!1y?ndqxBG@}&>4!6gmikLu>Z2k4vMqo5$HZ+H{*gcGenHxSvJV+ z%qA4e8K;YwY!*az80UVyKG7IYHdp8@&RvABfIo}h&7+Fn>xSl1!t>5ex&1pDe@(wM zpX`h1e=1gE-LNPe7zjuM#DB5{!rsx`!p@cHU&BQIy#*py?Z0+YdgPA8p~*rk>+ZyZ z%D{0Wq=3?tBI#DDdPNAARE66l(+}2~71yYNm*>5bjl_?Y4NW zdKXC)r7NgDC(5pS$kk|K%wS>h`i^7Io!r82qcXAPvMgDRlvM`2A5#psSj61sT<`%~FO_T{fsEC9|(Ru;RkKjv~cBDBnv=1Aa zd;wS7;Eq!h*GqZcF$2C4%;oMPB%A34Sqc=Y1E>zKVkIPU2X$s|c!2&#E5s!i6u*?& z#i^lF+gIUzP~_owiR&e3TbLsTHCQ)ZK~pk>G2TcWhF>Pnp= zNEUD~>6&7|K_~(tH@&8F+V~2zMf6Miup$&G0`@+h=G@e$j$dh z1+%7oPJa}Sjg~e#k#d9dhdQcqNg7xE{6HIJEs54$at4WJ39?G#B}tp|ShwSbYb@5W zi54h-^E`6<9QNO_BmKcljJQV9mg6z;9bK=4YqZgg6HWrFY$>!i&X@M+15X#kl_JNd zs*kN1!(>uI`vaTSADENi7T+}ZCGO4VA)o-nx=#nnEsmxjA?PdwqfaD}!`w|eMuCUl z)^D6H!4y56KNU)-D!qXLAGZ_JbN72n+`AArZ`j!pkd{T>q@)&key-|j0Wf2rz~Vq~ ztN;KOmJqsbE1s*#Fbxr6nd} zX6ESb;P&4g7-;`o=f1b?aGTqsLBj+IFYa?wA_f_fw8(VuxPwx#vzIW~T`rj?KRI++ z5iF-iQ7s#NAJNmOv?00|E=5&NtU@mle3W`DPnr9<&CQh_CHtJoGtRa#8SiO@Xifdeo4x`+`7ls+qe^Q$xD$Rrm zy?%}YqsV%_?Eok5EJ5(dOU-?n7w?d>>!0h~ek__W z&CBFTOYHp<`OU{UHvEayXWi%d3Tq4Qrc19h09LxDt;EuEhb^9Fqq!WOiKaLx6b_;$ zrnkjg)*dd0A|?fj<%koh){enq^Q>s86Lc;v?~kLkl;@9yT9PO-@hF?l{q?P9Mf+WN zwN|>hsWo`4?6R*eh6IvLjw*Q~F?Ut%71!G|`kyYlUfr+qT{vy)^$pLMhJ&)8q^Ew@vFNpMe>>(N$1qJRL zy55c37=XVb#Am`C`l?wjVHU~7y^Qc%X)`rsaP*$CFO@qeLJ=7Ns4n-Rp4=b zmkZ0W%I5T+(@4&DtEhxAuX*+}3c5c{=1hYcF^Kma*xV8fqEKr3-jwx(Dp;-hodxKx5nV3*hjSQ3-%3mm#c%b7u6bJiWIIk=kX4$7lQ z^v+Ln&+$Dx>29tki{&*cb0gm%i7X&8NSSL6B<_qu3J|}vJe<=dOC44=RK3s+Mpd>Z z@TZX@yfnWWXC^H3BEe4^{2V}mwlc1N@3~j%%lPVkbW22%x`Z4Lr3<1WFF?l_r7?} zy>0tm|Gu~}3ItP~hOO)#+_(k%iljV2+TOUBXWJTwUXtXQ?@g$B8?(x6(0K>7|fj zqt{xYpMGYKyQpez|)$igsM?Tr?k$Qvw_%pfm{XcZO?4)&%4PCL=|Z;Ddqbf_-E ziYx;bldO?`SwId}FpHmw)g^_ZcDyi;DB`8<DjGlBz4#t`Z4fhR28ESXe@#xBw=XZ}JF@ks(CpD(Jf z2rNxih~Ik|+#bHJi_<5&`l@dE27kXOiA_F_X!@hg5y`jEE<<8F(u_Tbr4R6tU4U@E z!$%gxNmo`|f1#OZQZnhm;h_PobmsRR$vZT@f~={xhXdL%xQlqxR$aOjXd;F>M_yPI zro=DhU2lCog}!o1RI87g+%XWps=iHt0v>gMN>LtYuqtn}<(ACG?-pKo)6WRY`+Fm) zQjId{_%xk+Ic{5dRB@kHaBn-oO8RIRGG{>`R!u5Fv=X)OBJnP07euZPM)1ec*y@A^ zzM1?_KPPe$Hy9X^9lwNveByR0a2lDWRPq-&ec5_KS%Yk(u`m^nLaE3}(ROBB5*i@u z)KT?~SulS025GyC?fcU(^w}hCZ*3LQ#8nAe0XfXKjQU6o23$o8Exsef#Up1KMfYd} zyA*}~fRRD;dyk|!>0Pv$fDwlhPAA8lqDH$QlPwDBm>)y!I7>@|(#7nFN5%v;lg%go1VymEs9aoC zE3GY;)l-tzv!7^Jp>8+E)>-ssR0Ag@%T$QoZ;vN&rhEXf;GS1zwqPSYJ=y_PHwW&B zTPgY%rJGt);ajI>yp#Cb(U@4;Zt)MMA`x;9E67vSg7Ws?&xDm+f72u2aepn>*Z+ZD zR6{R&Cm-8o0KrESbwoBi)AL(1ZLdnB_ecNMEgm)Cp*we8*`vPT)cg~=a)|_hR z^|moD^{1m1jI=AI6fMw-xiQF*ntIDsza5`fA=e?fp=SxSM@hu5`{-A4J}|On4A1V6 zpgqPK)ZZGjieA4cnjH;}t`KVLR@T3CCF!(wUf3&IlE{xXV|+J&@95Z9>OL@C)KbTa z{`$9t7GTL!on3!pRTvNuw*Q>u3KnkGj{m}Us=9&eraH1f5`kXYk)%vk8331>BCNzn z7VLV)cTs@|_krBCEMuM#&PD>HMZ;QbKNuYTql@@@sn zeGB7B!db?loMQbNnjBgm< zR^YGJr3CWBX}(JmHvD6cn4-;_x|Dbfv$wIPP_%HdlEf5d8%%{}^;+%L{q5NzopfV- zwx-bOLaFfwUPLb5AyXPRM#G!Sy@1nET(r;?49nsjgX-{-FG z2up`^f@WWTELCxFMqyqhP>q|!ytn)I^p5#a?j6bv>-L!*!?{n%42{4o5xPRyLJ)DY zGit8x$8>HAn%C55YKBj8Z66aiR)Prpvd5@OeNw$blz@r(bfmBPX5Xt*Eg!mP=E6tY~#WHFxikV<*KEr8+Wzw2LUGXEsO!?th3fH6H;Vo|G5#0 zq?I+o-R93gM^?bR2j6sQ7z0ARnc^rkqqOuplHHWMP23W-E@Le4=;?H~tk?7I>xjM|iS?O8PNYTy`&T%}EdpM{9{&No>P~KOJ~=_&F5BUo}f`PqAsMt=bsT zUSYhKxAK7s%R4%PyJZ{_w>H*L`@4JYVve~If~Yk zi1PF~F<64C6{iY14SdBRy?_(L+4rllY^vNsW7S+HbhojI}4EXgxB)xAuf^B>7_!`lLu)k9^_qj0aN%z;aPI;omr8OkTc3 z(L!N9!0S{`!}W>6R9;4-PmG%zu{b-_kO&yBy(L7v@rh@!DS$U#B&z1nS9k}@vLNjq z<^5ySioG-_M2)?fIx&MXdIp*_j5&l0DabfDB{(HS8F(3lCAcNT>es)G{f=wV%RWPb zfLNk{fN=chC{(tvaQTZvlmFAN;kvwv@nukZxKg*)rum1nwi$-Jam{1Z;}psm+E`Ru z8_zU^MmcN3)?_-3f((Gr5_Ye;^PYuc$hQO_@{+bP9cEvOle4@}2&7??nd?n@nWlvV z&Rw4SZMg=XY`M<;e!UJ4{0%igPRNQrb?rlPOB>)c-eAv09a?h98>fQ^dvTov_z0_B zN|#^CW&tCE87rv8&6XVG%sCnw9o1-(BON5>8|#`ZNVh3|2(sdt9>+#Z(|7D_uz`%u zGvNssbi(Ii2A!q|SVgd;Yg5piv|!7?AT|&i_BCNu{*e?M%FY9)NuO_0^cZ!QoGw1v zjH5fThA}@^ATY9wt(z|2f2o7VPY)Dhg3a9Lz_d^Pu>o@;Z*K_c6R|d1pp6a*Q5hyz z37%#^trOrB6T($h_FVT2o>>WQ2ru%u9kV>#(=LSrmmj94K!fjuuDw+KNFQd*t-Hgrjt-C>ZJ&G^ z)msi^d0SsH!ubU4mp@<*7)48vQv)L~kzR`1bRt-Sp*#0DcX3VaJ}JGC82-lO4ZL0`?@+ZC&ixhs2TE<-WoNP!m|lG!1$I-P;t`hjsZZh2Eq zm)2|UBjcAe#?zPGf;)lSFBn(Ntz>UwXO}UDI;<_f@N5W=LMx#%1P_%*5wU=1?Q-{r&d$a9KqEE#?%qDcq`9Wy9B7>9BF^a@OE8_8zu}5Rb zDt;kzkqy)Jh4b~Jm5KotD!v)f zhI({BTYml^WwSdsECa~N&HJIp4iLswLQaKHs7@+_pYV@)_1M>gR5w@!nDf7V)-!-R z3zoV&O}p;$%OclnylFDv&7rA=m#taC&Z5*N+f6pgHVjq%3f-LKZBJHk0I>4yR=Ot| zyp&WuLDP9$XESwt7%o1e%tzcHiM<8Sx?h{BOwg8^3u~(B`qZ@pI2M$v(=w-lC@U%U z=J^OA)ij%}j}L(@@V6sNJXIZoQD-W0>~`Jr{7ak&`R`S=Z2&>-;^u<^{ZW`2_vTYO z-9Cx=B(>IZSTUO&U5}W8{RMd*yP3G8qm1U%CyAx*nS7Sdh2t~?-@#jYMyV;W*^ZU$ z)Ahh5dtlU^-m{Z^I{qG7yW4?l2Tz7`pxF$Nlj!-pGLkj+E@g8K@W_oj|`(Iyj-lM8XPeN3$}xtWRU z&Fk_o?>A+*f^*RZI#hNxse_9?nfaT&qW2zgyY%!$dNd%kp2T2t)(SzW#&fg|~2p})j+K;~yP^FjXedS#$yB&pk_cODC%8Ss`mvr=@X%ltx zm0N$vALV-vz}}wh%&rxkqR0=0s(-B0R!L=<$?kyEQ%wAe{jfPFAL)&7&Ismh(Ed}h zU-6SO;Gx+_6gAe|$kMzqAatEItw=mm9L*GlEO3~L>?`aMyIjhH zIFciF@e4nXE*y4?GDntYkt7Rq5P@%s-wKZ8p(dMNHa@xG&qy z>02OWtpq}&4_GAW>d5@qd)*=Wo@t2oR^PYd;gCQuD8H74)I%|pT@RwJc*DUHKespO zH|*Qa%=6;maTX#q!MG3Zy#q2L3lp5z4w{T+L0X}IYq;MK=ud})00FUu1p(pz&xKSC zVCA6hV&dRxV)p;qck_R0Lu-~W4Kl)S$P$Gdp{Jm_w$u9vn4_(sFmW}hNZyHAgcx#nPy!I^%Eqt)JN_$)uJ_?@MK?!Bu{ zr>nQbz(||q37ixjX7l!%=b9Fbj+I&t$o2G2nuJGNhAhSp10w^hXt#3#E|vB|I0Fku zH!*QoORplz1lC&8r7a2$JLEQ^_82<40Imw{_Tx)o-`2|T9jk0~N>(JD=a$Xf4}rJ~ zs@4+O(1OFrLbe-ao^d8db?X&8>~zJcd%vmc6*#iy1!4>vOV495hf1Gi~0pgFX9K(j@+-bI&`+4@mm718E^rKz~A+?AHSoPDQJ?q+>Hw3OQya7)W~^cTEeW= z1MY;cH@bud1N)n+Ls2A3wZw``h`_t6*My^)Uk^UK&ViMdfZ$niYYodn$ z3SuE(thd%|Ncw$b)Km#9Fmevn_4!c~FMit+lk}tf5isFU1_tj!3wjkf1*1_my_Oi7 zE~DXnr^Zj$RPybc*vwrT!aue5otqAz#pzao#wv5!xYn7#^G?l1o(@aYbJwK8`Rdyh z;-Uw#+0YL|R(rE5T^;5voCTFe^V}wf+4DM@!&?_8E=~K~D-iqK=g0f=l0M8VnkB^* z2E23y61Q;dW`qUf6hf<%4Y_EoPk-cel%_}*t7LQ6=vX-KFHA!XI{JhbVvW)VMGY%M;OI(1x z(ARg$6;B-fIQRTt!%Yl!rqs9pRNpHlNCb`K9?4bqxL)wXI?K7!7C4FC!_q1NUH^Ex0$$Da&xJDBl11rS%MwQt9ct0oTAd zw%jOa%-ixAY+F# z)znquh;gK|remekaDx+x4YbMn9XtEQ5+=2-kN=g6BHq`eFxDxv8}aS!O74qTL_c za+$%;PC_$Z>NVPlaj{L?R^x29v^G`ZX4cS{cI2IqTv|vUM(YL~Md@m4aYRkHI9^~w zwo{%EM%kxF6(D^Kc|myWf5jqmZ6wlmiM)*X6aku7Trv5m-WOB z@X{-}yw;8s^%-h$9)?Fr+!Ge*uGNIk-pfC{Cf-%_|9O11;XWxQ9htbE>@o=n5IvIO-w*l9T+v4&2NdwyS>&F0x(ExyJ5NbyojWZQHR0y1bjJ8KyNhe z7n?qY0N*fj?tNV(U{({Zsus1RrR%s@DlAfj#C3s%1kwj{9kvtJEau5>M*&#sKtoTH z?K%9IoZ=w`osLh^oLcfq4@V=^P37shLy#`{^Jky7a-vVE3XzP^B*{Jq8JMC#~^Fv@8ZSThUw+){8pg_oxKkx7Jj>8W1z{n@ciaPv2C z4T|?3iF4As2a>y1TE#2s=CtK&Ow+P(Jf%KX>(0E@D>=2Yhd-GHLy04>MwOFZu*tAJ-MlLSv8xzA0O;y*U2Sx*+D`DTN2&~;-3#p5+Omu?HRX0%ZeuiPPFf$jn0@$TI?L=T?8$8q zoHTt^K8=LN^VWts5z0x3?Xy*iP^de=kkOPPpv-;T2yo+`1P6oME{S8w8a)B>0%N|F zds5a4r9>ooxZa^7_3m_|5$27mQxdnDsJa8whY4csht|R7L{TN<{wv}(W+|eEnrpig zT$_nmgS6wnWAShJc{;DABGjEf`^ z>o|``MK4dta%&EJ4QR>DB1ICiHhPjYE@G4?r{%K5V$#Fz9{C)-k(#hNMo93jl8{$uF@m> zMqsu?-hd%cU{s>G!%xHWDM^Ixo_~f^Z0&e9c(q4ylTbezJwm>h?k4Ckshnqi>{B z_-=Jt?^@GZi{p}nbaMbE@#RX6r&f;$xmEYS`C>KiVAigx+Wt)ShzWJ;e z#uqEsLGNRgL&8Sbb`iRF_Rv+1R;sQW`{QVOMEC`$eyb4?HLH=ZBu2@>*K5 zNZb-#e=P-F&b2oNd##!UjaEo6_boHN2m>81y-AKimd(UMT9eg&L6dWUvCoHiLcOUC z*)r4>)n-~nDWi%9-NGOHhzu__QBpp6Evx#C zcYLV#Tv5BfxoHZjT-ymdmd@>MPStByar$8`o&EO9QIHycvM)P+xuCXA9)h~ zApc&P^k`x%OGuoon^gO8wtaS%nY++j&-Qrk-&f!E_#?QA{{DfEE(z;`VfHjuf~D`r zdp=3K2yRJjZ&KT{?8XKiz!qt#&3l@wR9U77%Re-X$xNJ(>SubaWWPVOj=2jzUThdQ zTVgDwXin}-qOC1AVTa{wqU0h1LWyVy2Ef^3f?tp zZ;*CLy-apP)jCF=JgEBmfLHsnS@PB&b`Q?T1Uu*mnm=jCE>1K{BRh^)z^5H2M9d_c zT$ouHS{QVWDNZhqJ_zo=q#TbxVvp+#{I{E2#X3UFGcXVkA!rZ~)&ET`khAdqmmH|; zxo(Q1|Ixgc&@>oVadqr$VKMG*aS@$nAR+g_XLh!;;kx#^M>hAhO_^AXArGI_%X=l^ zzgt%=fQOHvivOerHa7wAO!FF;x=(U`&q?g^R11B*KOuosdyObN@Cj^te&l_{n&@n; zh3joL+V4C+fZZ0u5BqTPY#gC$GThKuzjJbU=Oa`1Vke1`ZVg3&K5%Oel zJoHu2W|1a>qm|*|Z3_qCadQ2LET*L{U*1ha1G4JJktR<(D<8zUnamf?BhDHLE^JZN z?7H^Tjc@%hHEnU!>?Uena-*y}WjxTbRf3k*u?J?0#m-+VwsH>Lw63?y%Mv}-|F)EOw1?O*3wF!5clF;NtGMBP` z(XaKl7Zi;W9Se0ROQs)gr!LWWgj&dja~+?JKUEqiA#DsxYe>sB3x7n^-Q4=6rH z9%s@<+YNV&yW$|G#+WrY?G4XfbhrGiktKWJbJ|Qe;MdUKC$Out4K{B-7$@&gQ!gOV zNEjn@J>MO&Z!$pJxu<@1q&T~VaYL(R4`)XQi+}g9Xc-^L(?`V$LC*5BRkp1nm$FuZ zWg4pL5Zy>GX`K-ByHDID;mE#sY_TDl-gtMlW6w8f-hqcGt4+|v&Wd`n+LAx~RmK8K6MLt}m3d^@Aq1 z@=1X!l{58`3?fubN)Wbk-1UmP_{k?^a(uS#^^?Q_T}he?R5kPw*N_T{1g6M*IRqL` zd=RMoZX0ZZ;&hNHJTmWM=r|^%sa1zqo+kDG5r=17Vqf zHQ_cEHK8e?;5**`1j~vNY?Jw4usHq&i`4%HSpJJ9qZ+$Pcv8rJp6zxC!okK>V5Rh6 z5$r{Yd7J{rLxI||=A$GU5fflY*8YZ>VGnse-DT%$XSQE4vZ?PI<(mE#%7oQ_6f{oe z5J>>b;ldi5T)SMp&1}uCY$rm0-mg4CIP6&M#sEyLI!4*`-bv7C2*ir1sEv_604Z`_ z8@W*p`XZ`WKCsmZiF@XYh?#F@uYFHG0kB9H^__3>sL@EFt37n*c<#V=9v5e2%Z9bm zB+HP!&FWgPfRWRAL`Dz1aMtREeR-f!|ksE$`hP4l76NaAJ|!f}96rohTgtHiNsipe!IN2L0~pbAsc zRD}E;xlPrlm+sicAO-Nu%V;Fr=sxBJT!;OMnRPg|c_m<7ut2P@NLG$dBWC!883+T( zwNp<1Vm>}f4$$>m!E*D%Qd^ehy|UuTx1@e?)9QwYm@d9loY!|{I)v42}B7y5bNP09h(JMsSb;eBIdoX=K;ms2?Q3*o2#9m4Q3 zoWl!iB6Qv4>Hd%!HWU#HJJU>Yh?2Fbp@s9o4`nls35;PcIod{zn7so(-UkPHv%Je& z`QvvdXLCId46ffXulr=U20zgh+{s%@Hp{^}-~6_4suW~-Y{~U)MXK5jOw4mZnt~ ze$;@vQTPD2^Cy#2r$c6uOn(4+XBhmvLQ&oFQqB8O&dKaqWUcP%{1ho!j^qw(e47ToOXf0!@qYwoUV^d=zZ!=>{ zZ!Hgyw+$UF5}Ow;F1y?Nw_Uyyhz2qge`5mJzlZpL;~>kqk(>Q@ByQnq;Q}zR1Ni*) z4lMsCgH+R#Ko8)Ak?9ygZD3$pSxsv}F!udsw5g0P+CW319;`m1%f)5e;neJTd<_XT zj^uYA{tCA|2V!Q0KNkOodc0W%wuN{DV0pd$&}20~?fRVeH)-tu`eqD@IBYgv|C6_Z z?zg}Tf8iCSvmZ@Z@l}=)N?y}+-Qgn2bMd8o@^sdy9y~2+#v09ubQXpzIOEF}k$3`b zd=See7N`GJ=i<(K)z7+R(uA#MxE{^A+nKxGoAbqmI;K>Cwf!+FILiL2>a`0CJ?HuQ zwiwWZ+H~ow3QW+SPIGc6hojI^OiIg z=PPEr0AuyDsdotK6nwrq%a%`RmRSRKfP6ox^02))Z4fsqrl?nlNyRTH4PUWSmjQ#J zV7W8fosrVh3)Ag~ilKz+1L?kqU6MZUN-*AYURt0bP|(VNI~&P+z&4ovzUm0LcTDr0 za497QugZ-g=%K@SPeI>&qP_MK`yIzrqmT%>4_<3gLAtun=qc2IqaZsshss9O1{8pt zPVctI0DLwPVrnz%nEBC|l07}oioSWCl@tJ_#pevMc1oyLSz^YWPbe7a0!pLbK5-BE zbaYw0E^2N#o?r=`tT)ByPw7?e_9QewI9L&8(lh--=37=c_Sih4tBpas37(Q!s4$II z&MX6kwF{@P_k4vWmL(<}jNcD+f9Wb2tuCLaJ;nEu?yq7NtY@>WM&|F`@F>04v_Pu* z-SuWj4*%q5Iz?m~10Sz2RQZ!^oIsmGuqF6Azs44RgPG%qT(=%fT!DG2-@!k6hB&y) z3feHrh)~$z6hf3cNrvc6AlqcBrnrYs2j_S$jK^W0!sDdxjjfp3$0&;?kr!_LFXr=$ z3%y40GmTHP4j0omIr{6UpZ)N{3B=kEawAM8g`Xocv!-Fpo$HgiMVoKfKQ^-;BgLBz zGYY$Z$*R<>O_6FJTg0>XLhSDCz?L5;Y^fURR>@thiW@p~PEo8pE)ZKv_xAX58yYdo z9afeQUpSY7M{e_+dG!_X$qMMd&^_9xZdDz25h{yUAFhE(&2& z4ZbGsdjD#dxg|RLqoXzcuI%%V+`UxzJt6CuBec;1uk@dy4p_Lg2%@69#36&En?2rD z>n~F_TuQt%Tt*qD;k6N|NDnlBdB>Pm@QmxW^Rv?78SFUmXXXc6Gp@Lw!@v80t_2r3 zqSd~y`!s|cvAnkkwV0V42=8|z-@{OAcR5{jLeiYs+@41GR45f&E{@ZK;mW0JP?cX>Z1_Z?9f1grn{~wI{uYGP~ zoa^+ASX?mazPbv0ih&6xY1D=Mpr)rzqp^MN4w>^P_ z`MU^E)3CDUbtirZ$s@kBDo&6`V;Fk@C0;c{R{EY8BDqi^9KQ)5FV0EHVY5Xx2uo~$y z3zuuT;$FXqjcuNEld9oLl`%Ixh`f9CKX`MTZB$S0ljpF`Jzh-iuG3OG9NmZLa|dog zsFOxw2(;FfvxJ{vZls>dhpX4yF}pyG{=o{a>`5Qy^*MzHDK zDbC_G1YJHWidE&&fHc{+@^Ti;m8y|(T+LPDEUq+ez{hZ5r28#2b!PF%dq#gE(m>O+ zP&7C*-s-@5y3=~pq{omgOuZi%8(^#q(Lqi^mTrUyQc@b;Y8;m|ejD?EKobgp+q zq|p%eI|PAmd#`VvdnE@jSg1NiaS}ra8#p5>>kS}E=_T(XU4^Y%6p4OQ+)%QTP6tc{ip#TNDqT zS$*T1BDtP<062OWv?~fH>Ln#LRl(=KT*LlB5Xj>Ht1puN3smC&GoboUAc-p~@*{kb zLaiD>$tQts&|BDf{@w-8Ls$qKQZv?qOE8x1!_akeul8bsmy~@+lf^@&O@avDJ&vwB z7ym&#qLQY>xXHAsD=Ygnk*wS8`wmL)_XbFfQ)Edv6Myj2Op+7PPQY15BQaLW6xJ8d zH(T4Fh1yd#sj4!**W+cKET?V5lMUpL+}uN7E9cn5$KS`WJu1X((azY}W# zV>7l|&)JP7F^o@EhKR7zyo)e0Kr)LQzr24hAQO<#g_mNk-AGN@kBbPGq2MLW=wmBoowCG=!EO&0b?E7I55M z1kqaCjO$%8U^C^a6NS~hb%t}Ya7}HW%^&xQAmG-nlUi3|0({;dO9Hnx;_}T#-3U>1 z_rnzZyH4w}w}|EiBZ>ysP&{(T29g?f^B)H7f#_#cAF~gMCAKrFUqK(~4n{wly5a!C zAoklSwh`7|aKKR}OvpFG?{&^rss8Ila?~9+Bp9>3WEgnOwQx>@8a=jJ`cSlB{b#Il zG8XVU;*r62QDa$?=vmW;OhR_HpI=gM)}os-hRptVEj@O5v+H9Hi*9GDg`C#hW@ZU% zB*5^-aP8YtGOK}D3hlJub0?{-bqy>i?x4)53PJgt4y<{Qc}3tL$7(Gikbo_ z)=Ryv*dW0K<$;V7pOn;WQVXAq1|kzHi*I3N%p=|52FzK`EM`vH`!8h+En+bp3KJLmqb^`Hk0K*mG0~cF99tG#D4GTYO zdB;^8@&B#O|Gg?XD$yilFcmVs5if6mj?rCzn3 zzhV-YZ{PU;pY!zpJh@QWlwDW(+cJ|CQieY*USMW0RN8_9mSbINxmGmV?N)v+zsyPS zVxUwk?5rl2_1AQU#v8s2Wj!|vgwsi_1-LJx(_E^oq3!c->80cUWI6gCWo&aiJ#Xc| zKRjLe{!r_Y1Qxa}Gymuw45*eGuKhuT7mEm={>$Ply#A2v&pDa!5h&~{MmId zt_LVQsPt?Exf@}|nYYG}eS#ymF)ze6n2Ae?00?%gS9{X1soAnTQD^gaBeY>YH?8N) zw5FBR@3_MbGh%Hx!p=K)_5iG=4~B{k){6{%4KJ$GrNcMsMT_R^Fu)Biwb@9!t#|F(T56=7HA#`4k?PK$sVH=_xaT0Ux!&l9)!rFM9^{!SPT)!g7X$0vF4G? zsPVBUScOMR$u!}Vh9mLrOk1TXVOm=TF)hotL4w(Kkj|(~u`kyAQ`ifLG^d(WHMsdE zj`oN^Txr-(8t!dC4g#;wB3C0+H0e$ygZ*%0nB zZXb9+v&3d~Yy0n2Yt*Z~0GzGO?M%sX}HA0gf0+3ZzkrICo>C^Zk|# zd6_7drkFHY9=A))ZO@%byJK#Bxk?)-a85Tmh2M%cKMG4(b_H5h#kD0E0XY0@a`HW%Z31XOo8 zIQd@$WzRS`OW-oT2_iz{XeF+i5wBr8Q5+3tno-6M9_>L>h)zfJdgL}U`}OHdO_SDG zc<@sY(EUPI{SVvEU3BS+lgV!OF+o(L}*?kTGWQWToM=s~1irJ3{lZ z_e@sQ1EijMinwYhT=O9Lt3u*zVzRj#`igSD!+iSK%uNv_bfH&o=`h|YoVLR<2NAR9 z%S7O2$PsXrzyJ@#SmH@wcW1W6x)WmIT@y8x<cEO^GWkzb{A4O?K+@3~2F13GVV>4RgMFvgM3a5KR>E3~GVPc(2A?YmtMni@yd)Iz zU=Wlv^i}mtUNIr_4c=TXsSZEs!ocP~H zZ75uh7k2w(*~t4>DcpVStYNI;Y;uFe z)^B7SMeHW*5(I9^f(OS(7`x;SS_dU|6ZTS$8~bWs{~}}FctqEjA-{ctNBQaY3asAs5MXMx*H7m*?E(ksm(cthm;4Az@V9`wdH-w2 zO8C`dhUXG-13|8|q|R$n69wu=Af@sJ1ZAOubsnK|g4oAQcm9`fVG&dN`0HiIFXEc> z%>n5WVN@evX>S!j^ z>ot-lI~xNn!BYzn>UTocOBJvy93w?q!uN9fQh?m=u|)HPHa;Rh&RWWhhzI%#%Wg)CNMwB1K|!Y~VwqOk79~ z4LF#+pNi>L+>dxgI!cN)ZWZI+)U)uS-{jE56e-B%c}e=6DMDOJj*#YI&b20qYR(G3 zNEYMqBrW2_t$qO)S!d@oC4m=eQ_g;%=AAo^;!f_An?J1Q8qBf7ext~=vxn-?E%S~k zYt-+vkA(-ut(*?h_arS-F)h~Fp6n6>xySX_W@;%-X1f$B?Ts!998HvCgtqROBzzt< zn+|YL*x5UpE(b?P5%fdE_{s?hXWHeQ$C9*LJSr7xtcELAMOx(=)WtPOFbCoMA`9K? z8|>yvFj3=~!{)BO`&mCAx>YvXUg*5866*|Au{Sp>^$pqE5h!xB0QSUES;mUOhLjOULsh?)L{~_|`Un(Fo4ebPvo^A7r%gQ5L8~xad z44j~?Tiu_Z>>4-O&Zic6!;{6S>yXOVZIK&^ljjx=ZKgnOoHm~tBUq`6fZ?{L z>Nn)apm;c1fyHtM+3s=Z2I)m+4{Lkp<|g`+c!3^`v;{)yQot1z;0Mus+qr8`Rj2~+sV#k=9mkE z5AkLZh4$DZT3)3Pkvw=C%j-l7?_MM?mv(lj$Yf-dNu9i7e3HlgQIqus-aWy0ZIH4lZr^q$ zSdg>HG<8;3aotV9X|9#L=Mb4nzI0GdK_wlHoSm+hg}95%Ez6{a<3Bh$JW}_uyU0{E zK4W#0Cxv5xNMc?sFU|6#h+Fl16NW1oh%|37-Ba-5GZ`)s^PQI3<8+en1SRV{D>3sq zhj%CE(8Ce=@a4L$@p?=DemJb9N~de3f|oE+;SWrEseGY%uOkNv?J}*kgZV9l&C0Gz zxe<*m?K7b;m)-E`=}@W}%1%+CTVKIl#~;u>JJ{bhpGkP)vwqAv3!P~f<;-TQ?Un55 z#OG%rh*E?Z8R}Jl4y|@GV?F_~Y~ffg%~`jQEILS-zqA}by{9L-`Ix_Y58oYg<*i^M z1#F`X&Y1Espdt+_IKn;=MEz|geNf=d-^oJoaK&xEeze!m-s6$h-j@sAKGo9k)kO$( zd1B(eY%tcnOaMJV!oQ6$dfcuaGdjFPv_V~QtY820(dWMZEo!XmIYuvvaIaLnX@;R$B*pHcmrgoOu3!dKF%&! z5!=>R;$8F0sabwtzegEuWO%4K^8R-4Dd1Yh92H@*U`-0$9%fS|y7iEO|G#`so>jd4 zrcc>u{R}7{gd4dL>;>4VLT8??IS$w|#z=_xt~evadAw6mrbuZLQW5F;vVrWAM-HC( z0}n47e{2$41bK_EB8`zKHtttzH*dTIGgYo>s%5(KOmQq2W7S0YE{Id1b?)1h(5seq zL8P*~qmfV8RIaU~62)FKg1J=e2)sx8S5Z4sS-Cg*TlRNw&eHMlj6OKv�TZxrc7S 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

HQ~v!mHYr#!cK8VYaY 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

fn~?3a&&QGOld`;nT~o_HVqC`&l=Rt-@N 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 + + + + + + + + diff --git a/app/src/main/assets/erc20_tokens.json b/app/src/main/assets/erc20_tokens.json index 5f2bfeb629..22fe8bd802 100644 --- a/app/src/main/assets/erc20_tokens.json +++ b/app/src/main/assets/erc20_tokens.json @@ -882,6 +882,12 @@ "name" : "WAX", "contractAddress" : "0x39Bb259F66E1C59d5ABEF88375979b4D20D98022" }, + { + "decimalCount" : 18, + "symbol" : "WETH", + "name" : "Wrapped Ether", + "contractAddress" : "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + }, { "decimalCount" : 18, "symbol" : "WPR", diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 78c6c7dbd6..544e1d7cda 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -2,8 +2,6 @@ package com.tangem.tap import android.content.Intent import android.content.pm.ActivityInfo -import android.nfc.NfcAdapter -import android.nfc.Tag import android.os.Bundle import android.util.Log import android.view.View @@ -14,10 +12,14 @@ import com.tangem.Config import com.tangem.TangemSdk import com.tangem.commands.common.card.CardType import com.tangem.tangem_sdk_new.extensions.init +import com.tangem.tap.common.DialogManager +import com.tangem.tap.common.IntentHandler +import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.redux.NotificationsHandler import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TangemSdkManager +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.home.redux.HomeAction import com.tangem.wallet.R import kotlinx.android.synthetic.main.activity_main.* @@ -53,9 +55,11 @@ private fun initCoroutineExceptionHandler(): CoroutineExceptionHandler { } } -class MainActivity : AppCompatActivity() { +class MainActivity : AppCompatActivity(), SnackbarHandler { private var snackbar: Snackbar? = null + private val dialogManager = DialogManager() + private val intentHandler = IntentHandler() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -65,43 +69,37 @@ class MainActivity : AppCompatActivity() { store.dispatch(NavigationAction.ActivityCreated(WeakReference(this))) tangemSdk = TangemSdk.init( - this, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java))) + this, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java))) ) tangemSdkManager = TangemSdkManager(this) + store.dispatch(WalletConnectAction.RestoreSessions) } override fun onResume() { super.onResume() notificationsHandler = NotificationsHandler(fragment_container) if (supportFragmentManager.backStackEntryCount == 0 || - store.state.globalState.scanNoteResponse == null) { + store.state.globalState.scanNoteResponse == null + ) { store.dispatch(HomeAction.CheckIfFirstLaunch) store.dispatch(NavigationAction.NavigateTo(AppScreen.Home)) } - handleBackgroundScan(intent) + intentHandler.handleIntent(intent) } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - handleBackgroundScan(intent) + intentHandler.handleIntent(intent) } - private fun handleBackgroundScan(intent: Intent?) { - if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || - NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action - )) { - val tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) - if (tag != null) { - intent.action = null - store.dispatch(NavigationAction.NavigateTo(AppScreen.Home)) - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - store.dispatch(HomeAction.ReadCard) - } - } + override fun onStart() { + super.onStart() + dialogManager.onStart(this) } override fun onStop() { notificationsHandler = null + dialogManager.onStop() super.onStop() } @@ -110,11 +108,11 @@ class MainActivity : AppCompatActivity() { super.onDestroy() } - fun showSnackbar(text: Int, buttonTitle: Int? = null, action: View.OnClickListener? = null) { + override fun showSnackbar(text: Int, buttonTitle: Int?, action: View.OnClickListener?) { if (snackbar != null) return snackbar = Snackbar.make( - fragment_container, getString(text), Snackbar.LENGTH_INDEFINITE + fragment_container, getString(text), Snackbar.LENGTH_INDEFINITE ) if (buttonTitle != null && action != null) { snackbar?.setAction(getString(buttonTitle), action) @@ -122,7 +120,7 @@ class MainActivity : AppCompatActivity() { snackbar?.show() } - fun dismissSnackbar() { + override fun dismissSnackbar() { snackbar?.dismiss() snackbar = null } diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index e458fd4e04..491f1c9cfb 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -15,6 +15,7 @@ import com.tangem.tap.domain.configurable.config.FeaturesRemoteLoader import com.tangem.tap.domain.configurable.warningMessage.RemoteWarningLoader import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.tokens.CurrenciesRepository +import com.tangem.tap.domain.walletconnect.WalletConnectRepository import com.tangem.tap.features.feedback.AdditionalEmailInfo import com.tangem.tap.features.feedback.FeedbackManager import com.tangem.tap.features.feedback.TangemLogCollector @@ -32,6 +33,7 @@ val store = Store( ) lateinit var preferencesStorage: PreferencesStorage lateinit var currenciesRepository: CurrenciesRepository +lateinit var walletConnectRepository: WalletConnectRepository class TapApplication : Application() { override fun onCreate() { @@ -52,6 +54,7 @@ class TapApplication : Application() { preferencesStorage = PreferencesStorage(this) PicassoHelper.initPicassoWithCaching(this) currenciesRepository = CurrenciesRepository(this) + walletConnectRepository = WalletConnectRepository(this) initFeedbackManager() loadConfigs() diff --git a/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt b/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt index 0ca9b929bd..2ae1553d38 100644 --- a/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt @@ -12,7 +12,7 @@ class CurrencyConverter( private val rateValue: BigDecimal, private val decimals: Int ) { - private val roundingMode = RoundingMode.DOWN + private val roundingMode = RoundingMode.HALF_UP fun toFiat(crypto: BigDecimal, fiatDecimals: Int = 2): BigDecimal { return toFiatUnscaled(crypto).setScale(fiatDecimals, roundingMode) diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt new file mode 100644 index 0000000000..dd997b7997 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -0,0 +1,55 @@ +package com.tangem.tap.common + +import android.app.Dialog +import android.content.Context +import com.tangem.tap.common.redux.global.GlobalState +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.ApproveWcSessionDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.ClipboardOrScanQrDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionDialog +import com.tangem.tap.store +import org.rekotlin.StoreSubscriber + +class DialogManager : StoreSubscriber { + var context: Context? = null + private var dialog: Dialog? = null + + fun onStart(context: Context) { + this.context = context + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.globalState == newState.globalState + }.select { it.globalState } + } + } + + fun onStop() { + this.context = null + store.unsubscribe(this) + } + + + override fun newState(state: GlobalState) { + if (state.dialog == null) { + dialog?.dismiss() + dialog = null + return + } + val context = context ?: return + if (dialog != null) return + + when (state.dialog) { + is WalletConnectDialog.ApproveWcSession -> + dialog = ApproveWcSessionDialog.create(state.dialog.session, context) + + is WalletConnectDialog.ClipboardOrScanQr -> + dialog = ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context) + is WalletConnectDialog.RequestTransaction -> + dialog = TransactionDialog.create(state.dialog.dialogData, context) + is WalletConnectDialog.PersonalSign -> + dialog = PersonalSignDialog.create(state.dialog.data, context) + } + dialog?.show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt new file mode 100644 index 0000000000..f30a481c98 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt @@ -0,0 +1,41 @@ +package com.tangem.tap.common + +import android.content.Intent +import android.nfc.NfcAdapter +import android.nfc.Tag +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.walletconnect.WalletConnectManager +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.features.home.redux.HomeAction +import com.tangem.tap.store + +class IntentHandler { + + fun handleIntent(intent: Intent?) { + handleBackgroundScan(intent) + handleWalletConnectLink(intent) + } + + private fun handleBackgroundScan(intent: Intent?) { + if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || + NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action + ) + ) { + val tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) + if (tag != null) { + intent.action = null + store.dispatch(NavigationAction.NavigateTo(AppScreen.Home)) + store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) + store.dispatch(HomeAction.ReadCard) + } + } + } + + private fun handleWalletConnectLink(intent: Intent?) { + if (intent?.scheme == WalletConnectManager.WC_SCHEME) { + store.dispatch(WalletConnectAction.HandleDeepLink(intent.data?.toString())) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/SnackbarHandler.kt b/app/src/main/java/com/tangem/tap/common/SnackbarHandler.kt new file mode 100644 index 0000000000..c10b8e6f37 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/SnackbarHandler.kt @@ -0,0 +1,8 @@ +package com.tangem.tap.common + +import android.view.View + +interface SnackbarHandler { + fun showSnackbar(text: Int, buttonTitle: Int? = null, action: View.OnClickListener? = null) + fun dismissSnackbar() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 6e348ea8cf..d56ae0af85 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -9,6 +9,8 @@ import com.tangem.tap.features.details.ui.DetailsFragment import com.tangem.tap.features.details.ui.DetailsSecurityFragment import com.tangem.tap.features.details.ui.twins.CreateTwinWalletFragment import com.tangem.tap.features.details.ui.twins.TwinWalletWarningFragment +import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment +import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFragment import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment import com.tangem.tap.features.home.HomeFragment import com.tangem.tap.features.send.ui.SendFragment @@ -50,5 +52,7 @@ private fun fragmentFactory(screen: AppScreen): Fragment { AppScreen.TwinsOnboarding -> TwinsOnboardingFragment() AppScreen.AddTokens -> AddTokensFragment() AppScreen.WalletDetails -> WalletDetailsFragment() + AppScreen.WalletConnectSessions -> WalletConnectSessionsFragment() + AppScreen.QrScan -> QrScanFragment() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index f97a4cd062..a792b8b25e 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -22,13 +22,16 @@ fun BigDecimal.toFormattedString( return df.format(this) } -fun BigDecimal.toFormattedCurrencyString(decimals: Int, currency: String): String { - return "${this.toFormattedString(decimals)} $currency" +fun BigDecimal.toFormattedCurrencyString( + decimals: Int, currency: String, roundingMode: RoundingMode = RoundingMode.DOWN +): String { + val formattedAmount = this.toFormattedString(decimals = decimals, roundingMode = roundingMode) + return "$formattedAmount $currency" } fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: FiatCurrencyName): String { var fiatValue = rateValue.multiply(this) - fiatValue = fiatValue.setScale(2, RoundingMode.DOWN) + fiatValue = fiatValue.setScale(2, RoundingMode.HALF_UP) return "≈ ${fiatCurrencyName}  $fiatValue" } diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt new file mode 100644 index 0000000000..99e6d8fe5d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt @@ -0,0 +1,15 @@ +package com.tangem.tap.common.extensions + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.scope +import com.tangem.tap.store +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.rekotlin.Action +import org.rekotlin.Store + +fun Store.dispatchOnMain(action: Action) { + scope.launch(Dispatchers.Main) { + store.dispatch(action) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 03dcb4a441..57ed1eaf2c 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -3,6 +3,7 @@ package com.tangem.tap.common.redux import com.tangem.tap.common.redux.global.globalReducer import com.tangem.tap.common.redux.navigation.NavigationReducer import com.tangem.tap.features.details.redux.DetailsReducer +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer import com.tangem.tap.features.home.redux.HomeReducer import com.tangem.tap.features.send.redux.reducers.SendScreenReducer @@ -23,6 +24,7 @@ fun appReducer(action: Action, state: AppState?): AppState { detailsState = DetailsReducer.reduce(action, state), disclaimerState = DisclaimerReducer.reduce(action, state), tokensState = TokensReducer.reduce(action, state), + walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState) ) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 4548ca046f..02deb7d980 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -6,6 +6,8 @@ import com.tangem.tap.common.redux.navigation.NavigationState import com.tangem.tap.common.redux.navigation.navigationMiddleware import com.tangem.tap.features.details.redux.DetailsMiddleware import com.tangem.tap.features.details.redux.DetailsState +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddleware +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState import com.tangem.tap.features.disclaimer.redux.DisclaimerMiddleware import com.tangem.tap.features.disclaimer.redux.DisclaimerState import com.tangem.tap.features.home.redux.HomeMiddleware @@ -20,14 +22,15 @@ import org.rekotlin.Middleware import org.rekotlin.StateType data class AppState( - val navigationState: NavigationState = NavigationState(), - val globalState: GlobalState = GlobalState(), - val homeState: HomeState = HomeState(), - val walletState: WalletState = WalletState(), - val sendState: SendState = SendState(), - val detailsState: DetailsState = DetailsState(), - val disclaimerState: DisclaimerState = DisclaimerState(), - val tokensState: TokensState = TokensState(), + val navigationState: NavigationState = NavigationState(), + val globalState: GlobalState = GlobalState(), + val homeState: HomeState = HomeState(), + val walletState: WalletState = WalletState(), + val sendState: SendState = SendState(), + val detailsState: DetailsState = DetailsState(), + val disclaimerState: DisclaimerState = DisclaimerState(), + val tokensState: TokensState = TokensState(), + val walletConnectState: WalletConnectState = WalletConnectState(), ) : StateType { companion object { @@ -40,6 +43,7 @@ data class AppState( DetailsMiddleware().detailsMiddleware, DisclaimerMiddleware().disclaimerMiddleware, TokensMiddleware().tokensMiddleware, + WalletConnectMiddleware().walletConnectMiddleware ) } } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 38f45e4ca7..b0328ee0c2 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -38,4 +38,7 @@ sealed class GlobalAction : Action { data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction() data class SendFeedback(val emailData: EmailData) : GlobalAction() + + data class ShowDialog(val stateDialog: StateDialog) : GlobalAction() + object HideDialog : GlobalAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index c7ed09486a..b7208034f7 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -67,6 +67,12 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.SetFeedbackManager -> { globalState.copy(feedbackManager = action.feedbackManager) } + is GlobalAction.ShowDialog -> { + globalState.copy(dialog = action.stateDialog) + } + is GlobalAction.HideDialog -> { + globalState.copy(dialog = null) + } else -> globalState } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index a0ac13bc06..c7e173310b 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -22,6 +22,7 @@ data class GlobalState( val feedbackManager: FeedbackManager? = null, val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, val scanCardFailsCounter: Int = 0, + val dialog: StateDialog? = null ) : StateType typealias CryptoCurrencyName = String diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt index 26344b0175..ed59b9b2d5 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt @@ -11,5 +11,6 @@ data class NavigationState( enum class AppScreen { Home, Wallet, WalletDetails, Send, Details, DetailsConfirm, DetailsSecurity, Disclaimer, - CreateTwinWalletWarning, CreateTwinWallet, TwinsOnboarding, AddTokens + CreateTwinWalletWarning, CreateTwinWallet, TwinsOnboarding, AddTokens, WalletConnectSessions, + QrScan } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index d4ce5e4b9f..af401f69bc 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -33,10 +33,14 @@ class TangemSdkManager(val activity: ComponentActivity) { activity, Config(cardFilter = CardFilter(EnumSet.allOf(CardType::class.java))) ) - suspend fun scanNote(analyticsHandler: AnalyticsHandler): CompletionResult { + suspend fun scanNote( + analyticsHandler: AnalyticsHandler, messageRes: Int? = null + ): CompletionResult { analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) return runTaskAsyncReturnOnMain(ScanNoteTask(), - initialMessage = Message(activity.getString(R.string.initial_message_scan_header))) + initialMessage = Message( + activity.getString(messageRes ?: R.string.initial_message_scan_header) + )) } suspend fun createWallet(cardId: String?): CompletionResult { diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index afcac855d5..ace735abd2 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -3,6 +3,7 @@ package com.tangem.tap.domain import com.tangem.commands.common.card.Card import com.tangem.commands.common.card.EllipticCurve import com.tangem.commands.common.card.masks.Product +import com.tangem.tap.domain.extensions.getSingleWallet import java.util.* object TapWorkarounds { @@ -43,6 +44,6 @@ val Card.isMultiwalletAllowed: Boolean get() { return cardData?.productMask?.contains(Product.TwinCard) != true && !TapWorkarounds.isStart2Coin - && (this.firmwareVersion.major >= 4 || - this.getWallets().getOrNull(0)?.curve == EllipticCurve.Secp256k1) + && (firmwareVersion.major >= 4 || + getSingleWallet()?.curve == EllipticCurve.Secp256k1) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index 99e2cba747..00e6acb92f 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -28,7 +28,7 @@ fun Card.getSingleWallet(): CardWallet? { fun Card.getStatus(): CardStatus { if (firmwareVersion < FirmwareConstraints.AvailabilityVersions.walletData) return status!! - return if (getWallets().any { it.status == WalletStatus.Loaded }) { + return if (wallets.any { it.status == WalletStatus.Loaded }) { CardStatus.Loaded } else { CardStatus.Empty @@ -36,11 +36,11 @@ fun Card.getStatus(): CardStatus { } fun Card.hasSignedHashes(): Boolean { - return getWallets().any { it.status == WalletStatus.Loaded && it.signedHashes ?: 0 > 0 } + return wallets.any { it.status == WalletStatus.Loaded && it.signedHashes ?: 0 > 0 } } fun Card.signedHashesCount(): Int { - return getWallets().map { it.signedHashes ?: 0 }.sum() + return wallets.map { it.signedHashes ?: 0 }.sum() } val Card.remainingSignatures: Int? diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt index 8b985f728f..68a0ee5b84 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt @@ -15,7 +15,7 @@ fun WalletManagerFactory.makeWalletManagerForApp( blockchain: Blockchain, ): WalletManager? { val supportedCurves = blockchain.getSupportedCurves() ?: return null - val wallets = card.getWallets().filter { wallet -> supportedCurves.contains(wallet.curve) } + val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) } val wallet = selectWallet(wallets) val publicKey = wallet?.publicKey ?: return null val curveToUse = wallet.curve ?: return null @@ -42,7 +42,7 @@ fun WalletManagerFactory.makePrimaryWalletManager( val card = data.card val blockchain = card.getBlockchain() val supportedCurves = blockchain?.getSupportedCurves() ?: return null - val wallets = card.getWallets().filter { wallet -> supportedCurves.contains(wallet.curve) } + val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) } val wallet = selectWallet(wallets) val publicKey = wallet?.publicKey ?: return null val curveToUse = wallet.curve ?: return null diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index c7a8a6cb8e..0415316f32 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -43,8 +43,8 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { val card = this.card?.copy( isPin1Default = result.data.isPin1Default, - isPin2Default = result.data.isPin2Default - )?.also { it.setWallets(card.getWallets()) } ?: result.data + isPin2Default = result.data.isPin2Default, + ) ?: result.data val error = getErrorIfExcludedCard(card) if (error != null) { @@ -88,7 +88,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable = mutableMapOf() + + fun connect(wcUri: String, wallet: WalletForSession) { + val session = WCSession.from(wcUri) ?: return + val client = WCClient(httpClient = okHttpClient) + setListeners(client) + val peerId = UUID.randomUUID().toString() + client.connect(session, tangemPeerMeta, peerId) + sessions[session] = WalletConnectActiveData( + peerId = peerId, + remotePeerId = null, + session = session, + client = client, + wallet = wallet + ) + } + + fun restoreSessions() { + val sessions = walletConnectRepository.loadSavedSessions() + this.sessions = sessions + .map { session -> + WalletConnectActiveData( + peerId = session.peerId, + remotePeerId = session.remotePeerId, + client = WCClient(httpClient = okHttpClient), + session = session.session, + peerMeta = session.peerMeta, + wallet = session.wallet + ) + .also { + setListeners(it.client) + it.client.connect(it.session, tangemPeerMeta, it.peerId, it.remotePeerId) + } + } + .map { it.session to it }.toMap().toMutableMap() + store.dispatchOnMain(WalletConnectAction.SetSessionsRestored(sessions)) + } + + fun approve(session: WCSession) { + val activeData = sessions[session] ?: return + removeSimilarSessions(activeData) + + val approved = activeData.client.approveSession( + accounts = listOf(Blockchain.Ethereum.makeAddresses( + activeData.wallet.walletPublicKey.hexToBytes()).first().value + ), + chainId = activeData.wallet.chainId + ) + if (approved) { + val walletConnectSession = WalletConnectSession( + peerId = activeData.peerId, + remotePeerId = activeData.remotePeerId, + wallet = activeData.wallet, + session = session, + peerMeta = activeData.peerMeta!! + ) + walletConnectRepository.saveSession(walletConnectSession) + store.dispatchOnMain(WalletConnectAction.ApproveSession.Success( + walletConnectSession)) + + } + } + + fun removeSimilarSessions(activeData: WalletConnectActiveData) { + val sessionsToRemove = sessions.filter { + it.value.wallet.walletPublicKey == activeData.wallet.walletPublicKey + && it.value.peerMeta?.url == activeData.peerMeta?.url + && it.value.session != activeData.session + } + Timber.d("RemoveSimilarSessions: ${sessionsToRemove.values.map { it.client.session }}") + sessionsToRemove.forEach { disconnect(it.value.session) } + } + + fun rejectRequest(session: WCSession, id: Long) { + val activeData = sessions[session] ?: return + activeData.client.rejectRequest(id) + } + + fun acceptRequest(session: WCSession, id: Long, data: String) { + val activeData = sessions[session] ?: return + activeData.client.approveRequest(id, data) + } + + fun disconnect(session: WCSession) { + val activeData = sessions[session] ?: return + val disconnected = activeData.client.killSession() + if (disconnected) { + onSessionClosed(session) + } + } + + private fun onSessionClosed(session: WCSession) { + sessions.remove(session) + walletConnectRepository.removeSession(session) + store.dispatchOnMain(WalletConnectAction.RemoveSession(session)) + } + + fun handleTransactionRequest( + transaction: WCEthereumTransaction, + session: WalletConnectSession, + id: Long, + type: WcTransactionType, + ) { + val activeData = sessions[session.session] ?: return + scope.launch { + val data = WalletConnectSdkHelper().prepareTransactionData( + transaction = transaction, + session = session, + id = id, + type = type + ).guard { + sessions[session.session] = activeData.copy(transactionData = null) + store.dispatchOnMain(WalletConnectAction.RejectRequest( + session.session, + id + )) + return@launch + } + sessions[session.session] = activeData.copy(transactionData = data) + + store.dispatchOnMain(WalletConnectAction.SetDataToSend(data)) + store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.RequestTransaction( + data.dialogData))) + } + } + + fun completeTransaction(session: WCSession) { + val activeData = sessions[session] + val data = activeData?.transactionData ?: return + scope.launch { + val hash = WalletConnectSdkHelper().completeTransaction(data).guard { + sessions[data.session.session] = activeData.copy(transactionData = null) + store.dispatchOnMain(WalletConnectAction.RejectRequest(data.session.session, + data.id + )) + return@launch + } + acceptRequest(data.session.session, data.id, hash) + sessions[data.session.session] = activeData.copy(transactionData = null) + } + + } + + fun handlePersonalSignRequest( + message: WCEthereumSignMessage, + session: WalletConnectSession, + id: Long, + ) { + val activeData = sessions[session.session] ?: return + scope.launch { + val data = WalletConnectSdkHelper().prepareDataForPersonalSign( + message = message, session = session, id = id + ) + sessions[session.session] = activeData.copy(personalSignData = data) + store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.PersonalSign( + data.dialogData + ))) + } + } + + fun sendSignedMessage(session: WCSession) { + val activeData = sessions[session] + val data = activeData?.personalSignData ?: return + scope.launch { + val hash = WalletConnectSdkHelper().signPersonalMessage(data.hash, activeData.wallet) + .guard { + sessions[data.session.session] = activeData.copy(transactionData = null) + store.dispatchOnMain(WalletConnectAction.RejectRequest(data.session.session, + data.id + )) + return@launch + } + sessions[session] = activeData.copy(personalSignData = data) + acceptRequest(data.session.session, data.id, hash) + sessions[data.session.session] = activeData.copy(transactionData = null) + } + } + + + fun setListeners(client: WCClient) { + client.onSessionRequest = { id: Long, peer: WCPeerMeta -> + Timber.d("OnSessionRequest: $peer") + val session = client.session + val data = sessions[session]?.copy(peerMeta = peer, remotePeerId = client.remotePeerId) + if (data != null && session != null) { + sessions[session] = data + val sessionData = data.toWalletConnectSession() + sessionData?.let { + store.dispatchOnMain(WalletConnectAction.AcceptOpeningSession( + sessionData)) + } + } + } + client.onSessionUpdate = { id: Long, update: WCSessionUpdate -> + Timber.d("onSessionUpdate: $update") + val session = client.session + if (session != null && !update.approved) onSessionClosed(session) + } + client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction -> + Timber.d("onEthSendTransaction: $transaction") + sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> + store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest( + transaction = transaction, + session = sessionData, + id = id, + type = WcTransactionType.EthSendTransaction + )) + } + + } + client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction -> + Timber.d("onEthSignTransaction: $transaction") + sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> + store.dispatchOnMain(WalletConnectAction.HandleTransactionRequest( + transaction = transaction, + session = sessionData, + id = id, + type = WcTransactionType.EthSignTransaction + )) + } + } + client.onEthSign = { id: Long, message: WCEthereumSignMessage -> + Timber.d("onEthSign: $message") + sessions[client.session]?.toWalletConnectSession()?.let { sessionData -> + store.dispatchOnMain(WalletConnectAction.HandlePersonalSignRequest( + message, + sessionData, + id)) + } + } + client.onDisconnect = { code: Int, reason: String -> + val session = client.session + if (session != null) { + onSessionClosed(session) + } + } + } + + companion object { + private val tangemPeerMeta = + WCPeerMeta(name = "Tangem Wallet", url = "https://tangem.com") + + fun isCorrectWcUri(string: String): Boolean = WCSession.from(string) != null + + const val WC_SCHEME = "wc" + } +} + +data class WalletConnectActiveData( + val peerId: String, + val remotePeerId: String?, + val client: WCClient, + val session: WCSession, + val peerMeta: WCPeerMeta? = null, + val wallet: WalletForSession, + val transactionData: WcTransactionData? = null, + val personalSignData: WcPersonalSignData? = null, +) { + fun toWalletConnectSession(): WalletConnectSession? { + if (peerMeta == null) return null + return WalletConnectSession( + peerId = peerId, + remotePeerId = remotePeerId, + wallet = wallet, + session = session, + peerMeta = peerMeta + ) + } +} + +class RetryInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val request: Request = chain.request() + val response = chain.proceed(request) + when (response.code) { + 502 -> { + return chain.proceed(request) + } + } + return response + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt new file mode 100644 index 0000000000..a4654d6429 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt @@ -0,0 +1,94 @@ +package com.tangem.tap.domain.walletconnect + +import android.app.Application +import android.content.Context +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonClass +import com.squareup.moshi.Types +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession +import com.tangem.tap.features.details.redux.walletconnect.WalletForSession +import com.tangem.tap.network.createMoshi +import com.trustwallet.walletconnect.models.WCPeerMeta +import com.trustwallet.walletconnect.models.session.WCSession + +class WalletConnectRepository(val context: Application) { + private val moshi = createMoshi() + private val walletConnectAdapter: JsonAdapter> = moshi.adapter( + Types.newParameterizedType(List::class.java, SessionDao::class.java) + ) + + fun saveSession(session: WalletConnectSession) { + val sessions = loadSavedSessions() + session + saveSessions(sessions) + } + + fun removeSession(session: WalletConnectSession) { + val sessions = loadSavedSessions().filterNot { it == session } + saveSessions(sessions) + } + + fun removeSession(session: WCSession) { + val sessions = loadSavedSessions().filterNot { it.session == session } + saveSessions(sessions) + } + + fun loadSavedSessions(): List { + return try { + val json = context.readFileText(FILE_NAME_PREFIX_SESSIONS) + walletConnectAdapter.fromJson(json)!!.map { it.toSession() } + } catch (exception: Exception) { + emptyList() + } + } + + private fun saveSessions(sessions: List) { + val json = walletConnectAdapter.toJson(sessions.map { SessionDao.fromSession(it) }) + context.rewriteFile(json, FILE_NAME_PREFIX_SESSIONS) + } + + private fun Context.readFileText(fileName: String): String = + this.openFileInput(fileName).bufferedReader().readText() + + private fun Context.rewriteFile(content: String, fileName: String) { + this.openFileOutput(fileName, Context.MODE_PRIVATE).use { + it.write(content.toByteArray(), 0, content.length) + } + } + + companion object { + private const val FILE_NAME_PREFIX_SESSIONS = "wc_sessions" + } +} + + +@JsonClass(generateAdapter = true) +data class SessionDao( + val peerId: String, + val remotePeerId: String?, + val wallet: WalletForSession, + val session: WCSession, + val peerMeta: WCPeerMeta, +) { + fun toSession(): WalletConnectSession { + return WalletConnectSession( + peerId = peerId, + remotePeerId = remotePeerId, + wallet = wallet, + session = session, + peerMeta = peerMeta + ) + } + + companion object { + fun fromSession(session: WalletConnectSession): SessionDao { + return SessionDao( + peerId = session.peerId, + remotePeerId = session.remotePeerId, + wallet = session.wallet, + session = session.session, + peerMeta = session.peerMeta + ) + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt new file mode 100644 index 0000000000..e0d37ec8e0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -0,0 +1,240 @@ +package com.tangem.tap.domain.walletconnect + +import com.google.common.base.CharMatcher +import com.tangem.Message +import com.tangem.blockchain.blockchains.ethereum.EthereumGasLoader +import com.tangem.blockchain.blockchains.ethereum.EthereumHelper +import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.TransactionData +import com.tangem.blockchain.common.TransactionSender +import com.tangem.blockchain.extensions.Result +import com.tangem.blockchain.extensions.Signer +import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.blockchain.extensions.hexToBigDecimal +import com.tangem.commands.SignCommand +import com.tangem.commands.wallet.WalletIndex +import com.tangem.common.CompletionResult +import com.tangem.common.extensions.hexToBytes +import com.tangem.tap.common.extensions.toFormattedString +import com.tangem.tap.features.details.redux.walletconnect.* +import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData +import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData +import com.tangem.tap.store +import com.tangem.tap.tangemSdk +import com.tangem.tap.tangemSdkManager +import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage +import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction +import org.kethereum.crypto.api.ec.ECDSASignature +import org.kethereum.crypto.determineRecId +import org.kethereum.crypto.impl.ec.canonicalise +import org.kethereum.keccakshortcut.keccak +import org.kethereum.model.PublicKey +import timber.log.Timber +import java.math.BigDecimal +import java.math.BigInteger + +class WalletConnectSdkHelper { + + suspend fun prepareTransactionData( + transaction: WCEthereumTransaction, + session: WalletConnectSession, + id: Long, + type: WcTransactionType, + ): WcTransactionData? { + val factory = store.state.globalState.tapWalletManager.walletManagerFactory + val walletManager = factory.makeEthereumWalletManager( + session.wallet.cardId, + session.wallet.walletPublicKey.hexToBytes(), + emptyList(), + isTestNet = session.wallet.isTestNet + ) ?: return null + + + try { + walletManager.update() + } catch (exception: Exception) { + Timber.e(exception) + return null + } + + val blockchain = walletManager.wallet.blockchain + + val balance = + walletManager.wallet.amounts[AmountType.Coin]?.value ?: return null + + val gas = transaction.gas?.hexToBigDecimal() + ?: transaction.gasLimit?.hexToBigDecimal() ?: return null + + val decimals = blockchain.decimals() + + val value = transaction.value?.hexToBigDecimal() + ?.movePointLeft(decimals) ?: return null + + val gasPrice = transaction.gasPrice?.hexToBigDecimal() + ?: when (val result = + (walletManager as? EthereumGasLoader)?.getGasPrice()) { + is Result.Success -> result.data.toBigDecimal() + is Result.Failure -> { + Timber.e(result.error) + return null + } + null -> return null + } + + val fee = (gas * gasPrice).movePointLeft(decimals) + val total = value + fee + + val transactionData = TransactionData( + amount = Amount(value, blockchain), + fee = Amount(fee, blockchain), + sourceAddress = transaction.from, + destinationAddress = transaction.to!!, + extras = EthereumTransactionExtras( + data = transaction.data.removePrefix(HEX_PREFIX).hexToBytes(), + gasLimit = gas.toBigInteger(), + nonce = transaction.nonce?.hexToBigDecimal()?.toBigInteger() + ) + ) + val dialogData = TransactionRequestDialogData( + cardId = session.wallet.cardId, + dAppName = session.peerMeta.name, + dAppUrl = session.peerMeta.url, + amount = value.toFormattedString(decimals), + gasAmount = fee.toFormattedString(decimals), + totalAmount = total.toFormattedString(decimals), + balance = balance.toFormattedString(decimals), + isEnoughFundsToSend = (balance - total) >= BigDecimal.ZERO, + session = session.session, + id = id, + type = type + ) + return WcTransactionData( + type = type, + transaction = transactionData, + session = session, + id = id, + walletManager = walletManager, + dialogData = dialogData + ) + } + + suspend fun completeTransaction(data: WcTransactionData): String? { + return when (data.type) { + WcTransactionType.EthSendTransaction -> sendTransaction(data) + WcTransactionType.EthSignTransaction -> signTransaction(data) + } + } + + private suspend fun sendTransaction(data: WcTransactionData): String? { + val result = (data.walletManager as TransactionSender).send( + transactionData = data.transaction, + signer = Signer(tangemSdk) + ) + return when (result) { + SimpleResult.Success -> { + HEX_PREFIX + data.walletManager.wallet.recentTransactions.last().hash + } + is SimpleResult.Failure -> { + Timber.e(result.error) + null + } + } + } + + private suspend fun signTransaction(data: WcTransactionData): String? { + val dataToSign = EthereumHelper.buildTransactionToSign( + transactionData = data.transaction, + nonce = null, + blockchain = data.walletManager.wallet.blockchain, + gasLimit = null + ) ?: return null + + val command = SignCommand( + hashes = arrayOf(dataToSign.hash), + walletIndex = WalletIndex.PublicKey(data.walletManager.wallet.publicKey) + ) + val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message()) + return when (result) { + is CompletionResult.Success -> { + HEX_PREFIX + result.data + } + is CompletionResult.Failure -> { + Timber.e(result.error.customMessage) + null + } + } + } + + fun prepareDataForPersonalSign( + message: WCEthereumSignMessage, + session: WalletConnectSession, + id: Long, + ): WcPersonalSignData { + val messageData = message.data.removePrefix(HEX_PREFIX).hexToBytes() + val messageString = message.data.hexToAscii() ?: message.data + + val prefixData = (ETH_MESSAGE_PREFIX + messageData.size.toString()).toByteArray() + val hashToSign = (prefixData + messageData).keccak() + + + val dialogData = PersonalSignDialogData( + cardId = session.wallet.cardId, + dAppName = session.peerMeta.name, + message = messageString, + session = session.session, + id = id + ) + return WcPersonalSignData( + hash = hashToSign, + session = session, + id = id, + dialogData = dialogData + ) + } + + private fun String.hexToAscii(): String? { + return removePrefix(HEX_PREFIX).hexToBytes() + .map { + val char = it.toInt().toChar() + if (char.isAscii()) char else return null + } + .joinToString("") + } + + private fun Char.isAscii(): Boolean = CharMatcher.ascii().matches(this) + + suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? { + val publicKey = wallet.walletPublicKey.hexToBytes() + val command = SignCommand( + arrayOf(hashToSign), + WalletIndex.PublicKey(publicKey)) + return when (val result = tangemSdkManager.runTaskAsync(command, wallet.cardId)) { + is CompletionResult.Success -> { + val hash = result.data.signatures.first() + + val r = BigInteger(1, hash.copyOfRange(0, 32)) + val s = BigInteger(1, hash.copyOfRange(32, 64)) + + val ecdsaSignature = ECDSASignature(r, s).canonicalise() + + val recId = ecdsaSignature.determineRecId(hashToSign, + PublicKey(publicKey.sliceArray(1..64))) + val v = (recId + 27).toBigInteger() + + return HEX_PREFIX + ecdsaSignature.r.toString(16) + ecdsaSignature.s.toString(16) + + v.toString(16) + } + is CompletionResult.Failure -> { + Timber.e(result.error.customMessage) + null + } + } + } + + companion object { + private const val ETH_MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n" + private const val HEX_PREFIX = "0x" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt new file mode 100644 index 0000000000..6e950a1cab --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt @@ -0,0 +1,79 @@ +package com.tangem.tap.features.details.redux.walletconnect + +import android.app.Activity +import com.tangem.tap.common.redux.NotificationAction +import com.tangem.wallet.R +import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage +import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction +import com.trustwallet.walletconnect.models.session.WCSession +import org.rekotlin.Action + +sealed class WalletConnectAction : Action { + + data class HandleDeepLink(val wcUri: String?) : WalletConnectAction() + + object RestoreSessions : WalletConnectAction() + + data class StartWalletConnect( + val activity: Activity, + ) : WalletConnectAction() + + data class ShowClipboardOrScanQrDialog(val wcUri: String) : WalletConnectAction() + + data class ScanCard(val wcUri: String) : WalletConnectAction() + + object UnsupportedCard : WalletConnectAction() + + data class OpenSession( + val wcUri: String, + val wallet: WalletForSession, + ) : WalletConnectAction() + + data class AcceptOpeningSession(val session: WalletConnectSession) : WalletConnectAction() + + data class ApproveSession( + val session: WCSession, + ) : WalletConnectAction() { + data class Success(val session: WalletConnectSession) : WalletConnectAction() + } + + object FailureEstablishingSession : WalletConnectAction() + + + data class SetSessionsRestored(val sessions: List) : + WalletConnectAction() + + data class DisconnectSession(val session: WCSession) : WalletConnectAction() + + data class RemoveSession(val session: WCSession) : WalletConnectAction() + + data class HandleTransactionRequest( + val transaction: WCEthereumTransaction, + val session: WalletConnectSession, + val id: Long, + val type: WcTransactionType, + ) : + WalletConnectAction() + + data class SetDataToSend(val transactionData: WcTransactionData) : WalletConnectAction() + + data class HandlePersonalSignRequest( + val message: WCEthereumSignMessage, + val session: WalletConnectSession, + val id: Long, + ) : WalletConnectAction() + + data class SendTransaction(val session: WCSession) : WalletConnectAction() + + data class SignMessage(val session: WCSession) : WalletConnectAction() + + data class RejectRequest(val session: WCSession, val id: Long) : WalletConnectAction() + + object NotEnoughFunds : WalletConnectAction(), NotificationAction { + override val messageResource = R.string.wallet_connect_create_tx_not_enough_funds + } + + object NotifyCameraPermissionIsRequired : WalletConnectAction(), NotificationAction { + override val messageResource = R.string.common_camera_denied_alert_message + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt new file mode 100644 index 0000000000..896e3c06d2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -0,0 +1,156 @@ +package com.tangem.tap.features.details.redux.walletconnect + +import com.tangem.blockchain.common.* +import com.tangem.common.CompletionResult +import com.tangem.common.extensions.toHexString +import com.tangem.tap.* +import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.extensions.getFromClipboard +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.extensions.makeWalletManagerForApp +import com.tangem.tap.domain.isMultiwalletAllowed +import com.tangem.tap.domain.walletconnect.WalletConnectManager +import com.tangem.wallet.R +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.rekotlin.Middleware + +class WalletConnectMiddleware { + private val walletConnectManager = WalletConnectManager() + + val walletConnectMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + when (action) { + + is WalletConnectAction.RestoreSessions -> { + walletConnectManager.restoreSessions() + } + + is WalletConnectAction.HandleDeepLink -> { + if (!action.wcUri.isNullOrBlank()) { + if (WalletConnectManager.isCorrectWcUri(action.wcUri)) { + store.dispatchOnMain(WalletConnectAction.ScanCard(action.wcUri)) + } + } + } + + is WalletConnectAction.StartWalletConnect -> { + val uri = action.activity.getFromClipboard()?.toString() + if (uri != null && WalletConnectManager.isCorrectWcUri(uri)) { + store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri)) + } else { + store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.QrScan)) + } + } + + is WalletConnectAction.ShowClipboardOrScanQrDialog -> { + store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.ClipboardOrScanQr( + action.wcUri))) + } + + is WalletConnectAction.ScanCard -> { + scope.launch { + val result = tangemSdkManager.scanNote( + FirebaseAnalyticsHandler, + R.string.wallet_connect_scan_card_message + ) + withContext(Dispatchers.Main) { + when (result) { + is CompletionResult.Success -> { + val card = result.data.card + val factory = + store.state.globalState.tapWalletManager.walletManagerFactory + + val walletManager = if (card.isMultiwalletAllowed) { + if (currenciesRepository.loadCardCurrencies(card.cardId)?.blockchains?.contains( + Blockchain.Ethereum) == true + ) { + factory.makeWalletManagerForApp(result.data.card, + Blockchain.Ethereum) + } else { + factory.makeWalletManagerForApp(result.data.card, + Blockchain.Ethereum)?.also { + currenciesRepository.saveAddedBlockchain(card.cardId, + Blockchain.Ethereum) + } + } + } else { + null + } + if (walletManager == null) { + store.dispatchOnMain(WalletConnectAction.UnsupportedCard) + return@withContext + }; + + val key = walletManager.wallet.publicKey + store.dispatchOnMain(WalletConnectAction.OpenSession( + wcUri = action.wcUri, + wallet = WalletForSession( + card.cardId, key.toHexString(), + isTestNet = false + ), + )) + } + is CompletionResult.Failure -> + store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession) + } + } + } + } + + is WalletConnectAction.OpenSession -> { + walletConnectManager.connect( + wcUri = action.wcUri, + wallet = action.wallet + ) + } + + is WalletConnectAction.AcceptOpeningSession -> { + store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.ApproveWcSession( + action.session))) + } + + is WalletConnectAction.ApproveSession -> { + walletConnectManager.approve(action.session) + } + + is WalletConnectAction.DisconnectSession -> { + walletConnectManager.disconnect(action.session) + } + + is WalletConnectAction.HandleTransactionRequest -> { + walletConnectManager.handleTransactionRequest( + transaction = action.transaction, + session = action.session, + id = action.id, + type = action.type + ) + } + is WalletConnectAction.HandlePersonalSignRequest -> { + walletConnectManager.handlePersonalSignRequest( + message = action.message, + session = action.session, + id = action.id + ) + } + is WalletConnectAction.RejectRequest -> { + walletConnectManager.rejectRequest(action.session, action.id) + } + is WalletConnectAction.SendTransaction -> { + walletConnectManager.completeTransaction(action.session) + } + is WalletConnectAction.SignMessage -> { + walletConnectManager.sendSignedMessage(action.session) + } + } + next(action) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt new file mode 100644 index 0000000000..84ef36046b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectReducer.kt @@ -0,0 +1,36 @@ +package com.tangem.tap.features.details.redux.walletconnect + +import org.rekotlin.Action + +class WalletConnectReducer { + companion object { + fun reduce( + action: Action, state: WalletConnectState, + ): WalletConnectState { + if (action !is WalletConnectAction) return state + + return when (action) { + is WalletConnectAction.ApproveSession.Success -> { + state.copy( + loading = false, + sessions = state.sessions + action.session + ) + } + is WalletConnectAction.SetSessionsRestored -> + WalletConnectState(sessions = action.sessions) + + is WalletConnectAction.RemoveSession -> { + val sessions = + state.sessions.filterNot { it.session.toUri() == action.session.toUri() } + state.copy(sessions = sessions) + } + is WalletConnectAction.ScanCard -> state.copy(loading = true) + + is WalletConnectAction.FailureEstablishingSession -> state.copy(loading = false) + + else -> state + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt new file mode 100644 index 0000000000..75dad54452 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt @@ -0,0 +1,70 @@ +package com.tangem.tap.features.details.redux.walletconnect + +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.TransactionData +import com.tangem.blockchain.common.WalletManager +import com.tangem.common.extensions.hexToBytes +import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData +import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData +import com.trustwallet.walletconnect.models.WCPeerMeta +import com.trustwallet.walletconnect.models.session.WCSession + +data class WalletConnectState( + val loading: Boolean = false, + val sessions: List = listOf(), +) + +data class WalletConnectSession( + val peerId: String, + val remotePeerId: String?, + val wallet: WalletForSession, + val session: WCSession, + val peerMeta: WCPeerMeta, +) + +data class WalletForSession( + val cardId: String, + val walletPublicKey: String, + val isTestNet: Boolean = false, +) { + val chainId + get() = if (isTestNet) 4 else 1 + + fun getAddress(): String { + val blockchain = if (isTestNet) Blockchain.EthereumTestnet else Blockchain.Ethereum + return blockchain.makeAddresses(walletPublicKey.hexToBytes()).first().value + } +} + + +sealed class WalletConnectDialog : StateDialog { + data class ClipboardOrScanQr(val clipboardUri: String) : WalletConnectDialog() + data class ApproveWcSession(val session: WalletConnectSession) : WalletConnectDialog() + data class RequestTransaction(val dialogData: TransactionRequestDialogData) : + WalletConnectDialog() + + data class PersonalSign(val data: PersonalSignDialogData) : WalletConnectDialog() +} + +data class WcTransactionData( + val type: WcTransactionType, + val transaction: TransactionData, + val session: WalletConnectSession, + val id: Long, + val walletManager: WalletManager, + val dialogData: TransactionRequestDialogData, +) + +enum class WcTransactionType { + EthSignTransaction, + EthSendTransaction, +} + +data class WcPersonalSignData( + val hash: ByteArray, + val session: WalletConnectSession, + val id: Long, + val dialogData: PersonalSignDialogData, + + ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 895f379b09..3b0e9c83a1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -9,6 +9,7 @@ import androidx.transition.TransitionInflater import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.twins.getTwinCardIdForUser import com.tangem.tap.domain.twins.isTwinCard @@ -120,6 +121,10 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber, + grantResults: IntArray, + ) { + if (requestCode != CameraView.PERMISSION_REQUEST_CODE) return + + if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) { + store.dispatch(WalletConnectAction.NotifyCameraPermissionIsRequired) + store.dispatch(NavigationAction.PopBackTo()) + } + } + + private fun permissionIsGranted(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val cameraPermission = + ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) + cameraPermission == PackageManager.PERMISSION_GRANTED + } else { + true + } + } + + private fun requestPermission() { + requestPermissions(arrayOf(Manifest.permission.CAMERA), CameraView.PERMISSION_REQUEST_CODE) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/SessionsAdapter.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/SessionsAdapter.kt new file mode 100644 index 0000000000..c1028c874d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/SessionsAdapter.kt @@ -0,0 +1,55 @@ +package com.tangem.tap.features.details.ui.walletconnect + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.item_wallet_connect_session.view.* + +class WalletConnectSessionsAdapter + : ListAdapter( + DiffUtilCallback +) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SessionsViewHolder { + val layout = LayoutInflater.from(parent.context) + .inflate(R.layout.item_wallet_connect_session, parent, false) + return SessionsViewHolder(layout) + } + + override fun onBindViewHolder(holder: SessionsViewHolder, position: Int) { + holder.bind(currentList[position]) + } + + object DiffUtilCallback : DiffUtil.ItemCallback() { + override fun areContentsTheSame( + oldItem: WalletConnectSession, newItem: WalletConnectSession, + ) = oldItem == newItem + + override fun areItemsTheSame( + oldItem: WalletConnectSession, newItem: WalletConnectSession, + ) = oldItem == newItem + } + + class SessionsViewHolder(val view: View) : + RecyclerView.ViewHolder(view) { + + fun bind(session: WalletConnectSession) { + view.tv_card_id.text = view.context.getString( + R.string.wallet_connect_card_number, session.wallet.cardId + ) + view.tv_d_app_name.text = session.peerMeta.name + + view.btn_disconnect.setOnClickListener { + store.dispatch(WalletConnectAction.DisconnectSession(session.session)) + } + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectSessionsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectSessionsFragment.kt new file mode 100644 index 0000000000..1a55a976c7 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectSessionsFragment.kt @@ -0,0 +1,87 @@ +package com.tangem.tap.features.details.ui.walletconnect + +import android.app.Dialog +import android.os.Bundle +import android.view.View +import androidx.activity.OnBackPressedCallback +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.transition.TransitionInflater +import com.tangem.tap.common.extensions.show +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.fragment_wallet_connect_sessions.* +import org.rekotlin.StoreSubscriber + + +class WalletConnectSessionsFragment : Fragment(R.layout.fragment_wallet_connect_sessions), + StoreSubscriber { + + private lateinit var walletConnectSessionsAdapter: WalletConnectSessionsAdapter + private var dialog: Dialog? = null + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + activity?.onBackPressedDispatcher?.addCallback( + this, object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + store.dispatch(NavigationAction.PopBackTo()) + } + }) + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.slide_right) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onStart() { + super.onStart() + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.walletConnectState == newState.walletConnectState + }.select { it.walletConnectState } + } + } + + override fun onStop() { + super.onStop() + store.unsubscribe(this) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + toolbar.setNavigationOnClickListener { + store.dispatch(NavigationAction.PopBackTo()) + } + setOnClickListeners() + setupTransactionsRecyclerView() + } + + private fun setOnClickListeners() { + fab_open_session.setOnClickListener { + store.dispatch(WalletConnectAction.StartWalletConnect(activity = requireActivity())) + } + } + + private fun setupTransactionsRecyclerView() { + walletConnectSessionsAdapter = WalletConnectSessionsAdapter() + rv_wallet_connect_sessions.layoutManager = LinearLayoutManager(requireContext()) + rv_wallet_connect_sessions.adapter = walletConnectSessionsAdapter + walletConnectSessionsAdapter.submitList(store.state.walletConnectState.sessions) + } + + override fun newState(state: WalletConnectState) { + if (activity == null) return + + fab_open_session.show(!state.loading) + pb_wallet_connect.show(state.loading) + + walletConnectSessionsAdapter.submitList(state.sessions) + rv_wallet_connect_sessions.show(state.sessions.isNotEmpty()) + tv_no_sessions.show(state.sessions.isEmpty()) + tv_no_sessions_title.show(state.sessions.isEmpty()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ApproveWcSessionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ApproveWcSessionDialog.kt new file mode 100644 index 0000000000..0b6a55899b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ApproveWcSessionDialog.kt @@ -0,0 +1,35 @@ +package com.tangem.tap.features.details.ui.walletconnect.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession +import com.tangem.tap.store +import com.tangem.wallet.R + +class ApproveWcSessionDialog { + companion object { + fun create(session: WalletConnectSession, context: Context): AlertDialog { + val message = context.getString( + R.string.wallet_connect_request_session_start, + session.wallet.cardId, + session.peerMeta.name, + session.peerMeta.url + ) + return AlertDialog.Builder(context).apply { + setTitle(context.getString(R.string.wallet_connect)) + setMessage(message) + setPositiveButton(context.getText(R.string.common_start)) { _, _ -> + store.dispatch(WalletConnectAction.ApproveSession( + session.session + )) + } + setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> } + setOnDismissListener { + store.dispatch(GlobalAction.HideDialog) + } + }.create() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt new file mode 100644 index 0000000000..5fb2d1507a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt @@ -0,0 +1,30 @@ +package com.tangem.tap.features.details.ui.walletconnect.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.store +import com.tangem.wallet.R + +class ClipboardOrScanQrDialog { + companion object { + fun create(wcUri: String, context: Context): AlertDialog { + return AlertDialog.Builder(context).apply { + setTitle(context.getString(R.string.wallet_connect)) + setMessage(context.getText(R.string.wallet_connect_clipboard_alert)) + setPositiveButton(context.getText(R.string.wallet_connect_paste_from_clipboard)) { _, _ -> + store.dispatch(WalletConnectAction.ScanCard(wcUri)) + } + setNegativeButton(context.getText(R.string.wallet_connect_scan_new_code)) { _, _ -> + store.dispatch(NavigationAction.NavigateTo(AppScreen.QrScan)) + } + setOnDismissListener { + store.dispatch(GlobalAction.HideDialog) + } + }.create() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PersonalSignDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PersonalSignDialog.kt new file mode 100644 index 0000000000..789384c758 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PersonalSignDialog.kt @@ -0,0 +1,45 @@ +package com.tangem.tap.features.details.ui.walletconnect.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.store +import com.tangem.wallet.R +import com.trustwallet.walletconnect.models.session.WCSession + +class PersonalSignDialog { + companion object { + fun create( + data: PersonalSignDialogData, + context: Context, + ): AlertDialog { + val message = + context.getString(R.string.wallet_connect_alert_sign_message, data.cardId) + + context.getString(R.string.wallet_connect_personal_sign_message, + data.dAppName, + data.message) + return AlertDialog.Builder(context).apply { + setTitle(context.getString(R.string.wallet_connect)) + setMessage(message) + setPositiveButton(context.getText(R.string.common_sign)) { _, _ -> + store.dispatch(WalletConnectAction.SignMessage(data.session)) + } + setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> + store.dispatch(WalletConnectAction.RejectRequest(data.session, data.id)) + } + setOnDismissListener { + store.dispatch(GlobalAction.HideDialog) + } + }.create() + } + } +} + +data class PersonalSignDialogData( + val cardId: String, + val dAppName: String, + val message: String, + val session: WCSession, + val id: Long, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/TransactionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/TransactionDialog.kt new file mode 100644 index 0000000000..192fc3d9e1 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/TransactionDialog.kt @@ -0,0 +1,67 @@ +package com.tangem.tap.features.details.ui.walletconnect.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.features.details.redux.walletconnect.WcTransactionType +import com.tangem.tap.store +import com.tangem.wallet.R +import com.trustwallet.walletconnect.models.session.WCSession + +class TransactionDialog { + companion object { + fun create( + data: TransactionRequestDialogData, + context: Context, + ): AlertDialog { + val message = context.getString( + R.string.wallet_connect_create_tx_message, + data.cardId, + data.dAppName, + data.dAppUrl, + data.amount, + data.gasAmount, + data.totalAmount, + data.balance + ) + + val positiveButtonTitle = when (data.type) { + WcTransactionType.EthSignTransaction -> context.getText(R.string.common_sign) + WcTransactionType.EthSendTransaction -> context.getText(R.string.common_sign_and_send) + } + return AlertDialog.Builder(context).apply { + setTitle(context.getString(R.string.wallet_connect)) + setMessage(message) + setPositiveButton(positiveButtonTitle) { _, _ -> + if (data.isEnoughFundsToSend) { + store.dispatch(WalletConnectAction.SendTransaction(data.session)) + } else { + store.dispatch(WalletConnectAction.RejectRequest(data.session, data.id)) + store.dispatch(WalletConnectAction.NotEnoughFunds) + } + } + setNegativeButton(context.getText(R.string.common_reject)) { _, _ -> + store.dispatch(WalletConnectAction.RejectRequest(data.session, data.id)) + } + setOnDismissListener { + store.dispatch(GlobalAction.HideDialog) + } + }.create() + } + } +} + +data class TransactionRequestDialogData( + val cardId: String, + val dAppName: String, + val dAppUrl: String, + val amount: String, + val gasAmount: String, + val totalAmount: String, + val balance: String, + val isEnoughFundsToSend: Boolean, + val session: WCSession, + val id: Long, + val type: WcTransactionType, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index d69a984733..5daef27db2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -15,6 +15,7 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import org.rekotlin.Action import java.math.BigDecimal +import java.math.RoundingMode class WalletReducer { companion object { @@ -291,7 +292,7 @@ private fun setNewFiatRate( appCurrency: FiatCurrencyName, state: WalletState ): WalletState { val rate = fiatRate.second ?: return state - val rateFormatted = rate.toFormattedCurrencyString(2, appCurrency) + val rateFormatted = rate.toFormattedCurrencyString(2, appCurrency, RoundingMode.HALF_UP) val currency = fiatRate.first return if (!state.isMultiwalletAllowed) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index 340238f2de..16f8f2b17b 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -10,8 +10,7 @@ import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import androidx.transition.TransitionInflater import com.squareup.picasso.Picasso -import com.tangem.blockchain.common.Blockchain -import com.tangem.tap.MainActivity +import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.common.redux.navigation.NavigationAction @@ -25,8 +24,6 @@ import kotlinx.android.synthetic.main.fragment_details_twin_cards.* import kotlinx.android.synthetic.main.fragment_wallet_details.* import kotlinx.android.synthetic.main.fragment_wallet_details.toolbar import kotlinx.android.synthetic.main.item_currency_wallet.view.* -import kotlinx.android.synthetic.main.item_currency_wallet.view.iv_currency -import kotlinx.android.synthetic.main.item_currency_wallet.view.tv_token_letter import kotlinx.android.synthetic.main.item_popular_token.view.* import kotlinx.android.synthetic.main.layout_balance_error.* import kotlinx.android.synthetic.main.layout_balance_wallet_details.* @@ -184,13 +181,13 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS if (state.state == ProgressState.Error) { if (state.error == ErrorType.NoInternetConnection) { srl_wallet_details?.isRefreshing = false - (activity as? MainActivity)?.showSnackbar( + (activity as? SnackbarHandler)?.showSnackbar( text = R.string.wallet_notification_no_internet, buttonTitle = R.string.common_retry ) { store.dispatch(WalletAction.LoadData) } } } else { - (activity as? MainActivity)?.dismissSnackbar() + (activity as? SnackbarHandler)?.dismissSnackbar() } } diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index fc33c31d7d..35ff627980 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -106,10 +106,10 @@ android:id="@+id/tv_signed_hashes" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/darkGray1" - android:textSize="16sp" android:paddingTop="7dp" android:paddingBottom="7dp" + android:textColor="@color/darkGray1" + android:textSize="16sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_issuer" tools:text="48 hashes" /> @@ -132,16 +132,16 @@ android:id="@+id/tv_card_tou" android:layout_width="match_parent" android:layout_height="wrap_content" + android:drawablePadding="15dp" android:paddingTop="7dp" android:paddingBottom="10dp" - android:drawablePadding="15dp" + android:text="@string/details_row_title_card_tou" android:textColor="@color/darkGray6" android:textSize="16sp" android:visibility="gone" app:drawableEndCompat="@drawable/ic_angle_bracket_right" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/tv_disclaimer" - android:text="@string/details_row_title_card_tou" /> + app:layout_constraintTop_toBottomOf="@id/tv_disclaimer" /> + + + + + app:layout_constraintTop_toBottomOf="@id/barrier" /> + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_wallet_connect_session.xml b/app/src/main/res/layout/item_wallet_connect_session.xml new file mode 100644 index 0000000000..b7ef47ef46 --- /dev/null +++ b/app/src/main/res/layout/item_wallet_connect_session.xml @@ -0,0 +1,57 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 339ccae0b2..d84ec691c4 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -113,4 +113,44 @@ this wallet. I understand There are only %s signatures available on this card. You must withdraw all of your funds. + + //Wallet Connect + Wallet Connect + Wallet Connect Sessions + WalletConnect session opened with %s + Ooops. No Sessions. + No opened WalletConnect sessions + Open session + Card: %s + Tap card to bind to wallet connect + + Card: %1s + Request to create transaction for %2s + \n%3s + + \n\nAmount: %4s + \nFee: %5s + \nTotal: %6s + \nBalance: %7s + + Can\'t send transaction. Not enough funds. + + Request to start a session for card with ID %1s\n + for %2s\n\n + + URL: %3s + + + Requesting to sign a message\nwith card %s\n\n + Message for %s:\n%s + + + Disconnect + Sign + Sign and send + Reject + + Clipboard contain WalletConnect code. Use copied value or scan QR-code + Paste from clipboard + Scan new code