From 27efea27596ad08bafa8b0f3c9752ac2b5c8de8f Mon Sep 17 00:00:00 2001 From: rahilmansuri1 Date: Thu, 6 Feb 2025 21:27:32 +0530 Subject: [PATCH] Update content on the Terms and Conditions page and fix UI bugs. --- .bumpversion.cfg | 2 +- .codespellrc | 3 + pyproject.toml | 2 +- src/model/selection_page_model.py | 3 + src/translations/en_IN.qm | Bin 47008 -> 130423 bytes src/translations/en_IN.ts | 148 +++++++++++++++++- src/version.py | 2 +- src/views/components/receive_asset.py | 8 + src/views/ui_bitcoin_transaction.py | 13 +- src/views/ui_create_ln_invoice.py | 2 + src/views/ui_ln_endpoint.py | 2 +- src/views/ui_receive_rgb_asset.py | 33 +++- src/views/ui_rgb_asset_transaction_detail.py | 24 ++- src/views/ui_send_ln_invoice.py | 14 +- src/views/ui_term_condition.py | 82 ++++++++-- .../ui_tests/ui_bitcoin_transaction_test.py | 50 ++++-- .../ui_tests/ui_create_ln_invoice_test.py | 10 +- .../tests/ui_tests/ui_ln_endpoint_test.py | 11 ++ .../ui_tests/ui_receive_rgb_asset_test.py | 37 ++++- .../tests/ui_tests/ui_term_condition_test.py | 138 +++++++++++++++- 20 files changed, 527 insertions(+), 57 deletions(-) create mode 100644 .codespellrc diff --git a/.bumpversion.cfg b/.bumpversion.cfg index b4e8c93..c850cdb 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.1.0 +current_version = 0.1.1 commit = False tag = False allow_dirty = True diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 0000000..bf1276d --- /dev/null +++ b/.codespellrc @@ -0,0 +1,3 @@ +[codespell] +# Ignore specific words that are intentional and should not be corrected +ignore-words-list = arbitral diff --git a/pyproject.toml b/pyproject.toml index 0cea446..e70309b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "iris-wallet-desktop" -version = "0.1.0" +version = "0.1.1" description = "" readme = "README.md" license = "" diff --git a/src/model/selection_page_model.py b/src/model/selection_page_model.py index d28e04c..579337e 100644 --- a/src/model/selection_page_model.py +++ b/src/model/selection_page_model.py @@ -1,3 +1,4 @@ +# pylint: disable=too-few-public-methods """ Module containing models related to the wallet method and transfer type widget. """ @@ -29,3 +30,5 @@ class AssetDataModel(BaseModel): asset_type: str asset_id: str | None = None close_page_navigation: str | None = None + expiry_time: int | None = None + expiry_unit: str | None = None diff --git a/src/translations/en_IN.qm b/src/translations/en_IN.qm index 47bca7155417b8626df677b7a4fe0c50dfcadff7..9abaf1aef84abce40026edd1719a55c25b89ffd2 100644 GIT binary patch literal 130423 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 gY2q1~hI|nbfP8}A0cR|z+r#Sv zb&=@Y;@e&1#c{$)Q8ko=3tu)?p&>D&YLp1sX!;tJbpgvhn-|?-JVB&;jALH&8lErOIOX2VqdrF>Dr&!2ffUZ>@G(9ZV=wC= zf?giO!%1=~t!=0lP`?)A(F&dyJ$EX%qo+>}dK#kX51dCKMU# z?_x9~EAMyk6`s9doA4yS>(|8hC)VUFN}uzO`W5ny+&G@dfaH?#RBhllk0afO7c&!8 zA&A;1YNKIIk$kuYNBwV)x3Cj_4O&Y!@FjXrFw{qD$OBKfw{kfv_fAnRyAfzLf=WQ$mZZk*jYBP&PHXa!YA)@_C|2yE zoI;IrE4~n1kv^VTk%b_g@XD!Ne@(d~QtWT}x_0yx*nD44@JuI0dhn^3(SW7s7W>a# z!|{Sd^2{q9xE#oyD_10!j4Q^XVI!BNqii8@D^Ip;7y=Ut1pa+jde|xZDw6v{v4Q^#Tw*?)H4LXcqKdHF~TO3HL8fx z(in~U${fC&T8bhN?1AWm?h`w!9spH|k3=zKgDaZjqBi+z>sx`ZKn%@QW08M(rr|)e zFlUs&n$~P-ot2I>@>)dQ^9G|TMD_iM=!NqTs{uvOVy_M2abu0r{heF3gZ3=A3f2Sm zo2Vll_oo)K`_A>w-!0CtCybv)AKL4yI4$AT zXQR%o7-hunqKQ}=B;oO4CVX=-R+gmRXwN}isJ+Q}y(j$5=o_t@tai_MDyzTUcn-~h zfW6=FUw>^6zhgSoMlFur{-A^K9K7jzPwB{ho&@Mc?4MX%9$)=y@k%b37 zrB>K&Mp-l{d$HBv0d_c#{&-)3xTiak;AZ;VuFu#~VOyX=E&pn2Pn^PKxVUEBY~vtj zcNkWR;OI|xtBrgjTOP;hEodgI+-zhla{0ZzCV9&5-Iqc)M)W!N1*vS)mYS8;o2jp< z@1d4lClqVbwqFtG-}X9H10S6J*cqsNsnJq(dU61)I=H8+_0mYYFIlHRbf;@q$r^>6ap2d$Z01p<>dv|yO?MH%n6ZphyUt#1g*-Tj|cm$<|k?g7B z7Ts3_=d$Ja<70>pe1*{SC2#~AJQEQW{d(%y(p7l?@Z4=s_=l8DD{M5VgC53chUbWN z(Tbu)P5q220D2S5@31!-t?E|e*i4~2#@8GDr>{F+&H>2g74RNOX4g~}(5U1&gIRWC zhryDr8^@X>T!NB{iOPE-8+&gW;nb>Iy>2l2ZkyZq(+p!ywCZLS^J9?Qw_6*b6`F)g$n)(1L!xY10=?t-A2jLI zzCZZj(ZAaWpTVCV4D+B*M5L#K)%1G@?EO48HQ27Oo-Mkziacjmf>s%nV?hJO^c!t2 zV`-dgvq78d3_m1<9L#P0F8d3Mfd7nIoM!kuf!$!OAX*^t3v3vV+FFDhh(sei2zFCh zE|4QDuCi+mXPC%bWk3^`6Nuxr6_at*n*8) zbM46-uZ^G9w=}LWW4y9^afSIRswyTqPz&9~VmMvM_@Jd{y{-#oG>r8I-bNPEr+{3z zxr9)k(i*m8OBNluex$g8rB3TSqOh+sz?1Uz4NWH|SnCgst- z;BnfQKeN{;F3EE5ARhQzi_Zr%>n5`S$gkF>l#yX~kYD)8=b7R@8jneV1Yc1+`3P7> z?Qmv|>^L8^5s*D7%2>^+G}7L3>=kKZf7R3H@*8b7To?2Y&(@+J_)xI+Xg7L5G?rMG zyR3ZaL`ZBfYfSBQ-i1RlYjIW0uQo)I#O9G&;t2EtGWB-C#yNk|a9qtKxlXRY9of7`ifo1DBR zV^y7~#Z8(FDJg*67>*S^#Pe7ia(~FeJ#LO3| z^L(18+8M1ZgKkGJwh-$ZUN!A(z{9ODm44Z}q9(MTDRY9KW*W5f5cW3AgiTVcAwN#+ z$n03X80AA5A~>!7v}2#Pu;a-vtncYb;-3asTo$pKbVCCNC%{7i>?9T+Ujn4Qe^UP6 zJuUi6H3vJO=g^2@`#+-yub4qmO+*&tm*79v18<`%imN2uP8CIo@rf#n@<>S()CbT1 zoNiG&FGojZH;cN)>?Sv`g3 zYHuslkoA)9NjtQi-Yx-DIDkdHpy1BmzOF0Xl#b#<$%B!Z$S+o*h)rW{3K@X^;M)oc zt@SEbZJe2zm@9ospPU>FRfsq~@?V7e5@f#Ns%&}SaoxQ@g`g*Kl~?=UxP#4RjS+e0 zcW{#h1Je|8=t*uyT#lDecor{h06@5SUodKVHqOlcg1U7NxQjw7i~Gf>%7b+BUx z(rnCEI7N^0*ti(~7qcyYKUP4c9Zo@?TbU>8>gW~Gp|1~0ddZv1XBT}~-0 zR7HwoLCa!YRFdKFqBZ67rPx;a1MyFmM?0yZu+GtwcZ;!uQ$clR{51F?=|D_x3pzq= z99w`iyR7*b^)?4fR@}Gl+UpoXU%uKIX#(%@B1nk%0WZjP@kMA6nuj%GwVPVHYA@&n z^%QbQx7hS)VT%+~@($hxeZ{x7%DZ{qCB*ezUOJ5i8gXu|322V{GS|R%P6=%-Wmg(RuJhWX0EF_v=rXbl-SMWaq=tRS z9=XdRND`r~pq8$04g7O_d}jntNmg3VQ$9Mn=(3L&Ko(s$sJp^(KlcKkr=C5ZbVCb;iFCS3BGN2)wS6opNq>&bO5+Odd_>>Ik`R!tEV z5=d`GV+?Ue-H6TbYz=2| zb)?LnM;FErwe_=etL-iWfYqa2aE)X8Oi6=pQ}{t-;^_covyW1rED9<|;e0F&@?h^GYJ4pS!XfS9q#4Jq|bh{zT zl42q4J!m==vkfpUhY_4TPVndpd#O`2urZ>h*LjQl;XYM=o?-l$n>glaRaIQ9#|de` zQYvr5%3x>EM^Q_XhWCe0P+Sl-VwB$V`O~pQGoWz!q1Pjh>rz%_8)L*P`8j3KKwav$ zz>6gLsRzZV!B+<)s}2FthVXG|b0`EYy>RKS4#$i4MKSl)yszEw(ivq#?VnEF+Ad&W z1`SW>?3$>Igt_5=tXdSa1eaFHyR1olWZTLPx?F1xbWpON#{W)D#fp+FG*&f5ryYC( zT5J|RPV{oF>nX-}9Ce?u(LMKp!WRAEntH@}hAYxht^jl4iEq-m`mr^uv_o3Z3bx4e<`@g&!v@uY z!Q!6LrKbKpr~n1!HKe2X&a`UVj5g82`7De=hKr0SH^9fsV<_)XrJNoG(JbZ&5jW;T zCS&C=`X8^Sxu!}|WN-PGFO-V&s;0%bRdFOrhQx^JwQB3;lnhi$>YxazpJ(Gso`Vxr8rJ||qKrY*O;9{Z%e~ zDXl&4O*JgysrpB8Oj%o}FVv;xf~=vTuE%K<*&b9xFhBw4ozqx<>=+v1(VhH%3R6oN z4RueVOjS;r%T1ZIq%(0}_%F_$U@hTWI##<-3+D9A_}w-|AZ7bKJq9StL1agq#!=4P zD#}YBW%%xN=GHL#yyd!Gj7X7}s7Xjtxy5k(Ew}V3e9j$Y5(w}&qo##vCmS9aX^QG=lba046sgO7^0JeQI-LZ_xu74|ZwP`0LGGyig>%~MsWX{0!(k0PKi!SP^-?VyeKDSH{(kuh{-C|TY7;ty`N{1J-A|7)n zMX1v1`siM>z**vAe3JVnc|lIew=N31&pr3j)pV6*!J9OznL0MoOv%`;V29+1bRP*& z^osvfq%WBuno|7*D}_wM`FK_e(SUNVJ@y+i<$Y1YP;V8pZsar0CaLfJZFe8S^ym>sLP;b!W@O$~Ox6iz9$|{cs_DkARYrstZtFa^@;zA& zp{fN)Q5;dS-VE#L1%PjHRF(E_wf7d!UC&;iFm(>BuV_T%Of?R86ro5SWQD3JEQnaI zN4J~4<%!Jg2dPvgx8s?XvgN+VEhVU!v=&xJO!5TRbH= z7bVQ)wmo_;E_c%z9ey%dx<&_`-J2FIV_IkSDum=h)g<8cRUXA`$oO1{!Bbr1PaLb} z98;~t-xQb8AITRIQEjK%kvZF|SR!*SQLSb`rlRynVimPhIKoA#Magn6|- zqxggxRZ*Ju8s+BOlqaYxE=2Cd-92tEQm(rfgQ5{~sEy6x#ElreTxQQ4HW{Tx%i0gW zsrt`KJS$sALp0{=O%aBo1TnfS5gyZHBUM`PfXXk>QSgag-({d2I>lCstn|H6w~KUB zf;)N*?Xi3=cWGa3dUjuW;<4g1Uu(ef%1 zHkVfDEvpBo#ltpTAg zTDw8EWpQJt6LO#xG4MyOR3U_tT!AZo%DcJ{(N-h|_>mh)l4}-!#fZ7A7o)kXI31$Z z0#jgST?>s*J%}U=XraICSP>R+6s#1#7om|f(#=(F4bbJ*RY%)@Yt|euQ}jdWIvcv> zdgoEO(<|l)j!_oU z=r_U{bD5&FrFccV=-9Nscf%zS_^@9o6bDS^n(w2l_crZt$xf(kUe~sJjcepZErs^V zrWXerRCcL$-bOAp#wy&4d}_32TZ6Qh#}$or4B_Av0%>=QW)_DD@>XS^6rsDU$Z|Aa zmv(9vvsk1Z+aY zPzOqEvakL?*T#NYml9=S`u?Enq$CIb;F(FpTkjzs#PCQ7?}A;G9=JW0p2eO%#`VY! zy+%`5_a;A628|aZR%Zsk8$A=28EdW%51v8C*qKq>S*_j@GCJUvk4&Q@FsAATS7i~n z0w+~b)E5Z7CMlceKFX=)6s@Ae_JAKWR|er)MLdT*H}8k?Zg#wjM=Rc2$hBhCLv$8p zp^E!gUBQN=^%nK`h#pw9!!|?Og(Ulx?umlRZdITdRzy5PnxTmEg6mZO_qwe%ZLA5gBh(O<(F4XBTs{AzA99{}6eKSSXUghjn!)A3`BxmH-nO6-RK7j-#4@OA zq|uO9YV<@X&>8%TmehFxm9X38(8Vqd*3h9h(&3trb|rGnc_B)s`jDO_TSZf_kS&l- zfguzJI$@!BSf1$80FkefBY(VwwIjMxE>)&2a9eb0e4d#UiGh!(q!d z{*}!K6*b=`8^e2K75n4!z|#4S#-r!4KRHGzQ3^c|MlM(vW-UW2aPd80lg@Zg8&CkN z&m|d4b*))e9H~-eSBL_TrC5V=IpvF`(`HfqZ&eM^L~zFw$r?iw_=q&c^W5s~zW268 z%J|IsN(WX{J`ekg-UF5DCREXg5XgkmyG0lxjf#*!!Ll8jkv`FnAc2yLsGCqfTEFI& zHsuKtA)g^@Evo|URAFqyxk%B)&mQSoUIlUak*5A=QK51S;g>LiOqe(5#Lh;NZo zircmtg^E_J*pCzaXyla3OssVARC;2vWD)vBd2qX|n3^{vA!!8f^l@w?Y2N?or$S=S z@NhcA$Snl#%1ab6DEcd;nf0nm?S0vH?YzQLNmj6N+S4Hm&)&UA+7bCTh`uf;kI_W#2p-go?pm`M;2`{%cDfaF~g+V>E zF^iA|h&W5y7`F&%nqhgLk@pz6mK~_c9eBf0sF2nd_Sm&p>xSkoF<5~3T8h> z6#4A-%-5v2>#-NJruZ-iT-RDMr^crCt7xG_{k=_|<;|qO+eR(E4Mu3bR=?0CkH?{= zv=wQGvZoARp5QOwi#{u2EocU9MCXcTR6Hm8B8wUW`INP3j%_Op%B4m2M6nICO6oLM zy`0F7$m+I7bJcsXEJVR@k1@q{*==S@BalW~rSW!^UG@CI=~V6O>Sd*#dd+m=3WIj5 z$;O2JR7?l$s2CSdn3b$V?v=&4>;bGnYiqkzxNQ%TR~s$w5pWH28{yU*=+sN1?={5O zT$)H+QnV-7oiu9^RPrm+HP6OhQ3@L?5oNPTv#T(39NZtKT8FLiD%j3Y`6#p1dPCj~ z%o?q=TYu<-49oWu)hI*dr(;$x^GGcDmpHD-3@yQ@NlWF$(sR-jRiv0eIn5%!QE$V5 zJ4QJ?39lE+M{sj`#;8B;6Y`ix5KBMEJfN8OEHMT&U~YadV!;*U!1HSI1#)Xv_qkVA zvEaJLO#Z%jq>8H|k{AWZ0`g=$bCsGGG}8FQgx>90L zH|E*|#ep8L&(-GNIgr0~%N;T1E!T^(*sngClJ2;6NvCBk8!d-NFvu5osduIHui~Ys z(#$GuvhfsYH|qkg8oMZ8otnv~2 zzs5?vFr8TYtiHcDyTNMw+wA{?c5d4ayL!-8R(9I|SL7Ai7)3*8Hyd^0X66 zn$O$OV!Or`qg3n*Hj?@?Qb6ywWVna{R8^l8`+huMxwd3o;wsa0cBP~gb+6=FN%RS= zwTi8nLU1{hJM3=zr}xLokJG^`Wzu>1e#v5ee1$K>Lb75g%NTQDq-dYXP~(?~_SqYF zb%!16syq%)!>O>bmR3I_Gu5BpF4$7rQwA4YXa}OQb1LD>mH?%+pvH@ZmZ+tI4_O9Z$spJ2&p3`S%TKs1k}(#`lJ3?f`; z#IVKn$~n`R*fk|`t!U+%`T1s0(RUN$8c`tbvBd+XmL*-cik78poy~Oyc%gl)k@ZP1-5IJL0-18Q>t=RRr&EP8}RCJ|FSUKoRtXlqs#o`wk zc@~`@lJuMfA4Vmb&NayfUJ09>uI;H8)-7u@-0W;`dy~!rWfag|99QO)#y#pV!;Ui- zPa;A5luv=ijnz6|yDPUK*Vx-c8u8pCFp`xeTZ5Kd!8W>{;v3U<($2R?57~Ws2U!ik zpUIEZ*LWoX>3Gri$qx?N+kWbSk5Of_i2}D8_mR&CGjxNKbA}846gk8bMcOISJYd%q zfhp!l^LfWf)p4)r89l5XvoC0L^b;EWAiN5DG}VcUpRV!%KnTsj7szDmgf()a^SFQy ze4_L{=Pk)k=;PMO2M0s8j0)OsPjg`n)nt^_(AW#8OO!o6LF4_rqMkp}S1Q)+g|TZ=04~4 z`H7U+4#x?a6jvNnEveEV&FWMY-CdfsN|Nq$R!r=CN$rvM=Q9$xVX=veqU=>c;o1`E zN}ia`3>_uGvNrq<=iw-I(q7kV*EFZELs!I3G)v<_(UhD)k-6(cY9+C$ zIx`RNjK!cM#w!-@FeoSC)6#r+ekNuH+4U)U`QYN7oRB!aQdGz$ta%41s|(%M4p`T z`jVnNc(f3%P3S3=QD)o;{I~jZ#HF;`)U~6&HsCmT zy~3@d@)%J?_UVhkJWBhAMx@}S%{ZZXLIXV^w4Br7WG@AYU=Kt2lz)cUY&rs%5tptT%2 z-=G~fu_#Yiz@pXQZ+yM-6j^r7RbL0vUR1o*py?l8&36>zrJ~nt4P*H?!tr8P*hv3V zI8`Qx%9QFecvSn65&22dHq5L%3NuGu;9?yDIsa2-PXZI7)D=N=dM8LRIJ5>K< zp0fIgWI=P6)uZ6imH#7n6!CWYl*@I!|Ah~#3rlL@rLSu!Lhy0KYhoqh3Z51RL|^yY zjXqa;=QU8TL^%D+>E5jRj90)rG|PAv^F3F_a@?BD=g^kL&A#@GXQkFh%%^X%xXeDB z(VZ^)ZL~Bl1rlgpoUNHmvPR&uD;knTk_B|lP(CMliIJj1Anr=`p;gqtde|fj`0BEE zxi0VDUd$|=Q%>sr(>`vwEj<`4LUSuhPN@|v;Q*YHZN?RkiKkTY@s2LhWi~`2_3vGt zQH3U+Vx*`GvAPb{#K)fXGZQq1D~d>c%vgVPjv-A%V#Gw+eJPrxmJF|nFN<}Ba!vB} zoo154NI?B{nFUE7;;$e(OZch`!;Ce+=KpmWK;y@>@n5We_o{W)Ma|wqV&HN17o96UYRiVax?DC?rG>T%d6j8IW z8IlAfAjVu|HOlG4*+_L&MXIc`XE>6#{|b0rrd0R9G7)9*Q?r2|^u%V!gPky(v3$C! zc;82PWKJ!prkAf3x%Ez~cMlJXNM+HP41(T1(E+}zLXTv6p0mTQHSZJeUqV1`Y0c@)DocbFul8a!?kW#@z4r zs8tDMK3Y|20hJudLxi(F3t}w6#$1a06t%)Ns;uZSReRS4Y%$PdKadgk-BfL)|Ec|9 z{37o(8+>F2RxagnvdeI%V^79-Q`3>0L05T^0C6R%N0cQw_0vzgD{9;`@=v zWv{XML?vd9^eT(^9pm`aVxoMHG2&TA@JJR+K0$P$a!F0i*Bjh>1D`|Sxv#F_bs|iv zuFJzBv+*tUm?0Zz4DZGnu{u5zZzykyv9ym;USGSM?}yvCa6{5a&Fa60t}&bNNE>-{ z&w+lp7m$=9fzSZ$g6=#4Es!oGPv3yhwfgmbf7Y#Ly*-j;-Wli$HB{%NgFyWB)#o|0 zJ;;u{8>4#0h@Q%aDS}RG)iE}LQ>qD(VWwS%j1=Skh`DfQ&X{BKEO06A!pBY673o_2 zelvM7AcMcrn)>_YRbu2za?A;oZb{@tJ?qqbT3;pq@(eiTqG;hOJCdtg^bp01E8Fo4l*p#M4ph(zuISh7R4nFm z*%ObQe(LXWeUj}^2Iz8w?Zwl}FT-hdZ)ry-Z?;oIhG$juk%gCx&6zf%*~E$Tvtet9 z8K9lpcb@$3pdN4+VdIJ-7`&Y$qAF3lo+GnRcM%o=9PtHl)!*ep zU$;jZ;7Ktb@n@v>>1hO_9&^@X<8<=)-X*k()4~!frgb2oQLZX|I%068uwn7ELj&*0 zst|vRdp@79tKu=JW?>&$ZB2Ip!KK6YUUiK_@wn5X$V-oC;Gg%x$%_F8-1QhVMhWnq zF|{uO*cn^1xV4w%R1BopjOYMsL4-b&&jlYEXW(3=V}UB7Ng6q)Zz!iJ!sAsKVh18Y zb$X_6F&~x$oTbNTx${fAjL>O(rhF4zW&Kp=olbh2>=o@>##ooEa=7$3=u@6sI4j2TfCzLkYdq+xS=w3z(8 zI0tfzZY{Q2MpW0h2IjUVX3#NnZoiiwS}KQ*dWzf>b8oKj6gu*s>G7AfVJ4mM!j8)r z3sLo@7cZb94uGO~MpT^R`m#y}1u@jlANH38!RPo3JE^$_>j{tP4!^;+NJ%z_< znNoj~jS+Q*-*_wa*5E2@M?8qWdDN4Rqu5f`45)ZZ^b~xOl zw|T$4v&-(@V0Y5}*|B?A+UPNzxLZ}Jjj~bfc^WiXf+FPd(K8}v(XK?>?6Ijwk*b+! z<+rhFjed}6A|>dEPB!%2AIOlp(D6LPbI70jd)a)KH@GkT1y)sP^oFD(l`H4(J{yZl zE+a{IciC^yp>dytdOEzd`dlVyYRcVX+2hge=rcDP-`_Q#r*-bSKi&2S3?7Z(^ zxn#e>Mtky5zFXmdz0YV{ZKNB6e&1qOI8EVt`#0`T*lK^H-CP?q9Y(+XyalxXy4rQU zv(Lt756=PnS5L;h5(n&QX3+V~2W-|jOB|Wsct*P^c7?f%Z!k7ChB~cwTO2Sbz$03o zXBoa*Z8lDbI2`0+pN+WRp2J? zNgEyNt%iT3fX=G8+1}V;v+uBX_S(F{dSiCJ%?vJ#dw%UI&-nGNwlf3{s|E+BXM%Y%9AZT(+;A``mC2%qBc6*na zChzaI|H^bo@+#^Tv37dZV_u}6@9S}W01r3@&7`5*gYIaD5HP2*33P)-3HAm7iDV9b zLK*fCv2zF+1#c+6FUadYqc;1F#Q7cB69e_-t@l{X3x9o`J%QO59c|7Nl|v) z{>yJbU;KgmUrHP40QZmqe+yV#W}pty#ZC4Tn|L^xJ?i1*1{EhuRmHfzDx!GZd#dK_ ztyr=1o|n^hwV1Qa+7+@B)sLCks|#dKF|&wOZLSx;LlxI8poquZWCmmlzRRu(2XZ(d z@u*snz2_R`H3-&>$TG<8!Pehx1)paX{(LbP)m&hX9(Z1=l`Lij&qN$U|2PqHlG0!-ApRQ1rmj@^D6HTELR z<}t)#pek~knor7S;jPNw!;u$ZNiOQuv9h|t;<)6sfs|XU&v+&Y?a&TY%SP?a;_0L= z%9MLrw;@lAT#6MQ$xv!#ih7Q}9W)+BD1n}R%#%UNwYDp$kfNv0QWoME9#(QWDHi^4*=K5Nptg+r?G`B@iP1@^vrP>@a z?p{RJpPsLJ$8e04$vJ=5>r;8o_uc7gmadW6|9>ZbiB~&ZwSN+d)t*|m z$8W^VdQVQdiQA-q0 zQ9#d?C`&|O7WN%`3*e!MUfHx2030NV2-ezS^ zZ>L~4fwT9kB1m9A?wf7=Vd6*hC>8yV8TC^uP_CB8Zm~!A=TyD|j@0>_wdT#Sm*Ab= z`l55}*}X$jNJbf2U2TF^`}A|ODeKO-?Z^RJS#NBa3=Edir zi*;Lm21%SCu+I-J@Ve{;oQMLcFRhry<^l^*KOWND7NXGe-tKjjT zV@s(M4iz!*1Yi-%RD0w79#c)}HNhrcZh9J9+eW{!>bu96BTgJ*#pWDTW601G7O^yU zD6wPbNuxRYANU>o+_dM>n7R%c=DEtr4YXSRjlJ7QMmdB7W*;iM+i&g`0eH#KhnBH} zWJs(vTetAk@humwoYri?&HRpWX_)`p=L04g*=U6XGMY2>W(3LC(yC zB=+$oGNA(Bp@%Ol^q#$6k#-zphs8z5{nI`DJTs-8&&C#Ae3mwH!l>v1{Z@_pRWvw< z>#9fH2bpXGG#Dk+`naEkg-v&!n7tnMdt?9kVH?4x+P`V<`u(`Bob*pu&$7A+AKrEi zAKuy{s75Xi2VV?Y=f9gJew{sO@x8t|-q#KargyiZBfKVA2b!nbnz$y0-*30huoo5n z4(mc3LT$Pfu}Jj_FM9GID7e*6#&a-BQ5~;aIa}=ju?obt?pyi7AN8@(gq?Kk0n(#` zPPB!cdI~_4#@{1bX2MUoiv&u0&%^I}v3QmA_V^2LsV8!Ycv}8a_J&2+yUm;7ziu;# z&yy~mC*Qzl!B5W-Xr`v(Z}Q?3K6Bq%Piv2^pbOfWE&7FrR`Im^dv`e9eP0rpmJvr^ zKsL3%*C)}*ZmTg0?ZTZqONdTjOOGHLcp5}keFi0u+o$$O(ow%0J~Q7hxn|Dv0mVL* zo9M$IGafXk7ZAc+NF?7lqgt9#!mm1tFZG>v0l#KN&vd?_dkNn4jxGYLSw%g5t_%{iR@G}bHbCr4H72Xi&w*M_a~*X zp?8|au}Xv`A?G@?epurC1&o_Z^<8DCE03V>dbc4OYK;DV^CKgKuILwE1gHF)3>EBg ztGu?yCV4ntIh7f)Q5%mUo|Nz z!_f1fy@Rir`jxSjecHw}CdiYXKomY4Wch5M-#ieDc-W|^vnN@x(L#(G_&j`J^-Ieu z8`#v6$DR*J)E;c5n7Nlz3Mz&_<}l#CED3>SZjMCma|6b!}r!; zex%It2U>;*m`Wfqz)lQYzKHcOI{3psA@ANB*UVFY6Ruz67Br>ysL34bqz8a^;WV;m zr^fgmbE!PB@o{tmy;Isg9>%3VMx8mHbv!Ea#Xf?b9*02-I%h0AfmRzYHAa%ugDrH5 zCl`f>zsD@^+qJ5>(Ex}fTflaLPUe+jVo~c0xBdXOO0-uFNEYqSccsm6>;p0f9AaP5 z^{S z)-J_ORnEvH76ZCtCsVxa*EZ$<#xkifs7RI1wCYg;Uk;M1=Tqr$rtEGCll!5o@aMSh z2)8#m-b?XCdlre~;jr6vTRnhA-ovkfC+H!YK`zDxbjfzekdKU{B9dVZh56chObua+yaRpS}#vkXWC?#VhqO?8QCLVXOSG(fNx; zsd_D0_p+SYgc2yLGNvM_=)3QhDp5QYH)^NcA+3+O>?KQPXKSkyUbx^td&D3%NUtsN;MQ9mvnvNVMBkAXar;w@jn-1_p?Ex z3KgD@NyscSINOlgF~(&rd}}G=Inw z+f$>OOD$Zn8AN4Hv_3xboWB@H_urlh?NoEmUTaSLYc$dA@x`AOXdcLM7cxr zAYZJkmzgI4qZec3xITB~ms+ zKL`w$vCcW&$oGqArtHAzIzHbXFVnA|MrQM}l7f039-zFW;u=fEN+*rp&GJckY0La`HAkVs4QeOJJG1P*<7>8sU+OHJ)Z)2j$I~!}x-o_WLP< z#F8Ig9OT$R2D9ali8gcYAJ!)b!9D%2>VKP4h2&i2gKun++ISh)mkbeXOH)i6N2bJKrx8{h;0I zsa3WipT?Rz3oTG+Hi!=xS5N6BjCT!W=JczAGrv14^<2%NZWw)e9&%<*Wl{$cr^$J- z31)|GM#!AWJf|4*ssCtCi;Mb(EU17cc$Die3ca_xfqXmtjOVwXj;-(z_l4g06|)@d z$o!a@&20+v$!a6{WL^9AV~(_MyA|1b3oK++d;4tn_{5%!AKoF6GbD_On8yX(`YKS$6L<*TMS9w=zDp@#zoH1+Pt-CW;~@vmbbmI`(C;Sm)VsG>um_m)xH&~#r?VO3--geBF;M5p zeolD9Bb-Ag)NpRw)7iPP!cm+DnQ*@W9Q#zSwVXm%H%QgD!o|-AnX()1U)$f*1S2v7 zsLgXZbDy`pZ-QGbJpF&y-6qP?Q^Xm!ZeGYN+?k-(J@M}>NCMu_{Acao#iw_kNSpF< z-`?Rn8hWdEfB23s5X-$VND(=5cYyaD%rmz;JliKS%U z#KU(7oV`2n&rVfP0Tzf`-UG`@^dYD7g<}=cpHIA_` z-Zjl$mgjAprMy)=!6*5Tk(OMm@>sY{9spn7Y-W~Jt8#tT1QvQ4$9=kaEhBAFkeP}6 za1IA|^P%mrKZc*!M@wa{6`Z$`u)kDr<$K`EGXZc+9i7hCW&2I6hMwwNmJ}GjWKcYm z1gs?DIiX7WSRD<=RR_45wy%vbB`-GreWbU4lbN0k#9Q}|duX-7F{U-LkIX6)r2qah z*b(24nzbRXl51)vC9{NZJQweBDuXlcdZ}1*pN|`Tf|A3wKhIq-4_-x>`yiPek0^%Y zp^z^=Sf5HQGL2VLKUnCR%bb&4r*`eD*Zw|FDuC+G%}G>`(61CON!PUpQGzvr>qP8S ziAbN_-Ry@1A!Pa4OA^kVb&9%v2fH@h$B(Im^jZ~t#(4y`XEbJ&#eNHs8hu48M)j<| ziHCVjfk!k8J!l3@Fmt-7whn{M18_fY|WZ2SQiHviPI1^s760nD=l*|JZp5~7tGz_ufdo(wsV_{q|fi{#b0j`l?TUDM+$dE08maPjXaW#L{9Tr zT#t@9G|A~7&ht9o{jrqVCVnp5&ZEN7$64{zbBEX5FaxThEu|H$INFl8^NEaD85waO zzvPN~VH0$?+bq>WWw0PbuQ;{?s`h$0y0ZEm=r zZ^B>t8f8f`(HolPEj_iBOJ$CUgmys6&Yg{D*3TE+_qkzlVWvgBsonY}9wBy{-AecX zd66F1sa>10R_b>!&r2ZEtQb_3?E61B!&_AKsE->XHF|beb!9y@@;r~|ui`g9d&-*Z zLz{mQiSdSh){KPl_D`j=zWh$h{HgKLCmtR>BhjyhS+9m_IG*jx_P73gi8qD5)9MBE zWHg}8U>sKe`dt6B_Pe`o^tnb3ZI!=a;d#s2${O<1MmhO`-|9hL^mn&3X=&DHc9HS# z?qBaQsg}gu)O(t7;Enh5GHHun_@a#+Ux1A=JM&egjMU=c@)mU`5i^xM5((#8zPTK+ zO?-_ri+y?z^=Ch7v!fqt-?231^3z6zC`7)XaNOA$$D--X@l}y*ybCfwQf4(sg{l}{ z(3&z~CU%IzJ%1}6{hfD}DDQwk_7;QLzV5Xy=F{41&YP)F_*s97J&&z2@xz(0h5O|7 zR)x_ec{^Uc@)th2?-@8|t~fE~0!uO94=>P<&%TSNvBl(m7W^8T0BPwQf$*AtPW!!` zxTc`bFMgp@tIK&PGOeP1UhYL>q+j~0ch%e+|HjmQHwNRxCk64(8*TV7{U+6)2+Y(}A!<$gFIhd!^LluvYD=!53R zl4rmvKWoLmX>VQ+lvKYovXC3Nv=^a9^@?=HT^T|)Q;Iwlqu+I1P;AdrsQfd3JeO#t zM{f^XD*Uu)AanAwzDm|_=}e(p}%XjP8=X@}}r3FWS=t$NGr7^rlsYD>PmH5bVSjjAmqz zP-{(_E-sX$!eAtxAYZKAS$*V`>#2li{MMho(*E}+?Vq={>&s_XtE0`y>iB$fa=JY| z-(2zIcD>ntzdi8N)63JV)y>h_*?PM=SzkTfo?raY_u3!&P6r$yQ)b?Md){u2A3MkS zlhxJw^4rtn_2Rg%4;>V;eyn%98|C}k_0@K>UMA`vhE_K!V&ip6!z63|PCABaF-?Ne z_LaZhP4edUo1@Ls)${Y~)syo}{(HH)y1uwLzg(X8jW(|-j^#>A7ItI)XnXpsk$&{- zVs&=(czw3W{i|&tD$|C7-2i{QJ>MQJzxHOm*0}rm0HRxW^Vm^k#<%DEcVqr|dwI0EIywe}t0(LA)#A8s9UQlP zqtR~6*ZVO~PZmdn>p4oVcBB2U8*Q070Fkj=cdb!=FpTnKeYrfu{RQ`f?RT4dVC&ay z{}md*gBvIDnC`|redWK}-+JA+(?jn<7r(N3fJIc|u09Oty`6mCyxtr=K3lK0=d0tR z<8Rih(@pazM`x#3%QpX3t3o%oaklCSZlU}2E1&O1fBky%`0R9ZvOZZ|Z@)WV)a3`m zi1riA0TN3>H9m{n3-3x4J=x9k>h)&*-NpKNyI#i2M~yyZX|`dn(z=vy)k-WP_uJju zx|`*v*PExC^PA1!Y%d$?S9iekgWYtWwYH)PG>tYKSy;_a_C_W<+ccegdw!P_{$w{7 z!IRPlMbjoCbp%3y00({6e!rX9w=(~B{j53Dqs!;3$AJKwuPG1j*tn5q>Nlo z*{kif>&@Bu@zaKm&H2eP-hMWq!}WJFXfnL!x_8?7;q~U~qJ_X!!|t-{|Lsmx<4n|C z410Sz1RHPWshAHN@vMd+5o%pe{BIf7j-F;&w{fWP_xR}e>Gj3x+vY)Q`r_nhyI%3HMe-Sl?~MDrzq~m*-44kbR!`*74J>N?pPSC9jmg1*kGo@* zrZ%l3S$vQG@1}oam_AdtEbop)@vjCp?W!=}7XuAfjRJf?#h@SEX51G|D>vtt%Yo?i zHYlFhQogL~wVU`D2TmcXWO$sDjnm>Jiyu4nK!@&4#`#-VCxPy1ix`gEwl6RoYC z5}hjpfFiqS?ypUUX`gOuG-3uj!d&r&U#d~j6kOsy@zJkY(iP>AA2Y`>EvL(PXQvVc@jm`tn zEGK&G;b^c_!t_kM_sfd65Rtip3G{_T=5FS&k4eS)>d@s5=lmDi&)QqOnH$z>=cl}R zR#8OQucC(*3cQEFtQRz?NAOI=MgP*B(bxj@Yzjym4^Oc!z?1Tx8R-4nfODi2t1(v~aYH}ghEKo{Z)Ie@f7lz+@aV|Z`@%ActD*^ltUaPGLwDXmJe)G-Ic6D=lcD8y< zX>fdY-E#TGiTGM>Q|$fZ3J@T!Ti4l`)+k~IxCT@EmXhy?c>U(`eDn3{UVin>+b{UR R6ehFn#(Qn(5!^Z4{{mTD<$wSH delta 4323 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 terms_and_conditions_content - This is a multiline string explaining the terms and conditions. -Please translate each paragraph accordingly. -Lorem ipsum dolor sit amet consectetur. Tempus interdum volutpat ultrices tortor nibh malesuada cursus augue ultricies. -Cursus magnis cursus vel egestas sed penatibus euismod. Augue hendrerit donec eget fermentum condimentum et diam quam. + TEST NET IRIS WALLET TERMS OF USE -Sed lacus eu sit id tempor aliquet sit gravida. Magna dui tellus orci augue dui porttitor. Volutpat sit leo sit elit ultrices semper non. -Vitae id nibh viverra porttitor sed enim augue gravida. Arcu at hac lorem viverra mauris cras. Nunc ac sodales a ut sit orci erat. -Nunc nullam mi tellus turpis neque. Tellus consectetur ut sollicitudin diam. +Effective Date: October 24, 2022 + +IMPORTANT: These Terms of Use (as amended from time to time, the “Terms”) contain the terms and conditions that govern your use of the App and is an agreement between you and Bitfinex Labs Inc., a company with limited liability incorporated in the British Virgin Islands, or any successor or assign (together with any successors or assigns, “Bitfinex Labs” or “Labs” or “we” or “our”) (each of you and Bitfinex Labs being a “Party” and collectively the “Parties”). Please read these Terms carefully before using the App. By using the App, or clicking a button or checkbox to accept these Terms where that option is made available, you accept and agree to these Terms and any additional terms and conditions of use issued by Bitfinex Labs from time to time. These Terms should be read in conjunction with the {0}. If you do not agree to be bound by these Terms or with any subsequent changes, you may not use the App, and if you do use the App, you will be bound by these Terms. Your only recourse in the case of your unwillingness to continue to be bound by these Terms is to stop using the App. + +THE APP OPERATES ON THE RGB TEST NET PROTOCOL AND TEST NET BITCOIN BLOCKCHAIN. +THE APP IS AN EXPERIMENTAL DIGITAL ASSET WALLET APPLICATION FOR THE EXPERIMENTAL SOFTWARE PROTOCOL, RGB TEST NET PROTOCOL AND THE TEST NET BITCOIN BLOCKCHAIN. THE APP AND RGB TEST NET PROTOCOL ARE SUBJECT TO FAILURE AND MAY CONTAIN DEFECTS. YOUR TRANSACTIONS USING THE APP MAY NOT BE SECURE AND MAY NOT BE PROCESSED AS YOU INTEND. +ALL USE OF THE APP IS AT YOUR OWN RISK. YOU SHOULD NOT USE THE APP TO SEND OR RECEIVE TEST NET BTC OR RGB TEST NET TOKENS THAT YOU ARE NOT PREPARED TO LOSE ENTIRELY. +BY CLICKING ON THE “ACCEPT” BUTTON OR USING THE APP, YOU REPRESENT THAT (I) YOU HAVE READ, UNDERSTAND, AND AGREE TO BE BOUND BY THESE TERMS; (II) YOU ARE OF LEGAL AGE TO FORM A BINDING CONTRACT WITH BITFINEX LABS; AND (III) YOU HAVE THE CAPACITY OR AUTHORITY TO ENTER INTO THE TERMS. PLEASE SEE SECTION 2 FOR DEFINITIONS OF CERTAIN CAPITALIZED TERMS USED IN THESE TERMS. +In addition, you represent to us that you are not subject to Economic Sanctions or otherwise designated on any Sanctions List. +PLEASE REVIEW THE ARBITRATION PROVISION SET FORTH BELOW CAREFULLY, AS IT WILL REQUIRE ALL PERSONS TO RESOLVE DISPUTES ON AN INDIVIDUAL BASIS THROUGH FINAL AND BINDING ARBITRATION AND TO WAIVE ANY RIGHT TO PROCEED AS A REPRESENTATIVE OR CLASS MEMBER IN ANY CLASS OR REPRESENTATIVE PROCEEDING. BY USING THE APP, YOU EXPRESSLY ACKNOWLEDGE THAT YOU HAVE READ AND UNDERSTAND ALL OF THE TERMS OF THIS PROVISION AND HAVE TAKEN TIME TO CONSIDER THE CONSEQUENCES OF THIS IMPORTANT DECISION. + 1 INTRODUCTION + 1.1 These Terms, together with the incorporated and referenced materials, constitute the entire agreement and understanding between you and Bitfinex Labs with respect to your use of the App. + 1.2 These Terms may be changed by Bitfinex Labs at any time and without prior notice to you. The modified Terms will be effective at the time they are posted and available through the App. You should check back often on the App to confirm that your copy and understanding of these Terms is current and correct. Your non-termination or continued use of the App after the effective date of any changes constitutes your acceptance of these Terms, as modified by such changes. Your only recourse in the case of your unwillingness to continue to be bound by these Terms is to stop using the App. + 1.3 Use of the App is void where such use is prohibited by Law, would constitute a violation of, or would be subject to penalties under applicable Laws, and shall not be the basis for the assertion or recognition of any interest, right, remedy, power, or privilege. + 2 DEFINITIONS AND INTERPRETATION + 2.1 In these Terms, the following words have the following meanings, unless otherwise indicated: + 2.1.1 “App” means the non-custodial mobile application Digital Asset wallet software named Iris and any related Supported Asset Network components managed by us and that you may access through the App; + 2.1.2 “Associates” means Bitfinex Labs and each and every one of its shareholders, subsidiaries, any subsidiaries of its shareholders, liquidity providers, directors, officers, affiliates, employees, contractors, licensors, agents, partners, insurers, and attorneys; + 2.1.3 “Digital Asset” means any digital asset which is a digital representation of value based on (or built on top of) a cryptographic protocol of a computer network, including the RGB Test Net Protocol. + 2.1.4 “Economic Sanctions” means financial sanctions, trade embargoes, export or import controls, anti-boycott, and restrictive trade measures enacted, administered, enforced, or penalized by any Laws; + 2.1.5 “Government” means any national, federal, state, municipal, local, or foreign branch of government, including any department, agency, subdivision, bureau, commission, court, tribunal, arbitral body, or other governmental, government appointed, or quasi-governmental authority or component exercising executive, legislative, juridical, regulatory, or administrative powers, authority, or functions of or pertaining to a government instrumentality, including any parasternal company, or state-owned (majority or greater) or controlled business enterprise; + 2.1.6 “Government Approval” means any authorization, license, permit, consent, approval, franchise, concession, lease, ruling, certification, exemption, exception, filing or waiver by or with any Government necessary to conduct the business of either Party or the execution of any Wallet Asset Transfer (defined below) or any transaction entered into under these Terms; + 2.1.7 “Government Official” means an officer or employee of any Government, a director, officer, or employee of any instrumentality of any Government, a candidate for public office, a political party or political party official, an officer or employee of a public international organization, and any Person who is acting in an official capacity for any of the foregoing, even if such Person is acting in that capacity temporarily and without compensation; + 2.1.8 “Law” means all laws, statutes, orders, regulations, rules, treaties, and/or official obligations or requirements enacted, promulgated, issued, ratified, enforced, or administered by any Government that apply to you, Bitfinex Labs, your Receivers, or the App; + 2.1.9 “Losses” means, collectively, any claim, application, loss, injury, delay, accident, cost, business interruption costs, or any other expenses (including attorneys’ fees or the costs of any claim or suit), including any incidental, direct, indirect, general, special, punitive, exemplary, or consequential damages, loss of goodwill or business profits, work stoppage, data loss, computer failure or malfunction, or any and all other commercial losses; + 2.1.10 “Person” includes an individual, association, partnership, corporation, company, other body corporate, trust, estate, and any form of organization, group, or entity (whether or not having separate legal personality); + 2.1.11 “Prohibited Jurisdiction” means any jurisdiction subject to a comprehensive embargo by the United States, the British Virgin Islands or the United Nations, which comprise as of the effective date, Iran, the Democratic People's Republic of Korea (“North Korea”), Cuba, Syria, Crimea (a region of Ukraine annexed by the Russian Federation), the self-proclaimed Donetsk People's Republic (a region of Ukraine) and the self-proclaimed Luhansk People's Republic (a region of Ukraine), including Governmental Authorities of those jurisdictions; + 2.1.12 “Prohibited Person” means any the Government of Venezuela; any resident of, or Government or Government Official of, any Prohibited Jurisdiction; and any Sanctioned Person; + 2.1.13 “RGB Test Net Protocol” means the open-source software protocol implementing a smart contracts system that uses client-side validation and operates on top of the test net Bitcoin blockchain as a Layer 2 solution, a version of which is utilized by the App. + 2.1.14 “Receiver” means the receiver of Digital Assets through the App. + 2.1.15 “Sanctions List” means the “Specially Designated Nationals and Blocked Persons” (‘SDN’) List and the Non-SDN List, including the “Sectoral Sanctions Identifications List”, published by the Office of Foreign Assets Control of the U.S. Department of the Treasury; the Section 311 Special Measures for Jurisdictions, Financial Institutions, or International Transactions of Primary Money Laundering Concern published by the Financial Crimes Enforcement Network of the U.S. Department of the Treasury; and, any other foreign terrorist organization or other sanctioned, restricted, or debarred party list published by the Financial Investigation Authority of the British Virgin Islands; + 2.1.16 “Sanctioned Person” refers to any Person or test net Bitcoin blockchain address that is: (i) specifically listed in any Sanctions List; (ii) directly or indirectly owned 50 percent or more by any Person or group of Persons in the aggregate or test net Bitcoin blockchain address associated with such Person or Persons that is included in any Sanctions List, (iii) the Government or any Government Official of any Prohibited Jurisdiction; or (v) whose dealings with Bitfinex Labs and/or the User is subject to any Government Approval or otherwise sanctioned, restricted, or penalized under applicable law. + 2.1.17 “Sender” means a User who sends Digital Assets through the App; + 2.1.18 “Supported Assets” means, as of the effective date, Test Net BTC and Digital Assets created through the RGB Test Net Protocol. + 2.1.19 “Supported Asset Networks” means, as of the effective date, the test net Bitcoin blockchain and the RGB Test Net Protocol. + 2.1.20 “Test Net BTC” means test net Bitcoin held on-chain, on the test net Bitcoin blockchain; + 2.1.21 “User” means any user of the App. + 2.1.22 “UTXO” means unspent transaction output. + 2.1.23 “Wallet Address” means the alphanumeric identifier that represents a means for holding, storing and transferring Digital Assets, on the test net Bitcoin blockchain or RGB Test Net Protocol. + 2.2 Extended Meanings. Unless otherwise specified in these Terms, words importing the singular include the plural and vice versa and words importing gender include all genders. The word “include,” “includes” or “including” will be interpreted on an inclusive basis and be deemed to be followed by the words “without limitation.” + 3 APP + 3.1 User Requirements + 3.1.1 Users of the App must: (i) not be Prohibited Persons; (ii) not utilise the App to facilitate any Prohibited Uses; and (iii) assure any Receiver is not a Prohibited Person. + 3.1.2 You may use the App solely in accordance with these Terms. You shall not take any steps to circumvent, avoid, bypass or obviate, directly or indirectly, the intent of these Terms. + 3.1.3 Your use of the App allows you to (i) store Supported Assets; (ii) view information that are part of a Supported Asset Network; (iii) process Wallet Asset Transfers; (iv) a dispenser for Test Net BTC; and (v) additional functionality as Bitfinex Labs may add to the App from time to time. + 3.1.4 You own and control Supported Assets held in your App. As the owner of Supported Assets in your App, you bear all risk of loss of such Supported Assets. Bitfinex Labs shall have no liability for Digital Asset fluctuations or loss associated with your use of the App. At any time, subject to outages, downtime, and other applicable policies, you may withdraw your Supported Assets by sending them to another Wallet Address utilizing the applicable Supported Assets Network. + 3.2 Limitations of App + 3.2.1 We may permanently or temporarily terminate or suspend your license to the App without notice and liability for any reason, including if in our sole determination you violate any provision of these Terms, or for no reason. Upon any termination, (i) all rights and/or licenses granted to you under these Terms shall immediately cease and terminate and you shall forthwith cease the use of the App in any way whatsoever; and (ii) notwithstanding the foregoing, you will continue to be bound by these Terms. + 3.2.2 The App is intended solely for proper use of Supported Assets. Under no circumstances should you attempt to use your App to store, send, request, or receive any assets other than Supported Assets. + 3.2.3 To use the App you must have a mobile device that is compatible with the App. Bitfinex Labs does not warrant that the App will be compatible with your mobile device. You may use mobile data in connection with the App and may incur additional charges from your wireless provider for these services. You agree that you are solely responsible for any such charges. + 3.3 Google Play Store App. The following applies to the App you acquire from the Google Play Store (“Google-Sourced Software”): (i) you acknowledge that these Terms are between you and Bitfinex Labs only, and not with Google, Inc. (“Google”); (ii) your use of Google-Sourced Software must comply with Google’s then-current Google Play Store Terms of Service; (iii) Google is only a provider of the Google Play Store where you obtained the Google-Sourced Software; (iv) Bitfinex Labs, and not Google, is solely responsible for its Google-Sourced Software; (v) Google has no obligation or liability to you with respect to Google-Sourced Software or these Terms; and (vi) you acknowledge and agree that Google is a third-party beneficiary to these Terms as it relates to Bitfinex Labs’ Google-Sourced Software. + 3.4 Digital Asset Transfers + 3.4.1 Your App enables you to send Supported Assets to, and receive Supported Assets from, other Waller Addresses, including Wallet Addresses controlled by third parties. Your transfer of Supported Assets between other Wallet Addresses you control and to and from Wallet Addresses controlled by third parties is a “Wallet Asset Transfer”. + 3.4.2 Use of the RGB Test Net Protocol requires that the User maintain a UTXO in the Wallet Address associated with the App. It is therefore necessary to transfer Test Net BTC into the App before conducting any Wallet Asset Transactions. We recommend Users only Wallet Asset Transfer a small amount of Test Net BTC into the App at any time. + 3.4.3 Once a Wallet Asset Transfer is submitted to a Supported Asset Network, the transaction will be unconfirmed and remain in a pending state for a period of time sufficient to allow confirmation of the transaction by the Supported Asset Network. A Wallet Asset Transfer is not complete while it is in a pending state. Pending Wallet Asset Transfers that are initiated from the App will reflect a pending transaction status and are not available to you for use in the App or otherwise while the transaction is pending. + 3.4.4 When you or a third party sends Digital Assets to the App from another Wallet Address (“Wallet Inbound Transfers”), the person initiating the transaction is solely responsible for executing the transaction properly, which includes ensuring that the Digital Asset being sent is a Supported Asset. + 3.4.5 When you send Supported Assets from your App to an external Wallet (“Wallet Outbound Transfers”), such transfers are executed at your instruction. You should verify all transaction information prior to submitting instructions. We do not guarantee the identity or value received by a Receiver of a Wallet Asset Transfer. Wallet Asset Transfers cannot be reversed once they have been broadcast to the relevant Supported Asset Network, although they may be in a pending state, and designated accordingly, while the transaction is processed by network operators. You acknowledge and agree that you may be required to pay network or miner’s fees in order for a Wallet Outbound Transfer transaction to be successful. Insufficient network fees may cause a Wallet Outbound Transfer to remain in a pending state outside of Bitfinex Labs’ control, and Bitfinex Labs shall not be responsible for delays or loss incurred as a result of an error in the initiation of the transaction and have no obligation to assist in the remediation of such transactions. + 3.4.6 We do not charge fees for the App we make available to you. There is no assurance that gas or other fees will not be charged by the RGB Test Net Protocol or any other Supported Asset Network. + 3.4.7 Bitfinex Labs does not control the Supported Asset Networks and makes no guarantees that a Wallet Asset Transfer will be confirmed by a Supported Asset Network. Bitfinex Labs is not responsible for the availability or legitimacy of, the content, products, assets, or services on or accessible from Supported Asset Networks. Supported Asset Networks may provide access to Digital Assets which have high risks of illiquidity, devaluation, lockup, or loss. + 4 RIGHTS AND RESTRICTIONS + 4.1 Subject to the terms and conditions of these Terms, Bitfinex Labs hereby grants you a non-exclusive, non-transferable, revocable license to use a compiled code copy of the App and the Bitfinex Labs Content on one mobile device owned or leased solely by you, for your personal use. You may not: (i) rent, lease, loan, resell, sublicense, distribute or otherwise transfer the App to any third party or use the App to provide time sharing or similar services for any third party; (ii) remove, circumvent, disable, damage or otherwise interfere with security-related features of the App, or features that enforce limitations on use of the App; or (iii) delete the copyright and other proprietary rights notices on the App. You acknowledge that Bitfinex Labs may from time-to-time issue upgraded versions of the App and may automatically electronically upgrade the version of the App that you are using on your mobile device. You consent to such automatic upgrading on your mobile device and agree that the terms and conditions of these Terms will apply to all such upgrades. Any third-party code that may be incorporated in the App is covered by the applicable open source or third-party license end-user license, if any, authorizing use of such code. The foregoing license grant is not a sale of the App or any copy thereof, and Bitfinex Labs or its third-party partners or suppliers retain all right, title, and interest in the App (and any copy thereof). Any attempt by you to transfer any of the rights, duties or obligations hereunder, except as expressly provided for in these Terms, is void. Bitfinex Labs reserves all rights not expressly granted under these Terms. + 4.2 The Supported Asset Networks and certain elements of the App are offered under an open-source license. Open-source software licenses constitute separate written agreements. To the limited extent the open-source software license expressly supersedes these Terms, the open-source license instead sets forth your agreement with Bitfinex Labs for the applicable open-source software. + 4.3 All materials and content on the App (the “Bitfinex Labs Content”), and all intellectual property rights related thereto, are the exclusive property of Bitfinex Labs and its licensors. Except as explicitly provided herein, nothing in these Terms shall be deemed to create a license in or under any such intellectual property rights, and you agree not to appropriate, sell, license, rent, modify, distribute, copy, reproduce, transmit, publicly display, publicly perform, publish, adapt, reverse engineer edit or create derivative works from any Bitfinex Labs Content. The Bitfinex Labs Content is protected by copyright, trademark, trade secret and other intellectual property or proprietary rights laws in various jurisdictions. All rights not expressly granted to you in these Terms are reserved by Bitfinex Labs or its licensor(s). Use of the Bitfinex Labs Content for any purpose not expressly permitted by these Terms is strictly prohibited. + 5 PROHIBITED USES +You agree not to use the App to engage in any of the following prohibited activities: + 5.1 permit the transmission of any Digital Assets through your App from, to, or for the benefit of, any Prohibited Person or utilize App for the financial or other benefit of a Prohibited Person; + 5.2 use the App in order to disguise the origin or nature of illicit proceeds of, or to breach any Laws, or to transact or deal in, any contraband assets, funds, property, or proceeds; + 5.3 use the App if any applicable Laws prohibit, penalize, sanction, or expose Bitfinex Labs to liability; + 5.4 use the App to facilitate, approve, evade, avoid, or circumvent any applicable Laws; + 5.5 use the App to evade taxes under the Laws of any jurisdiction; + 5.6 trade, obtain financing or otherwise transact through the App, with anything other than Digital Assets and Wallet Addresses that have been legally obtained by you and that belong to you; + 5.7 take advantage of any technical glitch, malfunction, failure, delay, default, or security breach; + 5.8 subvert any restrictions set out in these Terms; or + 5.9 violate, promote, cause a violation of, or conspire or attempt to violate these Terms or applicable Laws. +Any use as described in this Section shall constitute a “Prohibited Use”. If Bitfinex Labs determines or suspects that you have engaged in any Prohibited Use, Bitfinex Labs may address such Prohibited Use through an appropriate sanction, in its sole and absolute discretion. Such sanction may include: (i) making a report to any Government, law enforcement, or other authorities, without providing any notice of you about any such report; or (ii) suspending or terminating your license to the App. In addition, should your actions or inaction result in Loss being suffered by Bitfinex Labs or any of its Associates, you shall pay an amount to Bitfinex Labs and the Associate so as to render Bitfinex Labs and the Associate whole, including the amount of taxes or penalties that might be imposed on Bitfinex Labs or the Associate. + 6 YOUR REPRESENTATIONS, WARRANTIES AND COVENANTS +You represent and warrant to Bitfinex Labs on the date of your acceptance or deemed acceptance of these Terms and each day on which you utilize the App with reference to the facts and circumstances existing at such date, as follows: + 6.1 none of you, your Receivers or any of your respective affiliates is: (i) itself or owned (beneficially or of record) or controlled by one or more Prohibited Person(s); (ii) involved in any transaction, transfer, or conduct, whether or not by using the App, that is likely to result in you, your Receivers or your respective affiliates or your or their shareholders, directors, officers, employees, agents, or partners becoming a Prohibited Person; (iii) residing or domiciled in, or utilising the App from a Prohibited Jurisdiction; (iv) a Government or Government Official of a Prohibited Jurisdiction; or (v) otherwise a Prohibited Person; + 6.2 none of you, your Receivers or any of your respective affiliates is acting for or on behalf of any Prohibited Person or will utilise the App to make any Wallet Asset Transfers with Prohibited Persons; + 6.3 none of you, your Receivers or any of your respective affiliates is otherwise prohibited by applicable Laws from using the App and your use of the App will not contravene any Law applicable to you; + 6.4 none of you, your Receivers or any of your respective affiliates will use the App to transfer funds which are the proceeds of conduct that is illegal in any applicable jurisdiction, including the British Virgin Islands, the jurisdiction in which you are located, and the jurisdiction in which any Receiver is located; + 6.5 the App will not be used by any of you, your Receivers or any of your respective affiliates to enable any Prohibited Uses; + 6.6 if you are an individual user, you are 18 years of age or older and that you have the capacity to contract under applicable Laws; + 6.7 if you are using the App on behalf of a legal entity, (i) such legal entity is duly organized and validly existing under the applicable Laws of the jurisdiction of its organization; and (ii) you, and any individuals utilizing the App on behalf of the legal entity are duly authorized by such legal entity to act on its behalf; + 6.8 you understand the risks associated with using the App, including that: (i) Bitfinex Labs does not provide or control any other Person such as your Receivers; (ii) Bitfinex Labs does not provide or control the test net Bitcoin blockchain or the RGB Test Net Protocol or any other Person such as miners on the test net Bitcoin blockchain; (iii) market prices for Digital Assets can be volatile and highly unpredictable; (iv) the legality of holding and transacting Digital Assets may not be clear and may vary under the laws of different jurisdictions throughout the world; (v) both the test net Bitcoin protocol and the RGB Test Net Protocol may be subject to security breaches from cyber-attacks that hack and steal your Digital Assets, or electronic or technological failures that impede or prevent your transfers or cause recordkeeping errors; and (vi) Persons may be or become insolvent, bankrupt or default upon their obligations. + 6.9 you, your Receivers and your respective affiliates are currently in compliance with, and will, at your own cost and expense, comply with all Laws; and + 6.10 none of you, your Receivers and your respective affiliates have (i) violated; (ii) been fined, debarred, sanctioned, the subject of Economic Sanctions-related restrictions, or otherwise penalized under; (iii) received any oral or written notice from any Government concerning actual or possible violation by you under; or (iv) received any other report that you are the subject or target of sanctions, restrictions, penalties, or enforcement action or investigation under, any applicable Laws. + 7 NO REPRESENTATION BY BITFINEX LABS + 7.1 Bitfinex Labs makes no representations, warranties, covenants or guarantees to you of any kind and, to the extent permitted by applicable Laws, Bitfinex Labs expressly disclaims all representations, warranties, covenants or guarantees, express, implied or statutory, with respect to the App. The App is offered strictly on an as-is, where-is basis and, without limiting the generality of the foregoing, is offered without any representation as to merchantability or fitness for any particular purpose. You understand that the App is experimental and may have features that are still in development, may have bugs or errors, may be feature incomplete, may materially change prior to a full commercial launch, or may never be released commercially. + 7.2 Without limiting the generality of Section 7.1, Bitfinex Labs makes no representations, warranties, covenants or guarantees to you in respect of: (a) the connectivity or uptime of the App; or the functionality of the RGB Test Net Protocol or the test net Bitcoin blockchain (including any technical glitch, malfunction, code error, failure, delay, default, or security breach thereof). + 8 TAX +It is your sole responsibility to determine whether and to what extent taxes and tax reporting obligations may apply to you (including any goods and services tax) with respect to the transactions carried out through the App and you shall timely pay all such taxes and shall file all returns, reports, and disclosures required by applicable Law. You agree to indemnify and hold Bitfinex Labs and its Associates harmless from and against any and all taxes (other than income or similar taxes on income earned by Bitfinex Labs in providing the App) payable with respect to any transactions carried out through the App. + 9 NO INSURANCE OR REGULATORY OVERSIGHT +You accept that any Digital Assets and Wallet Transactions with your App are not subject to regulatory oversight, protections or insurance provided by any Person. In addition, whilst Bitfinex Labs may maintain insurance for its own benefit in connection with its business, the insurance, if maintained, is solely for the benefit of Bitfinex Labs and does not guarantee or insure any User in any way. + 10 WARRANTY DISCLAIMERS + 10.1 YOU EXPRESSLY ACKNOWLEDGE AND AGREE THAT USE OF THE APP IS AT YOUR SOLE RISK AND THAT THE ENTIRE RISK AS TO SATISFACTORY QUALITY, PERFORMANCE, ACCURACY AND EFFORT IS WITH YOU. THE APP IS PROVIDED ON AN "AS IS" AND "AS AVAILABLE" BASIS WITHOUT ANY REPRESENTATION OR WARRANTY, WHETHER EXPRESS, IMPLIED OR STATUTORY. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, BITFINEX LABS SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND/OR NON-INFRINGEMENT. BITFINEX LABS DOES NOT MAKE ANY REPRESENTATIONS OR WARRANTIES THAT ACCESS TO THE APP OR ANY DIGITAL ASSETS WILL BE CONTINUOUS, UNINTERRUPTED, TIMELY, OR ERROR-FREE. SERVICE INTERRUPTIONS MAY CAUSE YOU TO BE SIGNED OUT OF YOUR APP AND REQUIRE YOU TO RE-ENTER YOUR RECOVERY PHRASE TO REGAIN ACCESS. + 10.2 YOU ARE SOLELY IN CONTROL OF AND RESPONSIBLE FOR STORING AND SECURING YOUR PRIVATE KEY FOR THE APP. BITFINEX LABS DOES NOT STORE A BACKUP OF, AND WILL NOT BE ABLE TO RECOVER, YOUR PRIVATE KEY. IF YOU LOSE YOUR PRIVATE KEY, THEN YOU WILL LOSE ACCESS TO ALL INFORMATION YOU HAVE STORED IN YOUR APP. YOU SHOULD ALWAYS BACKUP YOUR APP’S PRIVATE ACCESS KEY VIA SECONDARY MEANS. + 11 RESPONSIBILITIES, LIMITATION OF LIABILITY AND INDEMNITY + 11.1 Bitfinex Labs is acting as technology provider only. You retain full responsibility, and neither Bitfinex Labs nor any of its Associates assumes any responsibility, for any Wallet Asset Transfers made through your App. Bitfinex Labs is not required to collate any information on any Receiver. Bitfinex Labs is not responsible, and you retain full responsibility, to ensure that any Wallet Asset Transfers are made to the intended Receiver. Bitfinex Labs cannot reverse any transfer. + 11.2 To the maximum extent permitted by applicable Law, you irrevocably agree and acknowledge that neither Bitfinex Labs nor any of its Associates assumes any liability or responsibility for and neither Bitfinex Labs nor any of its Associates shall have any liability or responsibility for any Losses directly or indirectly arising out of or related to the App. +You hereby agree to release Bitfinex Labs and its Associates from liability for any and all such Losses, and you shall indemnify and save and hold Bitfinex Labs and its Associates harmless from and against all such Losses incurred by them as a result of your use of the App in breach of these Terms, in violation of applicable Law, or for any stolen, lost, or unauthorized use of any App credentials, private keys, data or other information. To the maximum extent permitted by applicable Law, the foregoing indemnity and limitations of liability and releases shall apply whether the alleged liability or Losses are based on contract, negligence, tort, unjust enrichment, strict liability, violation of law or regulation, or any other basis, even if Bitfinex Labs or any of its Associates have been advised of or should have known of the possibility of such Losses and damages, and without regard to the success or effectiveness of any other remedies. + 12 FORCE MAJEURE +Bitfinex Labs is not responsible for Losses caused by delay or failure of the App, including when the delay or failure is due to fires; strikes; floods; power outages or failures; acts of God or the state’s enemies; disease pandemics; acts of any Government or Government Official; any and all market movements, shifts, or volatility; computer, server, protocol or internet malfunctions; security breaches or cyberattacks; criminal acts; delays or defaults caused by common carriers; acts or omissions of other Persons; or, any other delays, defaults, failures or interruptions that cannot reasonably be foreseen or provided against by Bitfinex Labs. + 13 DISPUTE RESOLUTION, ARBITRATION AGREEMENT, CLASS ACTION WAIVER, AND JURY TRIAL WAIVER + 13.1 Covered Claims. Except for excluded claims described below in Section 13.2, Bitfinex Labs and you each agree that any dispute, claim or controversy arising out of or relating to (i) these Terms or the existence, breach, termination, enforcement, interpretation or validity thereof; (ii) the App; or (iii) your use of the App at any time (each, a “Claim”), will be subject to and finally resolved by confidential, binding arbitration and not in a class, representative or consolidated action or proceeding. If you are a Person subject to the jurisdiction of the United States of America, the interpretation and enforceability of this arbitration provision will be governed by the Federal Arbitration Act, 9 U.S.C. §§ 1 et seq. Arbitration will be conducted through the use of videoconferencing technology (unless both arbitration parties agree that an in-person hearing is appropriate given the nature of the dispute) before a single arbitrator in accordance with the Rules of Arbitration of the International Chamber of Commerce, as amended from time to time (the “ICC Rules”). Judgment upon the award rendered by the arbitrator may be entered by any court having jurisdiction thereof. If the arbitral parties do not promptly agree on to seat of arbitration if an in-person hearing is selected, the seat will be the British Virgin Islands. The language of the arbitral proceedings will be English. The arbitrator may award any relief that a court of competent jurisdiction could award, including attorneys’ fees when authorized by law, and the arbitral decision may be enforced in court. + 13.2 Excluded Claims. The following claims and causes of action will be excluded from arbitration as described in Section 13.2: causes of action or claims in which either Party seeks injunctive or other equitable relief for the alleged un-Lawful use of the App or its confidential information or private data. The Parties shall be at liberty to pursue claims or causes of actions excluded from arbitration through any court of competent jurisdiction. + 13.3 Delegation. The arbitrator will have the power to hear and determine challenges to their jurisdiction, including any objections with respect to the existence, scope or validity of the arbitration agreement. This authority extends to jurisdictional challenges with respect to both the subject matter of the dispute and the parties to the arbitration. Further, the arbitrator will have the power to determine the existence, validity, or scope of the contract of which an arbitration clause forms a part. For the purposes of challenges to the jurisdiction of the arbitrator, each clause within this Section 13 will be considered as separable from any contract of which it forms a part. Any challenges to the jurisdiction of the arbitrator, except challenges based on the award itself, will be made not later than the notice of defense or, with respect to a counterclaim, the reply to the counterclaim; provided, however, that if a claim or counterclaim is later added or amended such a challenge may be made not later than the response to such claim or counterclaim as provided under ICC Rules. + 13.4 Class Action Waiver. You and Bitfinex Labs expressly intend and agree that: (i) class action and representative action procedures are hereby waived and will not be asserted, nor will they apply, in any arbitration pursuant to these Terms; (ii) neither you nor Bitfinex Labs will assert class action or representative action claims against the other in arbitration or otherwise; (iii) each of you and Bitfinex Labs will only submit their own, individual claims in arbitration and will not seek to represent the interests of any other person, or consolidate claims with any other person; (iv) nothing in these Terms will be interpreted as your or Bitfinex Labs’ intent to arbitrate Claims on a class or representative basis; and (v) any relief awarded to any one user of the App cannot and may not affect any other user of the App. No adjudicator may consolidate or join more than one Person’s or Party’s claims and may not otherwise preside over any form of a consolidated, representative, or class proceeding. + 13.5 Confidentiality. You and Bitfinex Labs and any other arbitration parties will maintain the confidential nature of the arbitration proceeding and any award, including the hearing, except as may be necessary to prepare for or conduct the arbitration hearing on the merits, or except as may be necessary in connection with a court application for a preliminary remedy, a judicial challenge to an award or its enforcement, or unless otherwise required by law or judicial decision. + 13.6 JURY TRIAL WAIVER. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, THE PARTIES HEREBY IRREVOCABLY AND UNCONDITIONALLY WAIVE ALL RIGHT TO TRIAL BY JURY IN ANY LEGAL ACTION OR PROCEEDING OF ANY KIND WHATSOEVER ARISING OUT OF OR RELATING TO THESE TERMS OR ANY BREACH THEREOF, ANY USE OR ATTEMPTED USE OF THE APP BY YOU, AND/OR ANY OTHER MATTER INVOLVING THE USER AND BITFINEX LABS. + 14 MISCELLANEOUS + 14.1 Governing Law. These Terms shall be governed by and construed and enforced in accordance with the Laws of the British Virgin Islands and shall be interpreted in all respects as a British Virgin Islands contract. Any transaction, dispute, controversy, claim or action arising from or related to your use of the App or these Terms shall be governed by the Laws of the British Virgin Islands, exclusive of choice-of-law principles. + 14.2 No Waiver; Available Remedies. Any failure by Bitfinex Labs to exercise any of its rights, powers, or remedies under these Terms, or any delay by Bitfinex Labs in doing so, does not constitute a waiver of any such right, power, or remedy. The single or partial exercise of any right, power, or remedy by Bitfinex Labs does not prevent either from exercising any other rights, powers, or remedies. The remedies of Bitfinex Labs are cumulative with and not exclusive of any other remedy conferred by the provisions of these Terms, or by law or equity. You agree that the remedies to which Bitfinex Labs is entitled include (i) injunctions to prevent breaches of these Terms and to enforce specifically the terms and provisions hereof, and you waive the requirement of any posting of a bond in connection with such remedies, and (ii) the right to recover the amount of any Losses by set off against any amounts that Bitfinex Labs would otherwise be obligated to pay to you. + 14.3 Assignment; Third Party Rights. These Terms, and any of the rights, duties, and obligations contained or incorporated herein, are not assignable by you without prior written consent of Bitfinex Labs and any attempt by you to assign these Terms without Bitfinex Labs’ written consent is void. These Terms, and any of the rights, duties, and obligations contained herein, are freely assignable by Bitfinex Labs, in whole or in part, without notice or your consent (for clarity, this assignment right includes the right for Bitfinex Labs to assign any claim, in whole or in part, arising hereunder). Any attempt by you to assign these Terms without written consent is void. Subject to the foregoing, these Terms, and any of the rights, duties, and obligations contained or incorporated herein, shall be binding upon and inure to the benefit of the heirs, executors, administrators, personal or legal representatives, successors and assigns of you and of Bitfinex Labs. None of the provisions of these Terms, or any of the rights, duties, and obligations contained or incorporated herein, are for the benefit of or enforceable by any creditors of you or Bitfinex Labs or any other persons, except (i) such as inure to a successor or assign in accordance herewith and (ii) that the Associates of Bitfinex Labs are intended third party beneficiaries of the rights and privileges expressly stated to apply to the Associates hereunder and shall be entitled to enforce such rights and privileges (including those rights and privileges set out in Section 5 (Prohibited Uses) and Section 11 (Responsibilities, Limitation of Liability and Indemnity)) as if a direct party to these Terms. No consent of any Person is required for any modification or amendment to these Terms. + 14.4 Severability. If any provision of these Terms or part thereof, as amended from time to time, is determined to be invalid, void, or unenforceable, in whole or in part, by any court of competent jurisdiction, such invalidity, voidness, or unenforceability attaches only to such provision to the extent of its illegality, unenforceability, invalidity, or voidness, as may be, and everything else in these Terms continues in full force and effect. + 14.5 Electronic Communications; Acceptance. You agree and consent to receive electronically all communications, agreements, documents, receipts, notices and disclosures that Bitfinex Labs may provide in connection with these Terms through publication on any part of the App. Such notices shall be deemed effective and received by you on the date on which the notice is published on any part of the App. These Terms may be accepted electronically, and it is the intention of the parties that such acceptance shall be deemed to be as valid as an original signature being applied to these Terms. @@ -1534,5 +1644,29 @@ If you understand the above remarks and wish to proceed, press the button below please_help_us Please help us improve by sharing the logs. You can report the issue in one of the following ways: + + privacy_policy_tnc + Privacy Policy + + + copy_rgb_invoice + Copy RGB invoice + + + copy_ln_invoice + Copy LN invoice + + + ln_invoice_info + This Lightning invoice will expire {0} {1} after creation and is valid only for this asset. + + + rgb_invoice_label + RGB invoice + + + copied + Copied! + diff --git a/src/version.py b/src/version.py index edadef9..dbdd9e0 100644 --- a/src/version.py +++ b/src/version.py @@ -6,4 +6,4 @@ """ from __future__ import annotations -__version__ = '0.1.0' +__version__ = '0.1.1' diff --git a/src/views/components/receive_asset.py b/src/views/components/receive_asset.py index 53546e1..b1174a0 100644 --- a/src/views/components/receive_asset.py +++ b/src/views/components/receive_asset.py @@ -232,6 +232,14 @@ def retranslate_ui(self): ), ) + self.copy_button.clicked.connect( + lambda: self.copy_button.setText( + QCoreApplication.translate( + 'iris_wallet_desktop', 'copied', None, + ), + ), + ) + def update_qr_and_address(self, address: str): """This method used to set qr and address""" qr_image = set_qr_code(str(address)) diff --git a/src/views/ui_bitcoin_transaction.py b/src/views/ui_bitcoin_transaction.py index 115c3bb..8d1a593 100644 --- a/src/views/ui_bitcoin_transaction.py +++ b/src/views/ui_bitcoin_transaction.py @@ -19,6 +19,8 @@ from PySide6.QtWidgets import QWidget import src.resources_rc +from src.data.repository.setting_repository import SettingRepository +from src.model.enums.enums_model import NetworkEnumModel from src.model.enums.enums_model import TransferStatusEnumModel from src.model.transaction_detail_page_model import TransactionDetailPageModel from src.utils.common_utils import get_bitcoin_explorer_url @@ -238,10 +240,13 @@ def retranslate_ui(self): 'iris_wallet_desktop', 'transaction_id', None, ), ) - self.bitcoin_tx_id_value.setText( - f"" - f"{self.tx_id}", - ) + if SettingRepository.get_wallet_network() != NetworkEnumModel.REGTEST: + self.bitcoin_tx_id_value.setText( + f"" + f"{self.tx_id}", + ) + else: + self.bitcoin_tx_id_value.setText(self.tx_id) self.btc_amount_label.setText( QCoreApplication.translate( 'iris_wallet_desktop', 'amount', None, diff --git a/src/views/ui_create_ln_invoice.py b/src/views/ui_create_ln_invoice.py index 2480b35..cb896ce 100644 --- a/src/views/ui_create_ln_invoice.py +++ b/src/views/ui_create_ln_invoice.py @@ -478,6 +478,8 @@ def get_ln_invoice(self): params=AssetDataModel( asset_type='create_invoice', close_page_navigation=self.asset_type, + expiry_time=int(self.expiry_input.text()), + expiry_unit=self.time_unit_combobox.currentText(), ), ) diff --git a/src/views/ui_ln_endpoint.py b/src/views/ui_ln_endpoint.py index 7d9c609..43c97fd 100644 --- a/src/views/ui_ln_endpoint.py +++ b/src/views/ui_ln_endpoint.py @@ -224,7 +224,7 @@ def setup_ui_connection(self): """Set up connections for UI elements.""" self.close_button.clicked.connect( lambda: close_button_navigation( - self, self._view_model.page_navigation.term_and_condition_page, + self, self.view_model.page_navigation.term_and_condition_page, ), ) self.proceed_button.clicked.connect(self.set_ln_url) diff --git a/src/views/ui_receive_rgb_asset.py b/src/views/ui_receive_rgb_asset.py index e047d92..e6a458c 100644 --- a/src/views/ui_receive_rgb_asset.py +++ b/src/views/ui_receive_rgb_asset.py @@ -4,6 +4,7 @@ """ from __future__ import annotations +from PySide6.QtCore import QCoreApplication from PySide6.QtWidgets import QVBoxLayout from PySide6.QtWidgets import QWidget @@ -31,6 +32,8 @@ def __init__(self, view_model, params: AssetDataModel): self.originating_page = params.asset_type self.asset_id = params.asset_id self.close_page_navigation = params.close_page_navigation + self.expiry_time = params.expiry_time + self.expiry_unit = params.expiry_unit self.default_min_confirmation = SettingCardRepository.get_default_min_confirmation() self.receive_rgb_asset_page = ReceiveAssetWidget( self._view_model, @@ -69,6 +72,16 @@ def generate_invoice(self): def setup_ui_connection(self): """Set up connections for UI elements.""" self.show_receive_rgb_loading() + self.receive_rgb_asset_page.copy_button.setText( + QCoreApplication.translate( + 'iris_wallet_desktop', 'copy_rgb_invoice', None, + ), + ) + self.receive_rgb_asset_page.address_label.setText( + QCoreApplication.translate( + 'iris_wallet_desktop', 'rgb_invoice_label', None, + ), + ) self.receive_rgb_asset_page.copy_button.clicked.connect( lambda: copy_text(self.receive_rgb_asset_page.receiver_address), ) @@ -82,7 +95,7 @@ def setup_ui_connection(self): self.update_address, ) self._view_model.ln_offchain_view_model.invoice_get_event.connect( - self.update_address, + lambda address: self.update_address(address, ln_invoice=True), ) self._view_model.receive_rgb25_view_model.message.connect( self.handle_message, @@ -130,9 +143,25 @@ def close_button_navigation(self): }', ) - def update_address(self, address: str): + def update_address(self, address: str, ln_invoice: bool = False): """This method used to update new address""" self.receive_rgb_asset_page.update_qr_and_address(address) + if ln_invoice: + self.receive_rgb_asset_page.copy_button.setText( + QCoreApplication.translate( + 'iris_wallet_desktop', 'copy_ln_invoice', None, + ), + ) + self.receive_rgb_asset_page.address_label.setText( + QCoreApplication.translate( + 'iris_wallet_desktop', 'ln_invoice_label', None, + ), + ) + self.receive_rgb_asset_page.wallet_address_description_text.setText( + QCoreApplication.translate('iris_wallet_desktop', 'ln_invoice_info', None).format( + self.expiry_time, self.expiry_unit, + ), + ) def handle_message(self, msg_type: int, message: str): """This method handled to show message.""" diff --git a/src/views/ui_rgb_asset_transaction_detail.py b/src/views/ui_rgb_asset_transaction_detail.py index 138978c..b0e09db 100644 --- a/src/views/ui_rgb_asset_transaction_detail.py +++ b/src/views/ui_rgb_asset_transaction_detail.py @@ -19,6 +19,8 @@ from PySide6.QtWidgets import QWidget import src.resources_rc +from src.data.repository.setting_repository import SettingRepository +from src.model.enums.enums_model import NetworkEnumModel from src.model.enums.enums_model import PaymentStatus from src.model.enums.enums_model import TransferStatusEnumModel from src.model.rgb_model import RgbAssetPageLoadModel @@ -377,10 +379,6 @@ def set_rgb_asset_value(self): else: unblinded_and_change_utxo_value = None self.url = get_bitcoin_explorer_url(self.params.tx_id) - self.tx_id_value.setText( - f"" - f"{self.tx_id}", - ) if self.params.receive_utxo is not None: self.url = get_bitcoin_explorer_url(self.params.receive_utxo) unblinded_and_change_utxo_value = insert_zero_width_spaces( @@ -391,10 +389,20 @@ def set_rgb_asset_value(self): unblinded_and_change_utxo_value = insert_zero_width_spaces( self.params.change_utxo, ) - self.unblinded_and_change_utxo_value.setText( - f"" - f"{unblinded_and_change_utxo_value}", - ) + if SettingRepository.get_wallet_network() != NetworkEnumModel.REGTEST: + self.unblinded_and_change_utxo_value.setText( + f"" + f"{unblinded_and_change_utxo_value}", + ) + self.tx_id_value.setText( + f"" + f"{self.tx_id}", + ) + else: + self.unblinded_and_change_utxo_value.setText( + unblinded_and_change_utxo_value, + ) + self.tx_id_value.setText(self.tx_id) self.blinded_utxo_value.setText(self.params.recipient_id) if self.params.consignment_endpoints: consignment_endpoint = self.params.consignment_endpoints[0].endpoint or 'N/A' diff --git a/src/views/ui_send_ln_invoice.py b/src/views/ui_send_ln_invoice.py index 3cd1387..1d53128 100644 --- a/src/views/ui_send_ln_invoice.py +++ b/src/views/ui_send_ln_invoice.py @@ -584,11 +584,15 @@ def _display_invoice_detail(self, detail): def _update_max_asset_local_balance(self, detail): """Updates the maximum asset local balance for the given asset_id.""" - max_balance = None - for channel in self._view_model.channel_view_model.channels: - if channel.asset_id == detail.asset_id and channel.is_usable and channel.ready: - max_balance = max(max_balance or 0, channel.asset_local_amount) - self.max_asset_local_balance = max_balance + if detail.asset_id: + max_balance = None + for channel in self._view_model.channel_view_model.channels: + if channel.asset_id == detail.asset_id and channel.is_usable and channel.ready: + max_balance = max( + max_balance or 0, + channel.asset_local_amount, + ) + self.max_asset_local_balance = max_balance def _validate_asset_amount(self, detail): """Validates the asset amount and updates the UI accordingly.""" diff --git a/src/views/ui_term_condition.py b/src/views/ui_term_condition.py index c10d643..0d782e9 100644 --- a/src/views/ui_term_condition.py +++ b/src/views/ui_term_condition.py @@ -7,16 +7,18 @@ from PySide6.QtCore import QCoreApplication from PySide6.QtCore import QSize from PySide6.QtCore import Qt +from PySide6.QtGui import QTextOption from PySide6.QtWidgets import QFrame from PySide6.QtWidgets import QGridLayout from PySide6.QtWidgets import QHBoxLayout from PySide6.QtWidgets import QLabel -from PySide6.QtWidgets import QPlainTextEdit from PySide6.QtWidgets import QSizePolicy from PySide6.QtWidgets import QSpacerItem +from PySide6.QtWidgets import QTextBrowser from PySide6.QtWidgets import QWidget import src.resources_rc +from src.utils.constant import PRIVACY_POLICY_URL from src.utils.helpers import load_stylesheet from src.viewmodels.main_view_model import MainViewModel from src.views.components.buttons import PrimaryButton @@ -85,14 +87,28 @@ def __init__(self, view_model): self.grid_layout_10_tnc.addWidget(self.tnc_line_tnc, 1, 0, 1, 1) - self.tnc_text_desc = QPlainTextEdit(self.tnc_widget) + self.tnc_text_desc = QTextBrowser(self.tnc_widget) self.tnc_text_desc.setObjectName('TnC_Text_Desc') self.tnc_text_desc.setMinimumSize(QSize(644, 348)) self.tnc_text_desc.setMaximumSize(QSize(644, 348)) self.tnc_text_desc.setStyleSheet( - load_stylesheet('views/qss/q_label.qss'), + load_stylesheet('views/qss/scrollbar.qss') + + """ + QTextBrowser { + background-color: rgb(21, 28, 52); + color: white; + font: 14px"Inter"; + line-height: 1.5; + border: none; + padding: 10px; + } + """, ) self.tnc_text_desc.setReadOnly(True) + self.tnc_text_desc.verticalScrollBar().valueChanged.connect( + self.check_scroll_completion, + ) + self.tnc_text_desc.setOpenExternalLinks(True) self.grid_layout_10_tnc.addWidget( self.tnc_text_desc, 2, 0, 1, 1, Qt.AlignHCenter, @@ -126,6 +142,7 @@ def __init__(self, view_model): self.accept_btn = PrimaryButton() self.accept_btn.setMinimumSize(QSize(318, 40)) self.accept_btn.setMaximumSize(QSize(318, 40)) + self.accept_btn.setDisabled(True) self.tnc_horizontal_layout.addWidget(self.accept_btn) @@ -179,13 +196,8 @@ def retranslate_ui(self): None, ), ) - self.tnc_text_desc.setPlainText( - QCoreApplication.translate( - 'iris_wallet_desktop', - 'terms_and_conditions_content', - None, - ), - ) + + self.load_terms_conditions() self.decline_btn.setText( QCoreApplication.translate( 'iris_wallet_desktop', @@ -200,3 +212,53 @@ def retranslate_ui(self): None, ), ) + + def check_scroll_completion(self): + """Enable the Accept button when the user scrolls to the end.""" + scrollbar = self.tnc_text_desc.verticalScrollBar() + if scrollbar.value() == scrollbar.maximum(): + self.accept_btn.setEnabled(True) + + def load_terms_conditions(self): + """Load Terms & Conditions text and format 'Privacy Policy' as a hyperlink while preserving formatting.""" + get_translated_privacy_policy = QCoreApplication.translate( + 'iris_wallet_desktop', + 'privacy_policy_tnc', + None, + ) + terms_text = QCoreApplication.translate( + 'iris_wallet_desktop', + 'terms_and_conditions_content', + None, + ).format(get_translated_privacy_policy) + + terms_text = terms_text.replace( + get_translated_privacy_policy, f'{get_translated_privacy_policy}', + ) + + # Add CSS for line-height + formatted_text = f''' + + + + + +
{terms_text.replace('\n\n', '

').replace('\n', '
')}

+ + + ''' + + self.tnc_text_desc.setHtml(formatted_text) + + self.tnc_text_desc.setWordWrapMode( + QTextOption.WrapAtWordBoundaryOrAnywhere, + ) diff --git a/unit_tests/tests/ui_tests/ui_bitcoin_transaction_test.py b/unit_tests/tests/ui_tests/ui_bitcoin_transaction_test.py index 0a8b825..72b409a 100644 --- a/unit_tests/tests/ui_tests/ui_bitcoin_transaction_test.py +++ b/unit_tests/tests/ui_tests/ui_bitcoin_transaction_test.py @@ -11,6 +11,7 @@ from PySide6.QtCore import QCoreApplication from PySide6.QtGui import QTextDocument +from src.model.enums.enums_model import NetworkEnumModel from src.model.enums.enums_model import TransferStatusEnumModel from src.model.transaction_detail_page_model import TransactionDetailPageModel from src.utils.common_utils import network_info @@ -57,17 +58,44 @@ def bitcoin_transaction_detail_widget(mock_bitcoin_transaction_detail_view_model def test_retranslate_ui(bitcoin_transaction_detail_widget, qtbot): """Test the retranslate_ui method.""" - bitcoin_transaction_detail_widget.network = 'mainnet' - bitcoin_transaction_detail_widget.retranslate_ui() - - expected_bitcoin_text = f'{ - QCoreApplication.translate( - "iris_wallet_desktop", "bitcoin", None - ) - } (mainnet)' - assert bitcoin_transaction_detail_widget.bitcoin_text == expected_bitcoin_text - assert bitcoin_transaction_detail_widget.tx_id_label.text( - ) == QCoreApplication.translate('iris_wallet_desktop', 'transaction_id', None) + # Set up initial state + with patch('src.data.repository.setting_repository.SettingRepository.get_wallet_network') as mock_network: + mock_network.return_value = NetworkEnumModel.MAINNET + bitcoin_transaction_detail_widget.network = 'mainnet' + bitcoin_transaction_detail_widget.tx_id = 'test_tx_id' + bitcoin_transaction_detail_widget.params.tx_id = 'test_tx_id' + bitcoin_transaction_detail_widget.retranslate_ui() + + # Test bitcoin_text construction + expected_bitcoin_text = f'{ + QCoreApplication.translate( + "iris_wallet_desktop", "bitcoin", None + ) + } (mainnet)' + assert bitcoin_transaction_detail_widget.bitcoin_text == expected_bitcoin_text + + # Test URL construction + assert bitcoin_transaction_detail_widget.url == 'https://mempool.space/tx/test_tx_id' + + # Test all label texts + assert bitcoin_transaction_detail_widget.tx_id_label.text( + ) == QCoreApplication.translate('iris_wallet_desktop', 'transaction_id', None) + assert bitcoin_transaction_detail_widget.btc_amount_label.text( + ) == QCoreApplication.translate('iris_wallet_desktop', 'amount', None) + assert bitcoin_transaction_detail_widget.date_label.text( + ) == QCoreApplication.translate('iris_wallet_desktop', 'date', None) + assert bitcoin_transaction_detail_widget.bitcoin_title_value.text() == expected_bitcoin_text + + # Test tx_id_value text for mainnet + expected_tx_link = f"" \ + f"{bitcoin_transaction_detail_widget.tx_id}" + assert bitcoin_transaction_detail_widget.bitcoin_tx_id_value.text() == expected_tx_link + + # Test for REGTEST network + mock_network.return_value = NetworkEnumModel.REGTEST + bitcoin_transaction_detail_widget.retranslate_ui() + assert bitcoin_transaction_detail_widget.bitcoin_tx_id_value.text( + ) == bitcoin_transaction_detail_widget.tx_id def test_set_btc_tx_value_sent_status(bitcoin_transaction_detail_widget, qtbot): diff --git a/unit_tests/tests/ui_tests/ui_create_ln_invoice_test.py b/unit_tests/tests/ui_tests/ui_create_ln_invoice_test.py index e9ecd64..5d8cceb 100644 --- a/unit_tests/tests/ui_tests/ui_create_ln_invoice_test.py +++ b/unit_tests/tests/ui_tests/ui_create_ln_invoice_test.py @@ -109,16 +109,22 @@ def test_get_ln_invoice(mock_create_ln_invoice_view_model, create_ln_invoice_wid widget.msat_amount_value = MagicMock() widget.amount_input = MagicMock() widget.expiry_input = MagicMock() + widget.time_unit_combobox = MagicMock() + widget.render_timer = MagicMock() widget.amt_msat_value = 'amt_msat' # Set up mock return values widget.amount_input.text.return_value = '1000' widget.expiry_input.text.return_value = '3600' + widget.time_unit_combobox.currentText.return_value = 'minutes' # Mock the view model method to check if it was called correctly mock_method = MagicMock() mock_create_ln_invoice_view_model.ln_offchain_view_model.get_invoice = mock_method + # Mock get_expiry_time_in_seconds + widget.get_expiry_time_in_seconds = MagicMock(return_value=216000) + # Test case when asset_id is Bitcoin widget.asset_id = AssetType.BITCOIN.value widget.msat_amount_value.text.return_value = '' # Empty MSAT value for Bitcoin @@ -138,7 +144,9 @@ def test_get_ln_invoice(mock_create_ln_invoice_view_model, create_ln_invoice_wid # Check non-Bitcoin case with empty msat uses amount_input converted to msat _, kwargs = mock_method.call_args assert 'amount_msat' in kwargs - assert kwargs['amount_msat'] == 3000000 # 1000 * 3000 conversion to msat + assert kwargs['amount'] == '1000' + assert kwargs['asset_id'] == 'OTHER' + assert kwargs['expiry'] == 216000 # Test case when asset_id is not Bitcoin and msat_amount_value is set widget.msat_amount_value.text.return_value = '2000' # Set MSAT value diff --git a/unit_tests/tests/ui_tests/ui_ln_endpoint_test.py b/unit_tests/tests/ui_tests/ui_ln_endpoint_test.py index 70a271c..6c957dd 100644 --- a/unit_tests/tests/ui_tests/ui_ln_endpoint_test.py +++ b/unit_tests/tests/ui_tests/ui_ln_endpoint_test.py @@ -106,3 +106,14 @@ def test_set_ln_placeholder_text_other_page(ln_endpoint_widget: LnEndpointWidget ln_endpoint_widget.originating_page = 'other_page' ln_endpoint_widget.set_ln_placeholder_text() assert ln_endpoint_widget.enter_ln_node_url_input.text() == BACKED_URL_LIGHTNING_NETWORK + + +def test_close_button_navigation(ln_endpoint_widget: LnEndpointWidget): + """Test the close button navigation behavior.""" + # Mock the page navigation methods + ln_endpoint_widget.view_model.page_navigation.wallet_method_page = MagicMock() + + # Test wallet selection page navigation + ln_endpoint_widget.originating_page = 'wallet_selection_page' + ln_endpoint_widget.close_button.clicked.emit() + ln_endpoint_widget.view_model.page_navigation.wallet_method_page.assert_called_once() diff --git a/unit_tests/tests/ui_tests/ui_receive_rgb_asset_test.py b/unit_tests/tests/ui_tests/ui_receive_rgb_asset_test.py index 5a1db4e..aa4b859 100644 --- a/unit_tests/tests/ui_tests/ui_receive_rgb_asset_test.py +++ b/unit_tests/tests/ui_tests/ui_receive_rgb_asset_test.py @@ -8,6 +8,7 @@ from unittest.mock import patch import pytest +from PySide6.QtCore import QCoreApplication from src.model.enums.enums_model import AssetType from src.model.enums.enums_model import ToastPreset @@ -45,6 +46,10 @@ def test_setup_ui_connection(receive_rgb_asset_widget: ReceiveRGBAssetWidget): with patch('src.views.components.toast.ToastManager._create_toast', MagicMock()): with patch.object(receive_rgb_asset_widget, 'show_receive_rgb_loading', MagicMock()): # Emit the signal to simulate a button click + # Verify copy button text is set correctly + assert receive_rgb_asset_widget.receive_rgb_asset_page.copy_button.text() == QCoreApplication.translate( + 'iris_wallet_desktop', 'copy_rgb_invoice', None, + ) receive_rgb_asset_widget.receive_rgb_asset_page.copy_button.clicked.emit() # Verify UI connections are set up @@ -53,6 +58,11 @@ def test_setup_ui_connection(receive_rgb_asset_widget: ReceiveRGBAssetWidget): receive_rgb_asset_widget._view_model.receive_rgb25_view_model.message.connect.assert_called() receive_rgb_asset_widget._view_model.receive_rgb25_view_model.hide_loading.connect.assert_called() + # Verify address label text is set correctly + assert receive_rgb_asset_widget.receive_rgb_asset_page.address_label.text() == QCoreApplication.translate( + 'iris_wallet_desktop', 'rgb_invoice_label', None, + ) + def test_close_button_navigation(receive_rgb_asset_widget): """Test navigation on close button click.""" @@ -125,10 +135,35 @@ def test_close_button_navigation(receive_rgb_asset_widget): def test_update_address(receive_rgb_asset_widget: ReceiveRGBAssetWidget): """Test that the address is updated correctly.""" - with patch.object(receive_rgb_asset_widget.receive_rgb_asset_page, 'update_qr_and_address', MagicMock()) as mock_update_qr_and_address: + with patch.object(receive_rgb_asset_widget.receive_rgb_asset_page, 'update_qr_and_address', MagicMock()) as mock_update_qr_and_address, \ + patch.object(receive_rgb_asset_widget.receive_rgb_asset_page.wallet_address_description_text, 'setText', MagicMock()) as mock_set_text: + + # Test case 1: Regular address update receive_rgb_asset_widget.update_address('new_address') mock_update_qr_and_address.assert_called_once_with('new_address') + mock_set_text.assert_not_called() + mock_update_qr_and_address.reset_mock() + + # Test case 2: Lightning invoice update + receive_rgb_asset_widget.expiry_time = 24 + receive_rgb_asset_widget.expiry_unit = 'hours' + receive_rgb_asset_widget.update_address( + 'ln_invoice_address', ln_invoice=True, + ) + mock_update_qr_and_address.assert_called_once_with( + 'ln_invoice_address', + ) + mock_set_text.assert_called_once_with( + QCoreApplication.translate( + 'iris_wallet_desktop', 'ln_invoice_info', None, + ).format('24', 'hours'), + ) + + # Test case 3: Copy Lightning invoice translation + assert QCoreApplication.translate( + 'iris_wallet_desktop', 'copy_ln_invoice', None, + ) def test_show_receive_rgb_loading(receive_rgb_asset_widget: ReceiveRGBAssetWidget): diff --git a/unit_tests/tests/ui_tests/ui_term_condition_test.py b/unit_tests/tests/ui_tests/ui_term_condition_test.py index 533699e..05e49fc 100644 --- a/unit_tests/tests/ui_tests/ui_term_condition_test.py +++ b/unit_tests/tests/ui_tests/ui_term_condition_test.py @@ -1,13 +1,19 @@ """Unit test for TermConditionWidget.""" # Disable the redefined-outer-name warning as # it's normal to pass mocked objects in test functions -# pylint: disable=redefined-outer-name,unused-argument +# pylint: disable=redefined-outer-name,unused-argument, protected-access from __future__ import annotations from unittest.mock import MagicMock +from unittest.mock import patch import pytest +from PySide6.QtCore import QCoreApplication +from PySide6.QtCore import Qt +from PySide6.QtGui import QTextOption +from PySide6.QtWidgets import QScrollBar +from src.utils.constant import PRIVACY_POLICY_URL from src.viewmodels.main_view_model import MainViewModel from src.views.ui_term_condition import TermConditionWidget @@ -16,16 +22,140 @@ def term_condition_widget(qtbot): """Fixture to create and return an instance of TermConditionWidget.""" mock_navigation = MagicMock() - view_model = MagicMock(MainViewModel(mock_navigation)) + + # Create the view model without mocking it initially + view_model = MainViewModel(mock_navigation) + + # Create and set up the terms view model mock + mock_terms_view_model = MagicMock() + mock_terms_view_model.on_accept_click = MagicMock() + mock_terms_view_model.on_decline_click = MagicMock() + + # Set the mock terms view model + view_model.terms_view_model = mock_terms_view_model + widget = TermConditionWidget(view_model) qtbot.addWidget(widget) return widget +def test_init(term_condition_widget: TermConditionWidget): + """Test the initialization of TermConditionWidget.""" + # Test initial state + assert term_condition_widget.accept_btn.isEnabled() is False + assert term_condition_widget.tnc_text_desc.isReadOnly() is True + assert term_condition_widget.tnc_text_desc.openExternalLinks() is True + + def test_retranslate_ui(term_condition_widget: TermConditionWidget): """Test the retranslation of UI elements in TermConditionWidget.""" term_condition_widget.retranslate_ui() + # Test text translations assert term_condition_widget.tnc_label_text.text() == 'terms_and_conditions' - assert term_condition_widget.tnc_text_desc.toPlainText( - ) == 'terms_and_conditions_content' + assert term_condition_widget.wallet_logo_tnc.logo_text.text() == 'iris_wallet' + assert term_condition_widget.decline_btn.text() == 'decline' + assert term_condition_widget.accept_btn.text() == 'accept' + + +def test_setup_ui_connection(term_condition_widget: TermConditionWidget, qtbot): + """Test UI connections are properly set up.""" + # The buttons should be properly connected from widget initialization + + # Enable accept button since it's disabled by default + term_condition_widget.accept_btn.setEnabled(True) + + # Trigger button clicks + qtbot.mouseClick( + term_condition_widget.accept_btn, + Qt.MouseButton.LeftButton, + ) + qtbot.mouseClick( + term_condition_widget.decline_btn, + Qt.MouseButton.LeftButton, + ) + + # Verify view model methods were called + term_condition_widget._view_model.terms_view_model.on_accept_click.assert_called_once() + term_condition_widget._view_model.terms_view_model.on_decline_click.assert_called_once() + + +def test_check_scroll_completion(term_condition_widget: TermConditionWidget, qtbot): + """Test the scroll completion behavior.""" + # Create mock scrollbar + mock_scrollbar = MagicMock(spec=QScrollBar) + mock_scrollbar.value.return_value = 100 + mock_scrollbar.maximum.return_value = 100 + + # Replace the actual scrollbar with mock + with patch.object(term_condition_widget.tnc_text_desc, 'verticalScrollBar', return_value=mock_scrollbar): + # Trigger scroll completion + term_condition_widget.check_scroll_completion() + + # Verify accept button is enabled + assert term_condition_widget.accept_btn.isEnabled() is True + + # Test when not scrolled to bottom + mock_scrollbar.value.return_value = 50 + with patch.object(term_condition_widget.tnc_text_desc, 'verticalScrollBar', return_value=mock_scrollbar): + # Reset accept button state + term_condition_widget.accept_btn.setEnabled(False) + + # Trigger scroll check + term_condition_widget.check_scroll_completion() + + # Verify accept button remains disabled + assert term_condition_widget.accept_btn.isEnabled() is False + + +def test_load_terms_conditions(term_condition_widget: TermConditionWidget): + """Test loading and formatting of terms and conditions text.""" + # Mock the translations + with patch.object(QCoreApplication, 'translate') as mock_translate: + # Set up mock returns for different translation calls + def translate_side_effect(context, text, *args): + translations = { + 'privacy_policy_tnc': 'Privacy Policy', + 'terms_and_conditions_content': 'Sample terms content with {0}', + } + return translations.get(text, text) + + mock_translate.side_effect = translate_side_effect + + # Call the method + term_condition_widget.load_terms_conditions() + + # Verify privacy policy link is properly formatted + content = term_condition_widget.tnc_text_desc.toHtml() + + # Check for essential elements + assert PRIVACY_POLICY_URL in content # Privacy policy URL + assert 'color:#01a781' in content # Link color + assert 'Privacy Policy' in content # Link text + assert 'text-decoration: underline' in content # Link underline + assert 'Sample terms content' in content # Terms content + + # Verify word wrap mode + assert term_condition_widget.tnc_text_desc.wordWrapMode( + ) == QTextOption.WrapAtWordBoundaryOrAnywhere + + +@pytest.mark.parametrize( + 'scroll_value,maximum,expected_enabled', + [ + (100, 100, True), # Scrolled to bottom + (50, 100, False), # Halfway scrolled + (0, 100, False), # Top of scroll + (0, 0, True), # No scroll needed + ], +) +def test_scroll_scenarios(term_condition_widget: TermConditionWidget, scroll_value, maximum, expected_enabled): + """Test various scroll scenarios and their effect on the accept button.""" + mock_scrollbar = MagicMock(spec=QScrollBar) + mock_scrollbar.value.return_value = scroll_value + mock_scrollbar.maximum.return_value = maximum + + with patch.object(term_condition_widget.tnc_text_desc, 'verticalScrollBar', return_value=mock_scrollbar): + term_condition_widget.accept_btn.setEnabled(False) + term_condition_widget.check_scroll_completion() + assert term_condition_widget.accept_btn.isEnabled() is expected_enabled