From 7d7ca4cc52dc315e33827a49717a6a532f59ebd8 Mon Sep 17 00:00:00 2001 From: Kevin Neilson Date: Tue, 5 Nov 2024 17:35:46 -0500 Subject: [PATCH 1/6] add 0xgasless tutorial --- .../integrations/0xgasless/dispatch.js | 93 ++++++++++++ .../integrations/0xgasless/output.md | 16 ++ .../integrations/0xgasless/0xgasless-1.webp | Bin 0 -> 22344 bytes .../integrations/0xgasless/0xgasless-2.webp | Bin 0 -> 21908 bytes tutorials/integrations/.pages | 1 + tutorials/integrations/0xgasless.md | 141 ++++++++++++++++++ 6 files changed, 251 insertions(+) create mode 100644 .snippets/code/tutorials/integrations/0xgasless/dispatch.js create mode 100644 .snippets/code/tutorials/integrations/0xgasless/output.md create mode 100644 images/tutorials/integrations/0xgasless/0xgasless-1.webp create mode 100644 images/tutorials/integrations/0xgasless/0xgasless-2.webp create mode 100644 tutorials/integrations/0xgasless.md diff --git a/.snippets/code/tutorials/integrations/0xgasless/dispatch.js b/.snippets/code/tutorials/integrations/0xgasless/dispatch.js new file mode 100644 index 000000000..c9718888d --- /dev/null +++ b/.snippets/code/tutorials/integrations/0xgasless/dispatch.js @@ -0,0 +1,93 @@ +require('dotenv').config(); +const ethers = require('ethers'); +const { + PaymasterMode, + createSmartAccountClient, +} = require("@0xgasless/smart-account"); + +const CHAIN_ID = 1284; // Moonbeam mainnet +const BUNDLER_URL = `https://bundler.0xgasless.com/${CHAIN_ID}`; +const PAYMASTER_URL = 'https://paymaster.0xgasless.com/v1/1284/rpc/INSERT_API_KEY'; +const CONTRACT_ADDRESS = '0x3aE26f2c909EB4F1EdF97bf60B36529744b09213'; +const FUNCTION_SELECTOR = '0xd09de08a'; + +async function main() { + console.log("Starting the script..."); + try { + // Set up provider and wallet + console.log("Setting up provider and wallet..."); + const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL); + const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider); + + // Check connection and balance + console.log("Checking network connection..."); + const network = await provider.getNetwork(); + console.log(`Connected to network: ${network.name} (Chain ID: ${network.chainId})`); + const balance = await provider.getBalance(wallet.address); + console.log(`Wallet balance: ${ethers.utils.formatEther(balance)} GLMR`); + + // Initialize smart account + console.log("Initializing smart account..."); + const smartWallet = await createSmartAccountClient({ + signer: wallet, + paymasterUrl: PAYMASTER_URL, + bundlerUrl: BUNDLER_URL, + chainId: CHAIN_ID, + }); + const smartWalletAddress = await smartWallet.getAddress(); + console.log("Smart Account Address:", smartWalletAddress); + + // Create a transaction for contract interaction + console.log("Creating contract transaction..."); + const transaction = { + to: CONTRACT_ADDRESS, + value: '0', // No native token transfer + data: FUNCTION_SELECTOR, // The function selector for the method we want to call + }; + + // Send the transaction + console.log("Sending transaction..."); + const userOpResponse = await smartWallet.sendTransaction(transaction, { + paymasterServiceData: { mode: PaymasterMode.SPONSORED }, + }); + console.log("UserOp Hash:", userOpResponse.hash); + + console.log("Waiting for transaction receipt..."); + const userOpReceipt = await waitForUserOpReceipt(userOpResponse, 60000); // Wait for up to 60 seconds + + if (userOpReceipt.success) { + console.log("Transaction successful!"); + console.log("Transaction hash:", userOpReceipt.receipt.transactionHash); + } else { + console.log("Transaction failed"); + console.log("Receipt:", userOpReceipt); + } + } catch (error) { + console.error("An error occurred:"); + console.error(error); + } +} + +async function waitForUserOpReceipt(userOpResponse, timeoutMs = 60000) { + return new Promise((resolve, reject) => { + const startTime = Date.now(); + const checkReceipt = async () => { + try { + const receipt = await userOpResponse.wait(); + resolve(receipt); + } catch (error) { + if (Date.now() - startTime > timeoutMs) { + reject(new Error(`Transaction wait timeout after ${timeoutMs}ms`)); + } else { + setTimeout(checkReceipt, 5000); // Retry every 5 seconds + } + } + }; + checkReceipt(); + }); +} + +main().catch((error) => { + console.error("Unhandled error in main function:"); + console.error(error); +}); \ No newline at end of file diff --git a/.snippets/code/tutorials/integrations/0xgasless/output.md b/.snippets/code/tutorials/integrations/0xgasless/output.md new file mode 100644 index 000000000..e2fcacf5b --- /dev/null +++ b/.snippets/code/tutorials/integrations/0xgasless/output.md @@ -0,0 +1,16 @@ +
+ 0xgasless % node dispatch.js + Starting the script... + Setting up provider and wallet... + Checking network connection... + Connected to network: unknown (Chain ID: 1284) + Wallet balance: 8.781249287153010128 GLMR + Initializing smart account... + Smart Account Address: 0xbBf77D3B43d81D426c4c3D200a76F4D3a914ccE3 + Creating contract transaction... + Sending transaction... + UserOp Hash: undefined + Waiting for transaction receipt... + Transaction successful! + Transaction hash: 0x9cb49cc0acc21abc364c13dd52b3f65c206ec61c57a13c23b635f59e1919cf7c +
\ No newline at end of file diff --git a/images/tutorials/integrations/0xgasless/0xgasless-1.webp b/images/tutorials/integrations/0xgasless/0xgasless-1.webp new file mode 100644 index 0000000000000000000000000000000000000000..99ee2f171cd4b916e2949058db30fcfff79e3c0d GIT binary patch literal 22344 zcmZ6xV~{RP&?VTuZQJg?ZQFL=wr$(CZQHhO+qP{!`+l)IvoRBKA__mUBCAfGEGbHg zi3z9y0jY@!{ZswNLCEr7eI6P(2bk&^WCj=r2#BntYd_X}1f|dOrE21~<+qj37uvW- zAG>0p&j0u8L;mQA?L+!LZy%t;Z_K~>z47E{q-$`ScYXE|H}t8|B}~`r{@e9|^$U9H z_8s?05KlnkCjsbs1OK75;{kHvh{q%jr_3X|1<$bsP=)cDjt|N{HK1iznb14pL!2{=Y1P~;{dbYo8O}!@x`=Sfb(zD7x)j!fqZq}8i3fZ z{>T46Bg{XbUvrmzuXC>i{C?R0@T0evyIa4LzE*$+AmE4M8s@$20dTIj#orGw{?F9h zP0_pFbKfEVlwSe>34pODex|qSH|-Y+Q24F-rU&r%^B?Ms^_2mv02n`gUvWQnk9*F& zUHs;};rudy#vgCLMqc<@+clPAjy3s(A4w0aoJwE|CG-vLJOhHDf<~>Z_S4 zs9BYaB7X^xJ8pBv#?1DLHuH_!s;lmW1=MZjt!C>t*+GX*R{l@`UvX+{d6`gR$Wrs~ zUatHh1*eYz%u6RG1^q*fy&a*3YyEzHfG6E59=#Fm!bf_gT?+DdsLZjWTa$r?3y0%h zs>bWqv$;V;LuroZBo$XJr?Y(_X*8n~oWcGjUOg+zAcU3==uDv4)5Ts3UlB{YbZG66nLC6|o#hF!&%YHBV ze7H>``x}CEAlE|LPnOc&*U0{~vh`bM$O{Jb)?Vz%Se$Z9qjq=jB=bMcvnwNC+!${r zt5Z@;?FeoSVllQzmdMlu&(-gv!${X0OzunO>HlFqYX>MtIp;lY=pY`RUug_=wU`s@ zev$;)x=LEmDFDnqpg~W~F-|fRVe4 zltG*SUyl)4(jnUqCD~?b^H1oDP`5X$s)oJ|@&#@@o;&5;7lWm%py6ukrqLHXY%a^D zXz=-;HTz+0Nwp)XIX4&{tBVG)GOGxT`UHwlW1ByfKIAC2Tv@XI>*1Clj<}mSnv^qj zY?CR?Eq%IIC831KE1dWgJ4vxH2D<>082c!$aXvM)|JS?bQV~>5f$jclKa$XytP^Hw zT@7et8e{GSen=v15g`Z4gqAoDwprAlwn|>hfVmS}37l(BdUqU%v$#L0J$%Lwn@S`$ z^agR0f<3-V#C8tY#1H5AURz_l%QJ63dtQ~FH^K7Erk0Q-GK_J!7R@r2XQi44B>N?_ zWr(v?SOHEG*0CKxfpV}j});fvhbh_HIP06pm<#`-}KPm6BMFwl5p7H4M_VHpuw!Ya~N$}E9`!>+&ER6 z=Ukfqt<;w5`pN)c21dYPMS~@6m66*w>MtcU? zBD{YO!!8MrYebw^f|R^*7$MS+)3>FGEcS3M((Yv^I{~g*UB_nmCK#kSQ1cV5Hz`c#dkwe0gZ;A04Kh3_nCJa`yk^UD-iz^Eu zdtay4FLXHumD)ADs~J4J{D>U~@r$;X84WJ#Fst)+3=xc_3z86SF>DcG0jUZ;HL(^o zI%o376 zD>EKt{T%1{kHW6W`v0R3O|o_J8H|aB>EqqjxLX^9JEu0TnGsl!$1uYUH~!k$2Pbta z@Lb*#M^ZIZH6lT)F#k&}4Tp3``TvW2qIURZ3N{ZL@(w-cD&vNk>qYwi9K;iOnN@ z$^z>n;T`62KJ_OQ(EGZ245oQPHv^$W8y2J6QznJd+Tt2E-nrBg$&^5U7#Rir%lyn( zg|i1YVEKY5tgb9>NSNdVWP}^gUvIj0xL5=6rS;hOyoVctgX8#(}7LH@HO+NsvkgFMTh4+J(OjdO+#ALw!& zy?+s;<^lAnMyiDJY(c-Ai?yC;weE2dBD&3`gg)_!3rRXz{j}0$j>t6QL{%_&`_uP3 zw4pXG9piMnC9ei{2r?wLE)qC{T?Jz>eJQ~(7s!W1ATzn*^tt|5+Omhqx#j7_IrYq| zIF#)VKl#k7INujD+e>*lM#WL1^q2CHC`$zxjv`vemCJjz*+7pjRV&(!z_3TT2VXJ9 zx#J|Q2dpgX38cyrVr-`&P~Q)jq_O?<*ZF+8$Ar)JO2;rjNnHx0pp2@Pn+A7rsqq)%2(%KpMo=% z3OwVSONMG`^{$r+pV_bzvTFM6KC-l+1Z?CzuV0t75In5E4WOfjv+RA{J^kM!(Vr#8 zYA{L>sWOO^XQENOlSh+qt#Q2{hr zov-6S;C9X8p#gOiJnlGXpJ%`qiwn*-dec7}n0vF+!hgD5AYC7ljQ%~bBtQKLpaIFv zDlC_ZQXH>c2u%*K!fQLdCRSx8+~*I$aPWRSso z5UZonE_<7gPrnXB7*Ng>1#D1g?`gn(%=-C*&GL@6`%nbOOdmaM=ux_rb=rP2gu)ZR(1CpQ?l-sETmk|Pe$^;eZ8 z@tyYIs;!j4;yR$46;=)&YKnK|8qBy0eFrMUH+kskV~hY^3V(pEc+3Fv_~Qx#TKiF3 z{tmu+Feif2FXU3S9jz~+Sb9hDRpWRFW+52jpMMZn6uV4`Y5lEkWEihn*mP!!0=5{2 zfrhfSvq%>g>mt;DDZpK6ttO^PpAWfH?xj^=nDS{Jr1OYog5!+^sK3tOrfBoYzfg4b zF6Z(EKuLlp0c>~@i=H>SKbAG6G1QifiA6D+?t-!q2mA3O_a*ZCG~!9UnQ2Xa_%fu( zJ2TIEjO5~LDRYLw-e__O7%^UD3H~@K5G!2Oz+#(SfxPmw+;G3~$0>jei=2y0}F3qyA ziq|+<6@=BLmxe)^=0aUfb8PRd6+kY?v%&?d1`X-Qjmxf=u8f^S0t9ZUzs>F8O5o84 zP%(FviP_w$GZX*nVcfxSiO)mjYex!ilcJ!&>LnC3Giham8X@+poL@Qa3XKs~PS1U6ffN>jL|mX9?8;+CYW<)PoK>lJ%vEUBp)$ApaP*!9ogKmX;nn2{TqOE zZU4})^x-+qFuD$I8~|=gE5A(GXpew_|G^OH0@oV_ca9C)-Tt#Mu`GJU@*YzQPV*$t z0Rrl82t@TY;Y949L$*r=H&7OtrXayrfU4o5WnRZd*9*V^vTm^iA!xh(+ZOEG{31#z1&8~oY z@M()X)5%m=+qJ9E>ZhukV|nlbt$;EHneH-gNBGVRY#&=@HVPt@Zx3=80gzeEYkwRp z_UH4mjo3ulc+HY9W1HHNhxpn$3Sx6Nr?zrjYx6)1&d@6L3Tbv{Zqb#gpvldMoGZX22Cs*)n-%2o(*j&+gy`>G( zHKAST-O$83E1)P?3r|blA<< z{%BM#L}lk#CH-f=X;QRe)t*9dc^{6$+Qf8m|Jn%+q=(xBkbO53OC^kgJ7LIdGUFwN zOh*ZL;EEKk|00tboxTn~-FAHP^35Jid(vyI(u_d+f_(s5x-;$U?Khn~xC1^UKqLXY z8^Q|3a8+h|(xw+cdsR1Ak~UO>-E1I%A5m`m>p&o3Y<|EH{Ul%zSOR-N#Nj|UzHOl6 zFPe`0&~ME+hKY;R7Hzd4FFq2Ksxdey$7`S+NenMiU)LJ3GM|AZ@`L z2*s>YK)*;cL<(m{Gid4~0Np=mK4e^e5pBZYw=`>4n(Ic928f})F#OKkBVl}g)rvxd~H+NGBRX^ ze$Ea%$i!ZYqMh*MPo_fi}=8OS@Z2KPAT5;UY-#Rzb3o#Jz8o^ZAEx&0Mm!k9;rB*m@y z6w+51wZP%798AEX?^p zsZLqHQ0`;ibP~@g4fugz43Lt-08U zzAkjUZx9P)9PSL+!^fjwwamzhs{4>jbOH9tyaQ8sHak)o^?OKM{$zhWbl6TyUeD zvtq-g&JKwDwvZR;f8MD?{~>0DGrHK8PQ*Eb^K}30-9l^B*l@n)>w(LdNt#u+#)Wey zzkO=tw~FK0iSacwCRNU*mbYHU9U?|!4hmZ5G3Tu3CTfVRJQ#rK=8d-@@5`0}e-4cc zP8j^1yt!MuZi^F$+rRQB)YGQ|~s*)qJpYgwHzYh)vewcy@Jx{TzyVe%?R z_qIA#Mc}a2YwOb9CIlMhO+#CImehHqAC?pM|d>`S0Qx7qK`CIG;mf)?Dt>{9TKdsNgW^|JrKbFqmBmeD$76u75u1w(F zYK(j>!|AyAo)N5C@&qz#0y|5LoBm5lqiSzl8@Q?K?b4pjYswux{x$VTd5iZ;DdUj- z2J+MkHMde=r=pHF_`OZ2oH9m}C*ZqSooi2I9J7@e=CqA#Qp{9AH}1uDE($Ds{)Tq& zqp6;gcYs@}>GeRQL$Yf#gfB;m)NJ-^ure$c4lO}h{1BLTndd?;y@uXGX{kTM!8sqK zf@{`68uK0I2YG1DoILPG276*(s}F_{aqFjuwJ(=@BCrf=2F^LG7+l(u$a5{b-gknt z!+{~MILD~IHUR{RkZ#3qNjq1R$^>}gjd#CbX39ewvaShTh@lhX%dBszxxnp;11eA1 z(bN{)VG)THM+eG0kV9^hb4EwdVv?Y)E+hZz`@Sfk`*v8Sw`I7dG4ClE2Fwj|sd1*5 zNrN^lh9CiC96%R-12Lcod0N8=P{~M~<+|1ES|ls0JY;&dj{Pp+=Wp-GIgb$omqakN z02Ns2J$U82w9QBjQvNwNb#TvVM?c~eW^6t+^S(JqvMS|4je+XKI%ppX$hNT-T)q;& zbac>FKnw*Z(lt!+4=3276qSU)2D-vgKHVQl(U}ge`a%#sdPQf(ohnpIBeg}3v{_2p zL!6lRAZT{F*K|Bw!%ngjEAoz&Q3Jz3+vcn4ZdWl&#Jl)cWY2)N@?f%M{9^FIgV9lY z*W&8CQ%ELj;P{wyGZr+`)qI8F$-;H#pdk6ltnBjcmP_uE&pBDWoWwID8bA8|r0PTm zC{u~%olgm?H!6&Z(#ceujj{q(3q7N(_}3f+;Y#_Q zjknR(hpl(qm6wtg*>0+I+YV$&DR`_9;DBOMDqHYj!WT7}hgnXI(gPtzh3n1U!!b=1 z+Lu>R@PeH=NmAIz_#CL4-<}DnTNF+%9)pbcwTiy=SH+EwKiRX@ir{!wXMGI;ww$f3 zGGnfW9Uy#Et!X=kieQ(BJ@c*^|AGVw9fN)@k}|_X4w()3>MAJtj&Qo@yle@@&jU3_ zx^0<43DO2mQ~eQ^@yu!jQ_Sq(Ruk%5VNj{NdOI4F%(#TX!x-+sj(+jfP7%Z?ENp=* z$4Dne3m<}%K@Q>(nPeukj}AjN5Nw1~Mn0{C3+Rb*9`Ww80WC;)qpH~L`I=1WGyAdG z0OoDd!b&cFR!|aN+m%xF3_h=9d4lTPGVhg@A!k|Ly=2*Tev}DUw!fQnS=sWbJ%+|e zcee{0&0XKQ^@ot+JJ~tlGN8?mt4UZ~=ZM+#M9TY+ptlBsPZa4{5fOUkoO;^N>i8!+ zG*2z^wSM2k``nA9iIGO0Q0iRRlSz;vxPT4}#Y>_@>Q@SRe;JjYfRMOq#uNCvY&;3} zx`mNh4q=YxxqZLijXo*FSCA(F>nm(T!)OL#OeE<#QUvE;9MkQ>UXsElp?>ag_}M_H;OFa+VR;VrrFGh2c`=`$O&xokloKFu=Mv5gvKZ5D6!`&UZbdvuZ$L&j z!Sp|A3~@R9<5tjRfdGFIDC-X3IFk)$>lWoZgF@UGa^B45a9ofx;D=FG>jP|~O0gsr zX6KKN^R--4L|)<1?*s?JlrTQ3spR}to!zJeH`uyz;k+2uyKS^S|y*u z$EYvLX8Hvi+S46piO?dZxM&_u%7Sgzxp5-haF<4et@OELvbGML8IO@b);xZgETpA* zo**TZ?9r$wQ};$X+j!aS?LI(uFWMlroRaJGhIvZmL#0J8MeF}A)Pbo9vFrI^w)6bp z4n>0eS`i>mkE}U|hJjlq?o_DG!=(=jl;~O(xMg7Y{kDcdjQIYwx5=*3{kIzrFBca@*93d`G^@c62S3KzY)%3(8BJKd zq;VA`inWhd_{e$U4UP!vPD=#lZ&n-P$K&k>$N%&3qsCpf)Qg&{2wingy({SF4+;)W z3G>Qy;Xj>NOHT)ky?1EwFFyVx)(pfje%F!gUC zFSwGwPad4R$oWj!m6hrm#HfzB}MbM%q&(rNF`&*Cgx=h+1;JvY{r6xb{)=NXlm5;O8bLGAN5U`0G?)d<(XynB5R3ZIc@BYb{Np)X zR;6`4wo7QG-qkIwh(^wmgKLM``Ve;n7E)$Nz5@0l6>7G4a4mnn@3>_+CcD4fZis>g zQU@~Ufj8$esd4baG}}3bHY#!>u!0a{zH;z@41@Wf}kaW2n>Sz$wwQeWyhXb6! zB%`QgjJTy+%elb|J3Zj3Wg~#Jr00EAzgIxa)O}%0XYy7tpn3#~CgVu3e7{*>d>SE& zK2ZIu68{{8GtiH)eX)-f%I6rpfAT8|B%BKwvsciMeC*iT?#TM=sd(k6QyzSkgBtU0 zOdFTmou^Jczlh*jLlZ3% z!BgEu$TgHBB+GUZjFZN`fqSvu-8)ZUwiujH9akZn2RV#kd%RVO1?^7Ji~6?W;5xZx zzDNOLi)f{TIbFBu7-#NeS8TMpgV0W?DVpG=WZ}S#MoQxP)!p?t2tvr}lD_8g2q|}U zJm2t`-dQJStMn`C99O6K7BlUO?6NSWwAU;nLOp#Bai{wB%FUOw)IoA_=e$$UizmQ4nxLMHmTyWUVfU83=;b9 z3hIv12GrI6(CXDOC?JSH(NEDS`^YL9x+9&O{&V|dTBlnt8%Y1`Et<~48b3z;zrCs) zMN*dGME_DmVKwuBlT!6bL!J!FR~_%i8b|L;O^6q!L9oF#<0fCbtaIc* z(dYrkePOeRA`B#ubDm#5_ui^gCW+Y|t1Bne+x4U2FtP44vBwj;m&E#_VH&nMIzIIadTiND0hxXm`VeYp!@mI)&*ty*g#FbuEioG2_`#{iyq_WDm)ezs%}YW z9kG8ButqH2Rc%d8@=lMp#gR(t69gWp)*Jgz=teS3qb?Jfj1zWbyKosYvD1B1Q50NW z;YS!J-et-Z80x8FelrVpog&pL5VwKwJYtecgxb)av7lfTmanIYPQ=Q5H;a>394bt< z=H4E<@@&U+^VFRm=0$w)MDu8yo;W9~OCzljkxT?1{f{%0ZH#(UN<9wucXWywK*7-- z9MJ`qTGP$`rHosbt=T2Af%Jf}B1FZ4p(cY3y}K|R3z!gtTiokq*xf@`lB<~sGgYzC zgB2cNA9^Kny-pUTa|X&ajEjy*p|nNMQ|c}f&RSWRz+YkFF5%u}^Kt_D&g*Xp&u*U@ zF5pZdP#k~dcH9C~PGem&ONCEMcLyY`WqV_c^Pnc2^y098J#_>lN!ngZxV(e{Izg(X z>uMrC98Uw6%JW4CG-X^@H7nFe8dqn#=Pe9?^#TG{}`uD(3*v>eOT;UQ47bh zA%N?jW>$F$nYKO>|<9U!tMt<@V#uDYk#Oy5nQu2W)k=mqZ-*# znj5ZIB8NBs#+NWKsMwb{JXegcc!F8-@d`4y^RQbJLvaWQ=R>_e!L!Xhml#&=JFWH$ zbi9&U3_se$Q+tSq@a0*&YT0BVLd_J}7uhK#A==mzG24L)RXk`iRYq1GiZd^~bpKlH z{!-gnuMv|il|m2*a42i-xcMG5`Q&L{WK;Rmn5w=bje*CB(dpX2qPVTIM(8&AZJZfG-}(=;GpYdO&nC z$9@R|{`KkpWk2hFBVnOduEfgw7&t>aPb?&3cQhAGD|c-hD@*y-W!E|+M~xg}l#PJ$ z3K%&lEWpRiP>>WeC?GJl;G$&#K270}PU#ora2P??sZjW$+tNvu+%J$?a z+178+B@8Ro9rII&D%eX5OsaYZk|+wk@&sf#q&^8EYh9jYMU+fI1iwyb;h==7Taj^T z$g9fd=DqIpV`F${>)(^%1qU_OMHiCJ+p#Ko7yGDSKgHoiA<(VLAt{rP-O9kF!+<-D zx?w1wh+Q^!ylMi&_N#iF14F)(kZs#|E6c*UhYb>v!d9>8`v&5?O!`g5H8F#-AxCY8 z5A9cIJ`mybd&p&A-z;XnlozH9@?8TyG#m3^TqPBv<(I)At|R>W*^|M5t!|s*tg@56 z`uqIVvcH9{Uz))nYJS(kor^u{3I9}m)e^iSI>p^5UY?If&qZuU-+yzTV4abwsG3Yv7%QmW$3nUexRvE5-$B~KJSiuP^{Fk`JavIOj@ zShwPLDB|-V!=P04tjNxFIvx5ObjVJB?J>xv^kQxMC4$BLH4cGm)Ew$hMz>)5qKgNo zu`ZIL^){if2*a6~H1VPHD}n>pF(ES4_{c^B46FU$o)OCID_r_}tNJNEqBQYoC=2%> z$Oxp3$&MLJ;Et^~rukd{*~d$K@4GK@skD-!IpH)_xK=QF&CtrD!broFJYV1#&vs0- zMohgn7J~W*S~8a>R$DUUTPs-gl^`8|Fep&$MfaBo*7;^Dl8J5t7brS*;EK*`0lpk5 z{~eQhQeV(R7%Wuk`T$3dF!E=W!Y_gq&pc>Dmcb77;3*r5S!5MUC#g^zXqn4htEe*v-wVJd#+DJx7@7?vtbrb_+`^u&h<4phMt!$;t^RV2oUF6j$H zbRFZ*DGCTtJ&xyTW1_XMF|`U|oZ{c;(e4f&>4mUaqdkihnSsZ0dB*ox^!*Vxj_>KP ze_Eyo{dEibPr@*ex)89AMcVzC+kRz81&@hhtvS~)Ua}mf54Y~zD{H;hJax%lP~xS%%VQ1-++nEPPwMj3?;GBq2?)=Q`Vc%9fMx^ zeqh43fDcD~I3xNawG+?*5|vo{FP;SNYRsn)|441QtgP$R3>YGqStczz*RKwg6J;u9 zsN&+cWWhAJ>_#odPlR#mjh=+W-ztxB_}}&`t>mZx7FHp`(=aVHd`ijNojj3~rbPo5 zuFY4xCqF>GxNNrxoA?9+qi#BIqwm}>w9Wfd1|MKL=1I6BS1{Q|o5^%`9YA_0`$HwB zgA-0;Q4}G?4y@m8N71M-%`-O9JbP2G2S2m$PFxc%ncXiowByqzLmOgwCtJbu0hH=Y z*2v4PUz_fYwaH*TxbXulRCd35@l5Z3X%4gE6=@agaA&gP@F!c#(($V7XsIy#)h_z^ zA;(B>Sv8QINW}ZPzYC+v6d|v6`~<|e4N>r-Q7`0%{cqp6=yG-D2b|r3#R$-p;CHGC zRgY=-sSwoQB-!*p^`&5O_M0w%`}jRc12FG$Sd6EXSWu7!#+*qE6(K90eLWg`F_0uPbW>6=3l+s>d_6F`;*!G$@L6xMTMNnd zEDxLDRc;L?VEa5(Hv;m|LWdnFMn%(6 zr@h6P-kCIwnD-xfbUUPy-7FaS-(c-VZlWkR9e!gAzo!tm{So04iH+n{Bs!wVHUq#X zyL~tZYUazkeGSd^wmnZgGA-ze2D?D*N``fwPnU0?0F&)xVF&{N5U#!L*{%hz?^b|S^Iw1YM3ylX{=jtgBJM)a!ppgsK{JfFM?5b*5IKe_-&I?cD7V-r4==HIKVM=D{0Z zvN)6?;BA}?(0Cq*D*i1#k+2$2r_iQ#If~RtZ*1U9y(vugEC11&^tZTs?{vJIEkN^< zNep2&hn_V8zYp!C&(!5Jl)IL{rT&+@&m4f|`7hy6cHoMRVfp@s$sDJ_wl$;Tf(ld=49A--!r(1vEAe5T!>kb| zNEpP_=`(bP$1fBy-uitDwEe9~fo*yk#Z!bwiJ?hDde+}-SO_p{)?`-;nybjdXEyu0 zt{I0LkC|@YpG43s7-dq-79sd^rDm&JTdj*?ubXz4sotg+gBRrH8!T{p@g1uY3trp^ zp1TRnNrsJ$AXo`Ba^HSM=1t=;1M*7*M$*k!hdB#I8g5@2Jro_^I@xza2#77i-6oWv zD6Xb%Z};iS&Z{dNa78e3{?{slL=BJN#u3a&)2tx6dO6Er=n;wE&<=HAvaVF|m+aLV z1Rf7cgMkECRdUcUtjQ{OTs3(MI z<9+)!_zbiJ#&O5xuuL(hQRp{B3vih!!!l-40KuvyPjuwXO36wC^O6CsKz$ant~;mG$G_RGg5p4 z((iTqxc!(hX7f3h14OB4Da4$I(xvYL69$Y}gh5cC&9J zTvclCA=z>?B{*o(PvSKDb(57WpFBu)z3taHjnFJT9?!{J+APIT??Ll)#@%9=hV?_U zJ(4$-4-^FNrhjd^k{YLp^lw0wJTD1NWa}!}EZ0H|<#hNaYWyKeEc#l_xtTKFCs>72 zlkH+UicWMe!GMNBKl>6)dtjioS5mb@8K#xG2;o>EgQ072j9K^;R+u57lpEg>|i0< z&;sZ{5ss_qNzb5!Q{6YcbFJ^anQj zJXgcZx$YC<8_%Z<-&v02LyfmRtO&7c*Uu7(GI7S2D01Bk0-tg`3kk~Rb6J+A8bahI ziIG#EjO4o}W*@vjHgcwu@NZOCYsnV@c=VBRL;2T}AlMap(rX{qo1$jB0ApoS@_rCi zI*m4PKs*wqEA+IVGzl-KMC^F4oNHMZTUj2yl|w3&4f7sKVjKn>(=W$Jk)UL?qu53^ zxNi>C)?wA1834)c+!fYLtZ?8-Zr3w|1V zzrumM+W3lIeV*oFU5_cMHf@SXjM%HP6{x-p+vMJHX39+)YDLmVM?CZs4pd9s4gx6Bi%2gpL9ehbeK zvsuH6wF6mbbB6A?HM?m~glCV2E;una+ES{3^t?R$!NWlue7Bu+mSyAkLP?#po;7m9 zJ3xqZR7!e!zDj_G-kF#>g~67auDmtzz!~;!lq3GxVy*9}g~M8j1-Ue*E?QwqhkQGp zh=SDtKFK%-xd1u(h=0?Ug3v*3&qHeYLg9)EiAx1q>ix>Z^$I7zPcnD=0c~FGefO!)V_Gy@GX(J&ULG{a&B5fTP}qryD6E} zFV@OVWQ)$Gr^Rp8{*y~ZbXx8D+}FYgKlO~{8td`!NnP_pb4wW=XbU3ex$V!^BoS}> z8gH6dkcauOK+3|2hcONOU_y`|7Aako5C*2>HYtcyG^ zK*)We{)O(@xdpcyZlRhU-M*K_6oqxqFXN6E3F}mmN?}jclaw?BY8PbG;=30KwItgW2MEx zp$v14kA~Tvz{%9?!TWld6^WMEQUJ2?2}~aVTm!0*XrwQz03wAWIa$bMS-e>8y@(0v zpA+fJn}kG8G7;^p+S}?c7>Yn4HyGn$gs=;Ym7V)B;qz@qHOSoa)v$F?cgk^zVZF7lGmp7E3!*#Q3(P+WiStX=^bk%yGh9Hk}hAiENz!prOg|cGrh?y z1CVKp@cz`<@+e!N)M?t=D)WwdLH5yiM11*CmPb3vL(NJ!tw_uI7>dY*Asy~|CytC) zFJq47s-f4a$^LkGQS2>w8;UCd7H$2Nd_CnH<2Yn z#^R5zk6H<<@bcBl(`~=I7Q);U50q(%TZq`h607r6c`hp(@rsbY*28+%QkF@aO$ni_ zm8!HVs#HjUd_Za5?NX|k9YpW}6noeKl_QxFd`&U-oaA$p^=UU+!9%zkc#0tANFns1 z1idCwwz^@CB-e_rc^D`awL6z7kwMl{JSFd?Xk%YUU6|}NCH`Am{=_m(0v1+%t|ezg(8~H; zFt{9$$S|?iq;J$0fxJ(sB~D*Rfkj{!1{G z=ot~q2hn$N;HXuDPe&w^)|BDEjE7^w_EJ+}nnlTpELctao^iN{&P$Y>`U!1bbN*6YMGP;o)3-NRT@xCUrviA zt}JqFaWw&|eH!c|(`gAK5O`iqi63@Uj*$&UL_~&&3izU`k$aROQBtTSv7U13H)0DN zOc#TCas|EiP^C;qm!Pxrk->aqsL%9y>gfXFf(rqj`+m9NQaIJgit4hTM^|5OfX!*H!Nt;$SGX{Rv<3ZlwZ9SvJ z{=R|wMuooY&i4-0Jw((p{5(UhC^Kl)_z(rt!YUCDxL%&0mVnj2dzT2hu=9(HwtsRg zN(g1DlTdG0gzSb=PNFj|u?jdJhI;fI{q&OR?(Yi#2jiWYbhN$=jt5!Ge&FkAh1P^N{ig`!TYt;`Yh^xIO4l zsSb7^ar3_iWsu^&R#(nh1TVv8rifkPt4Pq)&3(HiXv(&P`MgVKfVvqH15tTC7BQ*W zq7%!qU5z9`xhs;)RH&4_O+Iq^x%aLh$g#Sa>KcX~&SXA3t3_ONQ6VD{)0cCb1Z;y1 zYs71o=~R{9OLahGG}GO$iD-{+R-cpz%-tISU@d595D_N znUoYm&Z830)`C7h40k|fe_H*IcwPLEG~m{AN(N6-SZB1op_MHy%P-6Na?NEyw$IP1 z+|U>H$UHX@ds%fSiy6bZt-UCbC{89HPnyInu7vh`qOuwuG~@@_3CAai93n>9AFt!Z zitkLbHRFFxYZwzm>gDP~NPLjK(qgh)ff%jqRU{}jVxQj1BluFl{=F$Qit;%nsG8(D zjodpmCLuLCF~=v#x}>Ng#sgb5(qi(Nmf3ybXE;8q1u(CBV4|v=((Abp#>n3 zYh~|kKsM=}Thw2XKoNTNuPW$YQVCD&iK;ntpTcRgFvhQED@`hwYBv)R<*6g6tgw42z^AeJ3 z0KLGcTxg^P#s{jFb?D{`{~#kisi%TsGH9|^nGRHgNSNzQE; zoiZ`pv%d5xC~wvk$n7SK_b-Zn0%OxDIQKnc{Q@(2u~2RP=3 zu|QmN@h)ol$fni3)a%CURWY~}Zf4aiJGnsa+Pi*S9r@NCWfwz!hZD9XP4xVo%3Ms+ z-$i{3#=*PjmCEycYBUhOEMZZ0oUjumPa+}_YPguzT28v}0Nf0fg2i3y;lW~Q?;?_F zpw~>^0bd~rpe(S}t<~|?ia|j=B9(X8|IQnc&R}m|FwO7bM_3HnX{U{apYFv+2zsN{gP;QKSICV(wmuO!KxCl92Hw;C?4^8Yrz9XP{) zPbwn_?{*m>>Bil8BB;8TkXC~Zh4-c;RNeC9t7YAl5cRK?nn736bsQQ##g87DlHgrU zT|}6$p5!3949SIsaW!8nS$e}!^DE7ial|8bX;NrVdHXSpkz|%?9EM;>3K3R0AFFm%LN$P&tj#1Lsy4?PubslhO!dIKt@yXMF z=aBQYY>%vCg%Ix1n|tUq=@$2!N4s;GQ|8$H!I< zl^Dj?D<_-anL73Zw$`1o6WyfY&_lAv0uh@b(_l{wz?kd$mpf4F=NS_s9IY_g!^~yz ziHapjN;KHAl2wq^>!U9GdU*SAP}|jBhIUg;BMAXh&M9S;^tJy*KgPgNuXwg3Zui!) zIrCM4*2P7TbaKUAN>xNAwnh+dE^$eO`D3)%v^sH-cmxi4uG2I6`f#M~^`eQlrFjmr z7Md(diTMC_L9n_!5};$~|LVPqT;Bcma-9}BwS-qo3GZL->QmKl_M^-y9`*5_k9cL<29PYZq;~V9Qa~zm67IIF6@fG(pxD(;UB*{StfT zRhBG^iDW7NethDeTnr{(Ca-J(jFOE16w%Ex(d@jmN!ejrV_D3>JR$Oxoe)jPzaO9@ zT9(qoXhmi`@qmFNlG@L(8eL;nt*+0^9#x}OxQhlOtlDqEcg^>k3jUu^7}xd18E41* z2yW)g1L*3Us<`9WJmqX^fKLk~|I{+T{BZrjOkPF40c7@aysFxUJa1d77PLqZRkMYUgK8_G7s~Wpd8+Cj||G<+YH+zon!zcQq{iCIkw>apiq)w<8^)|8=ljz@?+YlCuYotd~A|*ua)2}gg6#=!kQL$4NPbvG#G3;a?fFPvTlmJR>fWBe(D2HL$lA1PNh_5*CkS#0lA3qD|61L1} zpAa|y?Z%F96#>=_5c0D;K%yI@#qZ>}R9`_a_C&*@>PFA`RVeRcXVejHKP9>QT7L_e zs_!~RT%7}b2CIN{s;>hr-lEA@3T#2b{}YE6c+YA0le&$jcnCp5;1-qH#6K5C9k#i@2ug-1pfj1+P|4KIGw8r0r zM^Pf+oK3!gJ%36S3m~9Cd@a~bLSK#_o*ZN|LR6I{J#2?#tGH31Z1*}<_~v{JY1JsN z2Pq(;YaSl8K$@AGjj*mkoxukMEu`I?3Ym9l1L;5E70(t2cm?U(+-@JSp*eMAR~@- zvBP$~76*r}u^B*pE+0Lwp>FU_e??t-y8^;$yO3-8c1&MlbA_M)zA$$};p8kqv8fVM zb%7(=p|hMU|1d$<(&j;;gSe>a^v)HqM16WdOM?D>v_W`_ zY=Qia`Z~3!%k$CUTZ+@|T3qSoLaO!rx>8{Y1|6tA8D9glBx2S3x;lENc*giy2}`Ph zekb(?=zA=aW0rxwwKis`pGt7~`C^F8T3o(Y8gTA4J%rPT7;K)W^dWj}5pPv|zCP@( zKq4}&QUC;j>a?PwwYEzlr1jJJI70Svq$Xo}zBcNPH|4T@@_V;2LxlX+9mKX|Lsxe) zZchBWcC6oyKfFOyxY;QBGikY^)$tgEbs-PGKT}g$k0|MB3T9(=+Bv0AGtWjF@;$hc zq*8b*i~OTF0J1wYxk?0ZfH~alGo@wi2el7^5;r@|bRocA1Tm_VVCN0F`8g#sK1m8RdVw7W|#XJ zU9DKL-CkWm7Rn z@k}YtG>TaFn0+A9Jz4LRZN-ge^_zSW8Rs{h^oADk$`fQQ`(MU(& z!nC#QI(^J3eqWjiIMKbuY;=pFA7Z{kd!-O{6CL_LOZmK{o@xbo*BNxF5kD{<@Zbn#o_wMe%P7Z1{_j$AuHHES59n1|Q0VO#|K zA80;1w@*NGs!M?Ti+g}4fK3g*9-n>*eE}+Ms-PBpFsspf40;uyG?BHnHDDr_!=EO>B7$kbdEXW4zoq=ikjFh7wuJEGrzq3(+oZTNIPB z7*Lh)dr^1Ym`O_U&q0o^VwIT(@|3EukENu2HMS+9ott3b*ma zYH!cphUe2E&6zm1Zc3tPb59)-BB~3) zSUUpX!RDx6+CnP%7>ag@(On&>O20AD0%1#TY=l6;w~bCCx+>`ZX8lKPfu>8R)7w=Qia`A-pTj3Mra3p#Xy?2|9snmFnEvdsMGw5f?VxH@GKc z>BT@fUzyw7c)YsDnIq|spYBp`fc7R^8o&n*_kAF6R|9V6O?r}{R)4V5#B>#@w*aSW zggY;W8);%Lj|>5DI2M7%UpXam<&<;A+duhP$$G6IZ%72UA(fvySTQ(Kg||>iJ)E)i zseS3TU0wwe!Vn=k^0vU}C+);+7S3B8=Dn5wJU{(;LI& zbVHhaN42L$>i0;46+~+>$fUbNg*BoOn37v>oJ?0*1mSxMf>mR50j)zr>&ACCb^~;{ zy3105b3@QaLW?hdBY3V(dbm0XH=Yx^CYAKfK#3UkcopcNe%d{@ZL2%Z4wB7H7ySlzQHCC=LH8gC)74cDr8jZd%Mj z;TKvdKfI1w4mbghCz9F!O1@gT+O&tSFtt1%F1DO3MmKQvjJmGx$+kFtgl>uZ6}_Z^d=4X+*2mw@L4@zRxUMxLHwa!arn4O9b~V0`TM)4Ufk7U3n5k9p zx;HeM%@^H^LP?%`@z+Isq)6K|RUs4PsfJxeFT5(63)qHUOpm0I0NXf2`X|kxH3FjM z|A^D~(o>~7Ke!j41OY($#5m63_egK>9)8`$q=^ko#$vb{N@|K{#Llm!&MU@l}bs8H;c z{2-sQ{Z1w!tDt@@C+(m6Nsn?B@P3&5K8Sv02M(R?FhHw!eR4F*3z`S;t3Th#6mTXe=_ssjH5umzNmRpu~hk)c?sTx zz~_5<-=w!TM~!AZ^w$d*Q8yzq_K8iq^#5IDwN@^Ev9HB4?e1X;5M<9n z>JG;PYS}gMklg{4oD5JsXDBsbo8m03-CG>OUe%SO*AihOg&x8dP3R}94m!+27l{yGy+`SY(!4FrXh^Onvh(y>3a<)F$^RFE zo|sgcU%jk3I3)K<9_%WFhib6lQBI ztEL1IlGD?C!N}9w;?MV-=ycnh8S*$sOSwrr#3Ku_$d-XKAWrG%OcfXS-=i1|{VcH6 z?OtXaElm=dXWLcip{EOYnweFjew*h!gjI(*-IBmb5ZDcGe21X~0!AIfmY}wbdFFm< za+?YK_d`Geb(y$YLp3<)uipjxCjcl%AOdvn{G72YVc(jp${=S6+m47#TkwPdH|^in zhG3g>&RdjHGG-Aj51km7wxv>wReHK+_@wru+~MKz%GZ?blKo@CFzg4%D_&4$MH0YM zso`YU>Kwzhali!#a$Thd(tZrNe%Rl_^;mQ%PoTOyF3!OjcIGsRFiUtc(F(h+7i6{JW0zdprBJQ7CT{ihsBKDwT zG6!PKH~S?_Lai3NftJ)~4#qVvD~^)_Gz%dt(5wmbT;j9Pt6YkRaORBV5%zr#6M~>i zGR9%pJDfJ#Wyx3Vv}PYGiUqm|w0`^_{+tSnEfK71SP>FUhS(9HKEwTg_j?Iz=Jv!M ztbJ>R-Y`))qN$(KD@B*l^fN*RJJW|(?V+6`z4DVzmyuZTzYnIb0y#0=@O=|{KvD>8 zDcPX+ZN{NI`WUR`4rVjZ>aBmxyOBDv5f4(OL}{8u@(c$~a6~b|Bnronj(S?N68%1G z(wqF~b2QXB=>bJevEhIaG_({WDL)x5xG~ZexfOmL-~CIc0Fx<-&H6ql=u-ii&VVu% zp*`HgKe2BlXmy3e)(zNuq3{BLXSWTOKp+By2mlOmuBihZJ#y4B-qOGeonioq_5k5` zy)u=#3h@$v0p|}}1MHoYD@$U9`|b5nsMs@xXQHvP8TZL{8bGo2-D@V%o(W(v0Q0FI8g04c)6C5_<|AGHBKNZA!#TPOmZ!nb;Q2PG?SCx3Uio z9)#$RXG9@lsgssongA7(K*zQ8#n(zkSULEP!=vl}^_iyyE-z$RkRf4gp{B4zllZDR zvkQ0P#~|l_=NkWhYSyYZYve9C^uOMAd&G4;ysKq&HnjaT)G!_0;_`>DIv@YB+%tV& zz_4A2!2O~vF`i;?`OwV!;OC#zx6oiIRO6fZ0!#G02X#y0%Cf2l&vN3~Dx(o3LNiBw zQ7MRw3zHiD@NW^bf&44y7*$rQWr2v{yc`9HKtCPSA-^e>8*w676|BI2EL(NVvpx$a z5qq+PO~#KtkqkYleQU7q@WRFdLH4pilIVtGPx4uOabSiF`=}*R$_6b6w*GucK{XzLKg}dJyNThuFU4QeBX`A3;jCCeyQJ7N5 zIW02WCxs$SiRZ1Mg1ZFhV7GbE3L^ydx7sB&rr~Yy&inN#4k~vJXQmU2oSUD*o?j&J zhEBH?R7I(dY^g|0&weiWAo-Isu1Q*}k|b3{n^o9gvC_n0<*mT!YA zVQ$>d1tsp&Zr~2(!1X`RnB^%+YflJGZM}zmNSD?&( z)XU*QGyrUDoG)?uvDzpPt7QT>1rSConVreKAT|H(?)kb;Bg?28G^I70Q%T7Wkv~SZ zAXX(gK&ST)ilx0bB8uownp`CP!L|dbe>f`VJeYGW(px700OJKEm~p=KRtHLteis?T z+kgk^lMaJ+95?Azut}N($heRW&<%I2r;MT}Rv8i8EyC!cV+{Ao1nNEk({kR0n+wIwkUH?3CI)}bRiNU;^|N5>UAIcJy;6{Bg{-d8UB*-#1m&(2% zc0{O;e>DYxlR4m>p`NjsNC2>UXBWSC4-+CxwEUG4_rTgQ;XJ5<(KSDN{j7O`G-5ty z0f5sfO5#gYXh%*{N3jpGlMxD6v0mJ9ob%m>*FgdtEQ5A z2u9DppFmOE$pq<$oI+Svf!-A`3NJEpoMl?Eh}qU#i}$f#r=ujPHov0Zcwtv%-X$<^ z)~54c6z$lVNG3fNkwU|237Gnc8QPxv1=hqwJv;t2TwMo=mVr&dPHd?m_Cx>`Pn0kc zF>RNrN;yq_O?w>)g`A6PHai-e%+};6I+7Q!Vk+*v5^m0zR*~&EW)(xa46L>YC1NjV z;QRmvYT@v?S=X_pg4Rh4&4oLd+YMc3jU5Koz;obobbZ9jv@4h@fKmE~p+il0sM!-v z?%Qrw7$yi{w4UeOuU)Z2sd+Mwm%=g*85Xwbv+lz##S#fzsvydy*F4OG|H%d2Mn+&I z-Hy_CeXXn)cI)hT1suB6`!6Q=jR^nwwE!S3W7KsqvT$j(ppQOHnI9nv6W3aU@0;Qv zkdW2hl2K9escIArA(*AF_sZsbi7lgf_fO)}a;`7fBHbGiVz2Ohe9XSOHD?3(Aj2BW9z z8BTzw*bJRT)iFS!fa161LqVnw%X{5u-!9>U0WI&?3Ss6q^jGR{^j&T2@7PaW${HEK zK6zk`Rf3Xf0FNx zZoYmve(K%{F8y2lvjG*i;NQ`olr@MyeMScN0uR1{{)zr(&piOg-?BG_^Rv&s4}p)q zvp({p@|seJHFeycie?4Esj| zcz>5Zn|{)N@;3MG4E6y-fG7ap&l`ZF&wt9l$=?lt{sZ_fdj)&~0t|)#F#wU@pzH|Pa->+T|zqT)Azl@)?UjSf)-^d@?wCorv#->YcuX@ceVkFveh#@YB{Nu+6`A?-o-C4<+mu~f=ba= zzzC}GIfBM#Sh9GN-mtxZ}I#i?!W}Q{(^ftWc^ix2E3!G~}83fCm!4ZU+l*G6rG_JD0RdD0J z(<#~zQYY-MrOW{z6qxz$?8hq>Iod9>zE<7+E5rGmtmdZUd{#%vbN4wd#UC2^Q&Sio zx6VK{!t7lLn(uQ?v|bFaCfF{uds}5kJQqDwQ4hZ$z;#?Y`agR>l$1pD>7{aEEGBzzW$LbH2yCNPO_M5797wul@_Zt zYwcL)>X7#y}isCw*3&}QE426F_xn`hBW4dHLa-n|s=kba~skh6@<_3}eZ)68xvAUqSYt$Q>A0hNp z;31g#U*UU~|7NQH7PVNg+4H+9U}!&oB#&l^#KTWU(Gz|2kR>kp>k*bs=dx~65cYE9 zH|_3E9R2s;K`8}{@8UENhw+O%EqvVQlMB|J)_x2jJO?uLDc#Y~U-x=*$D>)I|G~^H zGR1$~SvQHibu9Cu%(AG|pTY^RO_d}WT4Q`Bj^sIJwA{!f5~R-Tu#fbUcFD6mc0h$?ACtRjO!G>`$ z_g@CgYM^8VPTWTz*U-`eUrM~?me_*^40#5mlK&c*Umd@J7yD3*WWY(qvPb?OB+#gl z3$g>f-8|)!s)I2_(j6_zscqQK<%JLpr#W6wR9?59{g)4^ChR^1{r`skKPvd&S|~J5 z^!Pd(b>Uv9Qj!~y>k^+$%Amjae{8gC14wICiKit=H0=knhg(dr4C1GeuxbC3WV0;& zy#GIv3@1>&VOBDej>fu03V1?hk8{-jCkY%Km;U9TvUFy|hHU?^5##nWzhw~ryk?R$ zWPYh?itY75x71ks43$BXBo2}z7yZZmf3jlvTk@#Qdg5=3^^@bg(xS~<*9{NWsqMYOZ?yMnHF`v z^h4YaIYSnHagQB%#5J5%hB*r3y8-XPng%e}fKfcx0rrczP zi|S&|{oJGI)-Iy}$zMX|v$3mucUh>2{%dZf?;;hF@V!Cg_cWR>n?E@-Zv21Nd`k&; zg2ZC-*%bjIcCw`NCAT=>q=!W?UtW)bS2@?Sm^8i|0n*AvG3%k3zYt) zi&CDu$(Jnui0^mNZoUQuYQFN|1 zt1GyY2y4ObwIMx4p!ovi$zq>Du*I3vVu<*Cd38mqeqLpGRfi0I!T@1VcSD%5HE)(M z8c5VG!B08r%+pR>GHNwW}}*R#(u=%;``EJ=u;X(vpmsvLkTKEMV3wzgTHK z1dG$qz7IZ;>Z1S4?$o`HF^G-qdE%`L=S?qgwV-p7fauMQYGYtOU!C>lEm6%mWOoT` z>oT8w$Bb($4xusTPFwOTYsncqR=QK!CqWI|`NLh~H1d^K`xgz84jWHyBj9Ae&{8zH zpj-3{I7S2E_zKKh8LJ8U^E4M{mrJo@J+$OY5Pvpx>K}sW?AUyVI50`I6J!Cj%$~I% z?>--@+tMBO-}fb)OX0}Xwv9fuY(*Q4@%Fq})u9_X@9{|>qXdn{FJ2d>;Ae-KJJP+5 zT7ZFQq%pn*QXS2ZT2{Ga&iCGqA)dNnXUl?8%vqBa5Xb#P&*KkJd8GC=(21wCg-t-{ z5?VYa5PW2;eJev30S(lg4f&mX8&d)mrV?{u2;ZWHTd*Xs*(Eb&mM=NA^{_tAVIM_7 z=g%B-!R;X8?tm;av>S?)$iaqCdF}D#2fNxzK}lW@(&nzneN9)}r(2^}RLb#6w;D)6 zTy&Bd@e|78auV-#UCn(cO1^p-J8bi%<~d6WJy_bR;3S-%{P5!sh^Kmuz>JAH!^tmjPB6?z4;P9I@K4%JSxt`{+oLUd^PID*xD6)3?CE zU#5T0!dNE2_kLhQ7bE%iHB+hjDO*Hp?OGFOEee(TQe4D#kl;I9%ko2mY#TVHr1W%> zn$o0lXA`jlQ2%r;NEaLvpdo?UWuaMs1A|7%X2|9~A~8>D8Kp-y4I6;)Z7Zb~GVtoV z5JKj~1%)C75SS>*(~@ao`~epf!d26 zdJ_V|pQwjt&Qmx)&XSbxDfg9Q^pB)eIke!c2(I1;sm-gv&uDlNf&H`p7}UCICU_uPyhK@+*a|fH6x;N0&rxbngB35MxqO zRlX;j+ieh7{xD(E`*P7#G_%ljo#(}bdsr2!os9yp1%X>;GmML4PakDL?_G1^!6+o> zbN-?CDzW$8Q&hiB?zp;*LS6bTW$@QWB+0@aEOm-ybPkhjiJZibxTBr zJig^}4UB8>BEeiKnx6}AmkM|+^sWNyN=9ZcGLx>AZ zsiin;!-n5`k8;)czFpZLeioS>L z%rS@{hG(BF?c7HvQ-oTe`gqeOw<{Qk8{3#a(2&_qmG>t{4L`X^qYL++;Ns0r@MI5@ z8wSFuo%BY%L5I1usdphPC{co>xW+|p3oXuGLPZ&wN2gTolVvORo zFv?Uz)>(xIhW)H8)x6M!Esir6ZJ3xt^HD6vZT4cnQB2CXxQc3674j#@*+A|HVN_>> z9~71hkPFjOfIc$1>;w^*aK&_3s3Q~$l+d^<<|d^JD+{J6jmSj)1d2+PkP~*soh@M{ z!_Q1()J8_QXv^oq>ci;TYyKa7?3Ifw%pm>Fymwi6cS~>OD=ZGWpQiXxJ92rg6zI+H z(1z#Xa$JFj%+d(vz3@lx9vf3JkGLU~qhSGf$4~0o;w>^w~q6Bf^wY-VV?FdEcYF!#q&hgrUZ7SiG-7? zzj(f|W6D58N7Mw>Sy!5QM~~}yKNl%}m~P4<@=!koxZDQS43_Js^7HbPLWqcAT7$yz zKgj{fisA}QG4ce!?hDFe1|-@(!41bJ%yw-?|Kr zXp(n@|GGH5i%d&5(bTt&LFZPjDqV_c|I3a5otmF8XhnwY#>13*!=&}uWh}#G)h29} zkH*&;N^4haE(QuJ-ccu z_`c`8$Pq`CceqEZZqFPlh$QMCE-pJ~#VU$L5Gy~Zc6VFl_xT{ZX%Cis{D^c}0!ym4>x0V)fq+~+h01&k zBpkfE@hA_fz#ngH=7dV{+yk36anFK+c5JM|M z+*MYcspzD_6A0U{5Yx1sd&vQ_GYZiScyA6<`y-uGNk?>*$Li-FASIF7RDR-QnRjjv zO(s?#Uf8Y(AUvx)9qMx6sY`h$#qWIlyiKuE86H8)PccL7{k*-~EizaRJp2{f+T68& zAkx2k%zo{-RrKwEt$d|g#%7!|5Ht4vIK5R{2u6z%1@L8%(PM$bx!fH%d-3Ysi%CT6 z{?<=ONY==8ob7&=HZrSCtiQ{Fer7>SZBNFyA?5P`Q{0`aV|1L0r;mXDRB8V^3ZFfU zM%`{`^n}LVD7uKvAe|7@&w1|rh7?F6n{~59mY4Ph{T7mr-~Y~El=FG|LPyDh{faa( zHr4Nga?)B5s?pXv_1BJLvm#*BSz(th`Zxz1){C76F;+LCih^PsaPpQa%rQzx{FyMg zIr0twg2Lpl=D>+8N-7xb!Bw1EsD81%4?#3)O&N>APnbIhVwenoCT73!k;}-#?t0P= z*?1RiMxG&>fT~n7miD{pNY-auq{8eXDZF&S88rX9$%41;yV^2wV?wB0%dAq;9TDCZ z@4=yy*qSvF65cuQ%VpCbdFKqRcE*RR%N?%Spy<%}P0ZaM3E>*TnD)!ma|PzyhS*6C zWs)g}mv4h-KgurHh`l)x(e3yHBqe*;c1VG%1#%zVE4G|AZZ57Q17_e>5Q!!TJ?LY% zNaEkbUGMQu0{WXO@t?vT^JN(Qlwh^^hmPDGiDIwiqEQ=ctMfy*{;~Z(%t&2vPy)j>Fghbzv@btZW^Y z=hXGL5dR3*LfOb%+{3qTnh)!Q)!z|ye|d}6{F0@(Qj3K1zgi#}F?Ch6c=q?RQjLcG zBVClAN|eEu@>vHbsnh{#OWLeT-m12<^_Py_9T_r|Jb4alr-plz&jzpkjV=^0$#7Q; zoVTA+FM_a(w|NH-+k|*??F-%^uH1Dd%YyMxexz$^-#XHK3Mt4({O*|g| zFGggUwFd)KmuT>wpBlya?diLB+$4jHLj|Eh<+3+AC8=Hc{;jo`PaEyUk_*}c0C`Pd zKLqQ_*m4bY-20lrUxz$2SqEDD(wJ&xi7b588MHKf|5m69g@y_Yn);gBdIiAxTDkP} zL}@Wn^x;;lT+Sqbw3BQ4MP+mATH9)wdym{+=omn}sr&T~*4z%p6G}yoH`e+@3H?!c zDFPmwC)fn%gm-+&6#djlurKbQd-Jwc?$y1mJ!8aPZGQM$-xBN!NLUS=ez>E~aOM!z z=R*`DU9fP(gt5~l5{f!Cmm%~c)al(Gml}MUW@+bvkJ9t!CPshW&xBK#93;@;O~+rVYL)B{S1gHj<{Wp`|105=e1|Q3nSg5#7_t>w0V~2(1a+8hYW0vgpxk zpjBmY!Pm)0K`8)JZkyO^ef&!Be^+8w00i}f(s5=ud)nQMY?dokY{@FXN!?y1!!}zv zvmu-*emN#apgDc2)R4!=N)y{pQ-;>ST2yT>X4!Pz7Ox2h;xH6dCF}X0_~2RIAnTa| zr?FJCJQ(vi^jU@EqV(Yjt1G?_lu^ZxY$`E(MHH~ljqZwS&QOV}E?T>BMb$otGo?u; zrE|zxcuXCp-srBGzce2zTTHP}e^dh}1{q*(9Iq18w(HHYE-|ZXXrxL;8sSY^%n?3i zoFX;#btT;R%;xn-^X;P$`6*+JdNlV&&d)K3Gjy)9p zkgMnXe4_q6cT9FP*sf1P z77(d&5kmF1up4yjYzxUoZE*KuqI7%4Xis~MWnv&-f-9XL9?1Wl?7T4`ZStGT=;TN? zkB|4`k_o4=7fgD;J88Pr%XgWKLXMO=mcz%RZea$bw-YE;T@7#s zPj+~f>BIRBKjj!QfjjiYx;(Xeh*vnq;ca-sD;^&njx)9oc zO!KqEiimYBv|0dLk*zfRh$Q^zQRI_^%?S~m-+$rkvDLpxM5@3?F7<5w@#0NKmkqbr zdDCO%yei)=CN*6ds{9A+QQ7Vy#acQtnE(j{6yX>XuQx5M0!0P==^0w!H&a~803YEjxyZAA}EB7r~d3)W` zEwyM!a)9}bd7NfzRo|>UU83z_t1t+;gNNJ#Lx3g)KVV$C-t8@6xg1hH@|k?VJF#r( z$F2z&F+|nMO)W>#r3hoKVs*YH(aNnPg;-To265@FyPyI}0P#nYJ9@nW-=6Q4tDlrc zAau--?yZa_r~|--b8Hy|9HjcV@NYH(tGqmasYK=*2QI;mHgdc41v zO}RRV8AC-2_B3-y=V2P+!YQ~63%d*QeZZ7 zK`AFIwZ*=d99?(9l|U)UkfR_*3mLGXKSo#CVO&)nA0|pctr^;r!m|)6Ik~3orKVaL z@{(f1j)Du1iKV-34|_Wt#rYh@;0&s*69%jPLV@p3@RrIugUDW6QvNkm zk2Gofs~M!1$1pWU&<~H-O-V0bfEXuJ{$~`1!biUGOT`6d)yf^WBM;AVYBKMX;`p1e zF{JU&5hu>IRbE1UCbz3+If=HG5hanMg+3) za6~B+A*#Xf;Ma8uQ>)jbUk4<35O&`Dy|>v92)Wu@thclP=%2;2*U;%h{=0q~&;FUU zi;qBTDuVZ2nBa0GZuL90KTrRLc<{wXh1GQh|5^F#dHeLN!OJUP`n?5F~xTbjP8@1@mS^&i6HyUg+LO zZiQAWSlthj)ZqNS*3VEz-fg!YEC)sNy9v9`3tdtwU@N_DD+7Pp!P8d9mzuzE?2aym zX^FH`poOR^o_&C$6A8KMM%w52OSzGR;ZHI#&N3A^$L|fEPe72ufwgOdpE3CPBpp^SbEl4&WN+*iVT>OkoUonLHwLe_GTrX|R@<65*a>$-?&qjUnV-3Ou!xh&3aFRZvW{x#|6lS zd?7pEp{ORsDaARevGCyV8Zv*-`KnuJ6%GYDZ(a~+6TWA>8m6|ArU4t>Ya7;H$L){c zV02PibsvbQEY?=t0NJg(EI>#d1^BR!PN>nEfr7)8p_k}dv9%0I z4Z41;J`=MS8!0a2C+-qenfK=oB64UyGyz=A6G1%@{|KFRC;wC=$>$-gzVX1@$MYxe zYW13GkPo^L43>%izAMbVRXs+m$@+D>94PW|A=sk_wdLCp+ki_ByLe;3NQ4ys!q2Lu zJSjGI7#LS19^aQ+^nj{?;We0vcPY|o(y0N4{y{*XcQ7=O6nxMg$a=1=#NoU+%RhY~IvH7l zheJtDMQ18A*Bk0bWRLPLfk@J*_CD9+xKjz`_wRvR9y+zd%f^0Y^){{D0k1B^INdo4 z0!#*1+oEc2E?e+@7BQn3+q&0U+Qi&xWkm59M4{8*HE3k=ar0s~z2vzpp{=%3!(2q> zC)GF9ZNmChzFwfWG_L38)@H7(31)2={mkJz6F6V})T|wE$EXBo6K&8qfivRLgPXl# zG_K78Cnb+HM{^5V&qdvRX4PEW2yRG+_ZRH;0J3~0t|1#lIagR47ph2vq9yH=!m z_P_R5wSF;ce^ksB+FBLV@mTw-w8NWfVt$NJkNp^1)cHj-8Oduz`*6nP5ldWwTu_mI z9f)EN;^=SkyOGD&J~El8C{4`pd3|5FzrEVE8N@4al$4lJ>%t?k4DvvB2gaL-tE(Sg zt>1p!@{9mSAdYmPv)~|Qns~F%%^2DnJJA(vSB&}8N0;9GyRjoF4~$DVo<0--Mk@jH%)yeu~nRzd0v-tTqIs)T<<`XlaK(Qp0rWr1lui z?zCxTniS?qFHt=TKQ?#)u2*s*gh4>xrKPx>qyY*Nk&jbWI7_fr9wu7(yD!wJKK2T}K|49&cD%suQsj~Bd|bDgw5pXvColMjn(Cd~w!W>t<|DoFA7pS|$tsyu zG0`d@Llo1d^4U#ap;);G;#4GFXopqK=bN3*2kUz=F2XA;l#Uri-1-GBe*c1zdp_P= z8Oy%qNRNeE_ur{wrH=-s9FxayAeSU3{o2w*au-tgmG$%LbZ`k_V+u4Dt-cm4$vOr1s`@duf^Y8en2wLa4(jkM8YokI z&evDmRKMJd%GI_)unNWfPnmdFZH{^4{ZL$#NW9= zYG>PJnv2=|AX+tUDc4W0=YlfAzRbi75z!7rm=kthJ)@)Q>gmW0zTAtza zXr(-5xF2(^z4=)0_?4FDD<-i#S{Kq6noF)KD3-!^@%+1~N?iT|{l&ziETfi7h+X#( z!nMck!$_MO;=2i6J2L>BBjbjaxC1}&0h`M6`+VGq^)%^jHrtAjUWA3Yg=g#%IYopH zB|tDp*kiht`E%{l+X^s<*O6Er-``#WYTjOWMx0VxzgrXo0&*V^8>8cdXYDTsiZ{;$ z-#}v?trtJC)Or?z?kq3dDLLNkz>Ee|3m=d}V+>W(@1lNj)UMvkTUL6boopd=PSN3y zJn<4{X8L7QKm-Z=srTm;b=+tG1&J$$DW)r{xQQLct)!2!>Kte=ANJwFI(k*EOpNFTt|!PS2JufwYrh^WK{Ot+>Ha ztQh9a=U|&h(R9_3dfwO}TMm<=T4;CeWY=0dKxn7`7T=AMXv?}^G}APrHA3x^ki*0Y z3ZDjYny;LqU{|=;pU{%uJO>7-7EJm$icMmdc7~`R6@OzE ze4biQm7zRnMUhx7v=D0NeO^0uxfD1$+eJT@6gQ^VZS)+a4fvbv#$VS7OWen{l8y=) zB!?pmjH+o{!Y0gIhqL**)0%yhHeqzHh%<)Y7rl_@;$C}W5}HMVd#7pLenNs<(bqJE z8Ht4X61)71H$~ft^X7%CziB{8uzPf6mm*y7J74Xayr-?~`Y?=s&DI&|ealwh!JcF& zTu#QoCXY5ScY@&9TMolVD?^9mjXZ6i!~twvmC-mC1c zzRhr=h3tDZLThw0ey~#)q7lz76jE zULcsfl~%xtw>mHuhB0o`Bg0$0`=1sSl{bcz2RTbzq)Jra-((t`Fg)UTRK0SJWU2J(?Kto*n)`~#p1 zZ*9$xXd(@~#`dsBL8{_~*6hCQ!1JQOMZHCdJVK^H{`yv-s(79$j%ZFry3*tPrV09f~m8-8J@uL8GX%OY$4H`k*c2k zW5=Y3@CnuI*bPlL1^fHhE-w<-JbW=rjy-(WZ-%TC`@vOO9a~wBWokSY0&}`jh!TQU#uln%k_r69FhY1jKs85&g@EN0cyawEE0vjp zOgnhEKU5!ztOK01zp}M@lBVT$wtKRWf&f#SVrB9a0^9jN`O^L}mscmo%+Y-A04K)+ z=U3JoJ3jh|E!4Tm)T~MWqryLZuN zQJ7jlrU|~RY@ioGD|hwjWLkYJMvguuRfLD8X{rOq6ZluN{$wO@!X*^sQgZX*xlQUb zPx7I$Km~6@R5}Hc?o(A6>1#|;2D?uCCq!G?2j2ot!ytCB2gP1Q`zGK2o8Ml?$PTHf ztbyu)Kj}-~1Fw1^HfvC|rNZLIeOMN`>|aK-J{T%EXA3Z&9D|e!d7p=ExFUbRH40FY zRA2x4zJ@zH8_HvbJ)f0rc*CBH)k{xPK8bUjT-?8~e6XwN-)0Ac0 z>$}`0Q2`Z1{o~m3W4FdecLcn!tzlhG4H=9w6P>Y+39%`htp48V8qe9MnW^+j)GRP= zzOp0U(pVRn7VfTWI(OBi$CV}iKe67YL$;4uHRzCy2*73Tc}s^IubP6baBx_(LL^i> znr2^Vuv2?l#Xy+&Z1L{QHY_v57)`>zRxzxawex9=_Ja!aq*>u57ZqutR3i=n{Cw0O zs)5!}@QFQRjrvgF>hcyzsuifUVB#2$Tnc7aVT8@wg<;C`<^<5I%9cCOYRY59Vi=#T zz9a+Dy_~NS&>gKpjL_sm8)6eq-LuQ|gZaoLAcu9zvGG)vw?Q-Z$w*>0_N8c8-KS6P zbWyi3uo_DuiBk-JAd3}qs6(hVz!}F9=s>Q?Znb_*?PhjWq3?M3Pn0Byw{Ew`kR54~ zieF0!@L=DsFZ2u;)sJs&_zB6{i2ZTPpy)qN3u-lwaW0uRxduG`j@Afvc9I zpN|Vhm{H9J&NW*;urMb>)ZrruTxS=HAfn3F^U)o0&(s`>{#{f{svxSpK%vA2U! z=4_t1g1+h7&14Va8R3_N_xs7Jy%-IQsB(RP5py-$(XvH``=2limxQEF$O5vPjoQCx z>OyvS|*j;ika-lUrCKeFwy}E7x+tLEm8QJ_G_U#Q%ZmFgTa~lCd`1iLV`*x#df%Z1J zQ{2_}kEZ!K)9@CMQ3Ld=gQwMMUd+}sHCx$mTiPi4KK(>>A8g(s4$`sJ0>}Q`wQ2eY zMH*~j>Wv@g*CS-7Ny%TZIN_U+TAOyE#e5nYuFh}2HdOXHw@Y{M=<~ozuCV8@d!V}2 zf8x!l2rB)&d$xZ^ zD4U)!E4uQrzfTk>B_U2OHAjmB3gQoWON=*F)+Qr?a)bG**)Fln=VfY7kd*s9!QD~J!PTbS^yEdcCCXBMB`&vWX&xJ7E}mT0}UNYj-P3HISc)46kW*6A5;8L z!vKTv7nF?%9<@>y=kE4_=1@=uytR5j+&qcM zQ#YeEOJEGt8@j3YE%VK$aDXFWBpVK^AM|6^5u5Ehh3)O~hH~8yP3NiAw@rIP$?p;` ztgz_Lx7f7gK@KTH0AO^>q1eUFWhOkwOXh67vXjR*(i>_L2Mu!X%;)DydWCKz zGK&(=id(exrrS5zBD2NS&y79vLgHaz0|v*CX_y!v62p3u>RyFX)F%vWKvYf#!CQk2 zIWkh{DhzPo0uej?Klf7ax9GDaxHD}gtkXR>V3l-C`rHO?o!_Jp#(@agI$39!hQi30 z*HwYqteJ$s&9_5GJA!^xLU~@rjMpzeS%>h3%714eCO2a0o$#c_=fdP{f1rn8PiZ0y zRm1fHHWi+b_U+7nTje(EDa0iko6<9VO8?x}Xp{RMUIgmFL-sK7B#^v1(VUq3e2dE$ zMu|~)=F=H?F^0OTp=rJ@Y=jlxLHU%<1VcYXaza?<8G1v!3-G!HV_br^5v8wh+*b!S>gfksWT78mQjg)FtjkY|E* z`HQbjM;X0xZ&QxwB&F)js1k&8oYOio=MuiQkNmGh^`0KjPec$ed3k;&B0SshUdeIX zEfaW59mwy*y-eD*$~3a@`!tS`==w2`Ig!;iTSdO=C;y~pYKeYZNA&?y3nPglulJ`52fP{?B&VuccKE=Wk z(}B9}sO+9btiA|D>HRRqPzS>D{%TAT^K61C=8nwje{3GstIfLG z-EpTnm2IRfr~Pd4rjqAjd*ZVNzAQ10EH;52SCrDiNTRcE9jDjAT%&w zVoWx%m&g@stWyFxjOfV%TApRnp@`p3fX;ipi)h_IAw8>NDK=KTIIh6W+r60E?RwZB zGk9-0NAcxtZ?S&GtGv1g`VM-8A@Xr~Te+M+P$Gb!N6^|NrX(>w$}{9<_RsExIC9Zdn2~eUM&&d7G>#A!qK20{`N3+fO%2AN5k1 zm24)SU8r}2hp!-w=VsWh#WJ zl_8J}(pZkJgB8cm#Df>1%3!xoU39O_0FO^%JjzUDHdzykm?x#rP2p@ok=p$mqYtie5V~>ah08&QAE}(3lg_tJuSY-LDSyEhAw=rws38ZWq{R?7*d3 zxcruf>|t>yC>c`ha*V7TKkdOQcED;o$A7~253v`QD}~xeVU4Tw z49b>2ay6%z>UW6l4V0sF@KA94w#)z&MJh??m`U3;0w|An9uUi&YS5A#0`oIC>Mm$n zRz@Nb-)3SvkXW`ZM`ArimD~~VbhflX%|gHr+%`cktk2WALa{TP!hzP$Eg98FvsbCw z&T%oHv}*IV9|+d830;L>%FgjX&5L2KV?EaIG8EpVb=Z@Ap(MqT z-5Pue_8}4FkADpIc0c`+5;sWTev_Y-_HsaXNfMtPYzO9n(TgLCO*oF$L6<7$T6|q2 zqs0x`8WL<0jC`{ES`65%7UE3QB_1Fj4JszhEbwGU0$@%mS8Af2qB$9urV4LV)n9&d zAa0;lVWCj4PL3A4zTy5l%Q?-DngWy5mrDYH9V+5iqyBpI=Z1{=!LrwQMQdF#R-sq% z;RGLS3E8<`z7RPCtLR35Gi#LJa!}cORTJT+kxNN!W51;JCtToCg|FAs!#SVL47M>@ zhpW#}BF|~_xte|WPz-n4e~TXgvfhT$saWb-it}>*4%@te50H2_a(tt{xaGuOt34#9 zF4A1VY;k6w;OJ?Q21_wVi!P<_6*;$#t!s>2E}$ZF#O^jKQO`?Odt>#i?ccyzB=pRI zFLq6?GTO>oR|4yR1)dZFgxQkE3hfttu&N#tpTCE8tt=Kovg{#1<2`~sTC(D>2cmLLMD3xDq>^@#7g)%5hX9Gbj z523veecJ4(eph6Nr(oR@hfTGvvZy~cBWoR7{R6Ey9fu_d#6OIziB@;v=LTb%t2ZC^ zT(88!9Zf1QbWr7yHyaUMM#7OMnwftT&0))V6(__m#5oUeB$LX>8g>LO$Ztu!aEB#- zPOQ?XRL)GFh5&b%vF&Tv(yOnBA>{{nq&_?i8l<2V)o?Js6UG^Z_4QGxhcY1ZCich>Et{ z1IP~k*qZG8;Z;bH3-4#1|C%G^FL#MoVuXfrOoUd z+;J2#FA>nsQXONsOz5mu>Ee8n7{@R7ul)Oe{cQv3blLS8zhMrsm-%k@+J(!-m6JrNVYD>>s9jX~==$UW&U+r>fYp@FtQdLI}n% z^uCg-9@%$*;nkxKr zo?{^12;}X|$ec<;Lf1al*2Z$+WjX>Yu7kgBI#so^W(HVle1O>NO3;<4gG!60ZH=#Z zP_fZq);PrrU5dbA3v7kEc_CcX<|gN_Pzbc);c{6L^`A^!*e@Gv(5R^^{0(qu_}G9m zsdAAsG2hcBUt*K<4fd}*Afc8cLg+CV&fyNmo@J~Im$Vzcn=jLO>s_d8aBInta(N12 zUF?7)&`}=H$l?!_SC#6-w@Z)CLkS5Ec?$F=uUeZ8HL_br1z*OrgG?(V)-)1BK$dunq4?6bX<}bM#nzb;14E+H!&Q$_bT_7v9CTe-f@i@bojo zhCx@{NFMNGru$02NVM$^V#5lsV+|C5Xh8EbdE`}l0ooKL>srhVu|Xb9IHE}8*HY&-*1LOgmmOx9 z4hyOpEmt&30imIOICMRzF$JhRa@rdcFSjgWoCZT?#Z-dkiE8ivK3BQ_9yDJoWyZ<3 z6ck=O=uk!MXRu7KvN*>f3%80_3caB-A;uVGEU6*%($8(EBA$2VJhcRFTe!LaHmiWrc2}1;v z)I=Lw7Soz1x;uHA4Vb1k6_guULVGdL#jn^lXA$(`>zO;k*uE_T@aQhE{tlRyQG&FH z(c#sG!AhqBvyuIXq-D+*$%G43rTnF8a|mQkNb!}c9HkTCTZZ=EKt;}bIE zR&ex&d^$N=a`qSf#8?%YQ?V>YLAVt|fDg+2z1Mn|Tuae?BGK=q#>1XtfCdv6*UtA& zEMd7qc+r>>_$hs}8t~?1T6c>SfQgP2p~EBPxGE%I*e=xB@={9-|9E$~84Zmj(L>Lp z1|j3}3w1s>xlCHfG-47~xZ@#^hHHp*9x|-UZxfwdE47Fnl&<<6rYS!uURzXlvG&6K=)6=PII1rz^pn zmF=-s(=T-hTXo#~q`>9bLXd*JG}*2Czic(Bk`DY6ero@fAlPk}bqNT{mwefCS*NI% zfTbOm4diQ#zE#Y9m9m1Mx}g}h>SMVSlCzx{2)mzt+g?&;Q$AfeMo^Su86*l62HYXJ zJRsT7uH0&lgmGKUCxm_^bz(b#4-rwtsCx~GpN@o$oOyYnmE;kk$kL^arNfjFDI?6B zmXao@6{jAAn+ywkgcAn>#Vs6)o&@=Jbn+rpOLN<6dJ*k`?$+UiGux@MP8|D*taeyW z7g$3^gsu7ANt02c)nOJX8}@bC{Vnr$(l^{JaotXTc>>NqoQt#-pUr4kgYdOl_p@@2gaxCYxRQcqrDM)ToZr%2YYRc1D z^2;1-CSC{U6ts{pPQMAK*h)M{(Blpg@_6o*j+i;dBTwdgMc{ImFwz}zaz-9cFiv&` z5-V6fh>-C_u%JNN+;j!42a7X_+o)nezz32x+vBrD#e)Oj@Mxo_P+z}6nu;Ap zOHGZwjI6iU!+NQL?sL6jsup?-fyGF=N68cp{D(jC%0kZ2$3sXSyU{1o?3p~em`O_h zcoEUwYX&-KJ402nkxy686od7;i6vkqk8 zTn0RISN(YimvT^*TAa*sJCv2t@)^vis~h1qz=Bc_VBr zey;8?zJ^K2WM5)E)BuU$j?w!Fik*U{7TgLzuugkrtRK~}rIAn4< zeB@B$5a%ZY{^Y@>@~8siEnuFfq#oqy9{#TxUD#p8OqE62N&o0&Vvi3E+Q*HX_NKBDp=E-R*bvnEj{Zap%EiV z_U;;X-=LQ?_nPP^K3nP+4ao%+g;m~(XI+*~Ghd!i4AFc-h3QmGZ3Ho9F>9E$35rAC z0ut&fWfeVJhHeI?_-~>^lS$dco8ijyR`&8?B&LyOxM`H6vvhB2{({KBUDN*vd3+S+ z=RgkU6hF6Q+ERSn@&+|n3BnAjJ!6)HL516S%3qqmseuK9gepOe`O^LK4T<1e?>-go9e70|)kT{6}VZ4gO|7!aG3;WYgL!dXd|v8>Ch0a2C}9nZ@lPgfQF#ZJqj9 zy#$*i^8Kb8rlgKBJx-Wt#)Q==+nfKl`p{+=t=qH5UI*EzB3e47g&`2M1O7q^MLbK5t8TS@O-PmhDzCYb*H-w-E)v~hU@Dc^G9!0jO637At#zcuQ6%U`xxdRH>;vx7J6U{)yX ziwwG002F^lVDm?jZ$=e|nd83ewMDCD>sCC>P;Nxkjzpq_0%lG}VS{&mzk4}*Okdj0hS%Sj6_$)8E@NwJImu2ct}5+;o}a?N^4 z*LhE&kkLY)(EtJXH<7J~_n>&ZR%9o{PUTP&K_RN+nxuM^zc%IX|HwcKSfB3QI1NzxNRrJ#Js-Qg{+38eNeA(-?i+X zzdB3L5z6J?cA2#rod2BY<1iN^iH-&!H71#b#L0_qULb+xfo8}p2eoN6qGGNok){>U z8nJh0CJD$eO$^9HYAYWHimAN8EkLnF?L`Cizjeq)jJQVFTO&uJXnCmL{HZtb>=TEY zGP#;Xj7Q3*OW}k+=N%z}>5DBK=Ti=8<~|Uf5JW4wABl3v?vc!SNe6NLfpP_L$%hGN zlfCR^hu`BOTEb3$N%l%RW?ALU3cRVAWKDdl!|5A*!Uj-MK1U&4Uk2USEY>MsM#^8L zCbiAdEX5=5$3F!qXPQUCXGKF74Yn|=c->Xd*()Ri$1GKM1RrGN8_hJ4gX800pm3ts z9|;fuP?&f3U{nA@DXq;hqzlMAfz6PTk!S6Uh%>Cknv+vM2ESHR&Bl>5_U-zNFn4<% zXCFsx5NiS32BxoB%_J?wGgg&DQcPjV5lJ#gqG#F*;ox>FdyLr4DYkeOl*`MKn%I09x7 z{D*n_44^;KM^%aO$jNQ(EV?nyL#jLG3-?f zL`Z7SpW(_H2Q390xVY&_L%BdEib2Mg#GZUyZbC9SJ$e6^-#F2xX!%|6Ox}Pk(P*JZ z#>z{{ahxCeJFR*9xM_I)NfvZ2ROU=DiX}o!s&@wiXzzB>aUVtRCt9h`r)8$i>Foq2_EQvh~gEw$@(8}`*v6`5Xe4pDhNOH9CZcy_Pd_r1FDS&>rJ8o@%qkR+$ znwlD;)C!KIWL6Y{B;%pOSjePK1oYG|jBYVn(5(Z&PKi5IR~(xT3+G<8Ia2)DVUyO8 zOCddMbfG{x!_>*h9|Pv_iw=J@Q>y-6bJe@1y0(VFI-eyW;SktsR=>IFKJLUPwGq>| z+#OX~i)PLUT4h#vP3Xn`d84e_?XX(;3197rc@gu*l_hl^5bc2aaM5hoQR~`+ zD)7_`x5wm)QsBJYlnoQmb`ME()avd57w;OeC-2kX(Bp6xM(jrpzV{|0zLZa2js6*mG{zyaz|?RTJ>vr|ha1Cn!urccb- z^n8lxC`&A~p#f!>SGwiF85|6bSD2Tj$6HbMEBXeSLz;B*D_aISwZbe$D&YU2wgEM> zF1*vZP3JJ?X79&tLuh?m%0!mfo*R~ZkKG3(l#=a$kAIvy0V%R&LG~AMUTs>?!^;>P zGfbkXA>@bkuH(9P6FmSNw2q*ipK0~YUanaP4=ERyC6tL5VG+yQ{|vdDXD4_)+mJFC$iyXY=!{>l6~KSa#=k8O zIB+w1xRS8Cwa`?hE}x|*zTq>kHUYCb4#9NuunUzvYZPHR5>->^rVa32-_Px8XF z*6tdt9ehb9PP#+P*CQsCQ3@9Bqy^wwxxsE|kJf$+#=kQD{fv>L(XxsXm?FqaspX5j zXVw4Bu?@;~Ipp}jLef(+AWi1SG*XzXzC|lC`lWMV($a6%$~%o#|3J#nm_F2XS?Z!$ z=naS`IH~LD1cXY<2i^4>@XFik9-_*1xn zr#sA>mae-rAt6dA21|edFN6JeeA$3Pif1Sp=?^spq7WG=Wr{XOfWrDN^uZDst8E0> zGk--YiYA~zGWtgO@NlN`Z;UyC-kt+ulf5soROc>$1xj^85kP#n!0zD4^#hv3GvELo z$GH23vK91&vCRNL1Q-6G1oUQnVeAr>GMAsD6BFny>1i&tVJl*NmzXbJzqvngjLD`q zJSKM1Z%95 zLj3YuNr>&J*|m~rWl8@v9|9UH?urLKlH?%&diAL}%q^pF6b}c`cvymsa~a=s`o!^A0001p|5#@L literal 0 HcmV?d00001 diff --git a/tutorials/integrations/.pages b/tutorials/integrations/.pages index 63b636127..19d568aad 100644 --- a/tutorials/integrations/.pages +++ b/tutorials/integrations/.pages @@ -9,3 +9,4 @@ nav: - 'Use Oracles to Fetch Off-Chain Data': - 'oracles.index.md' - '[Supra] Fetch Price Data': 'supra.md' + - 'Enable Gasless Transactions': '0xgasless.md' diff --git a/tutorials/integrations/0xgasless.md b/tutorials/integrations/0xgasless.md new file mode 100644 index 000000000..140d1527f --- /dev/null +++ b/tutorials/integrations/0xgasless.md @@ -0,0 +1,141 @@ +--- +title: Gasless Transactions with 0xGasless +description: Learn how to enable gasless transactions using 0xGasless. +--- + +# Enabling Gasless Transactions with 0xGasless + +## Why Gasless Transactions? + +One of the primary challenges in blockchain development has been the requirement for users to hold native tokens (like ETH or GLMR) to pay for transaction fees. This traditional EOA-based model creates unnecessary friction, particularly when onboarding users who expect Web2-like experiences. + +Gasless transactions can help solve this through Account Abstraction ([ERC-4337](https://eips.ethereum.org/EIPS/eip-4337){target=\_blank}), implementing meta-transactions that separate user actions from fee payment. This architecture allows dApps or third-party paymasters to cover gas costs on behalf of users while smart contract wallets handle the transaction execution. [0xGasless](https://0xgasless.com/index.html){target=\_blank} leverages these principles in its SDK, enabling Moonbeam developers to implement sophisticated features like social logins, transaction batching, and custom wallet controls – all while abstracting away the complexity of gas management from end users. + +In the following tutorial, we'll go through the end-to-end steps of setting up a paymaster on 0xGasless and dispatching a gasless transaction to modify the state of a smart contract on Moonbeam. + +## Create and Fund a Paymaster + +First, you'll need to [register for an account on 0xGasless](https://dashboard.0xgasless.com/auth/sign-in){target=\_blank}. Then, [create a Paymaster](https://dashboard.0xgasless.com/paymaster){target=\_blank} for the Moonbeam Network by pressing **Create Paymaster** and then taking the following steps: + +1. Enter a name for your paymaster +2. Select **Moonbeam** as the chain +3. Press **Create** + +![Create Paymaster](/images/tutorials/integrations/0xgasless/0xgasless-1.webp) + +Your paymaster needs funds to cover gas fees for sponsored transactions. To deposit GLMR into your paymaster, take the following steps: + +1. Enter the amount you would like to deposit. +2. Press **Deposit** + +![Fund Paymaster](/images/tutorials/integrations/0xgasless/0xgasless-2.webp) + +Your deposited funds remain flexible - use them to sponsor gasless transactions or withdraw them whenever needed. + +## Dispatching a Gasless Transaction + +In the following section, we'll create a script demonstrating how to dispatch a gasless transaction. + +### Prerequisites + +Create a `.env` file in your project's root directory with the following: + +```bash +PRIVATE_KEY=INSERT_PRIVATE_KEY +RPC_URL=https://rpc.api.moonbeam.network +``` + +Why do we still need a private key if the user is dispatching a gasless transaction? While this transaction will be gasless, you still need a private key to sign the transaction. The account associated with this private key: + +- Does not need any GLMR tokens +- Will not pay for gas fees +- Is only used for transaction signing + + +!!! note + + Never commit your .env file or share your private key. Add .env to your .gitignore file. + +Also, make sure you have installed the 0xGasless SDK and supporting `ethers` and `dotenv` packages: + +```bash +npm install ethers dotenv @0xgasless/smart-account +``` + +First, we'll import the required packages as follows: + +```js +require('dotenv').config(); +const ethers = require('ethers'); +const { + PaymasterMode, + createSmartAccountClient, +} = require("@0xgasless/smart-account"); +``` + +Next, we'll set the critical constants. We must define the `Chain_ID`, a bunder URL, and a paymaster URL. You can get your unique paymaster URL from the paymaster on your [0xGasless Dashboard](https://dashboard.0xgasless.com/paymaster){target=\_blank}. + +The contract address we've defined here is the address of an [Incrementer contract](https://moonscan.io/address/0x3ae26f2c909eb4f1edf97bf60b36529744b09213) on Moonbeam, on which we'll call the increment function specified by the function selector. This simple contract will allow us to easily see if the gasless transaction has been dispatched successfully. + +```js +const CHAIN_ID = 1284; // Moonbeam mainnet +const BUNDLER_URL = `https://bundler.0xgasless.com/${CHAIN_ID}`; +const PAYMASTER_URL = 'https://paymaster.0xgasless.com/v1/1284/rpc/INSERT_API_KEY'; +const CONTRACT_ADDRESS = '0x3aE26f2c909EB4F1EdF97bf60B36529744b09213'; +const FUNCTION_SELECTOR = '0xd09de08a'; +``` + +#### Paymaster URL Formatting Note + +The Paymaster URL format has recently changed. Use: + +``` +https://paymaster.0xgasless.com/v1/1284/rpc/INSERT_API_KEY +``` + +Do not use the deprecated format: + +``` +https://paymaster.0xgasless.com/api/v1/1284/rpc/INSERT_API_KEY +``` + +The difference is the removal of `/api` from the path. Make sure your code uses the current format. + +### Sending the Transaction + +To send a gasless transaction using the 0xGasless smart account, you can call `smartWallet.sendTransaction()` with two parameters: + + - The `transaction` object containing the contract interaction details + - A configuration object specifying `paymasterServiceData` with `SPONSORED` mode. This indicates that the 0xGasless paymaster will use the gas tank to pay for the gas. + +The function returns a UserOperation response containing a hash. Wait for the transaction receipt using the `waitForUserOpReceipt()` helper function, which polls for completion with a configurable timeout (default 60 seconds). + +```javascript +const userOpResponse = await smartWallet.sendTransaction(transaction, { + paymasterServiceData: { mode: PaymasterMode.SPONSORED }, +}); + +const receipt = await waitForUserOpReceipt(userOpResponse, 60000); +``` + +Putting it all together and adding plenty of logging and error handling for easy debugging, the full script is as follows: + +??? code "Dispatch a gasless transaction" + ```javascript + --8<-- 'code/tutorials/integrations/0xgasless/dispatch.js' + ``` + +### Verifying Completion + +Upon running the script, you'll see output that looks like the following: + +--8<-- 'code/tutorials/integrations/0xgasless/output.md' + +Since the gasless transaction we initiated interacts with an [Incrementer](https://moonscan.io/address/0x3ae26f2c909eb4f1edf97bf60b36529744b09213#readContract){target=\_blank} smart contract on Moonbeam, it's easy to check to see if the transaction was initiated successfully. You can return to [Read Contract section of the Incrementer contract on Moonscan](https://moonscan.io/address/0x3ae26f2c909eb4f1edf97bf60b36529744b09213#readContract) and check the number stored in the contract. Alternatively, you can head to the **Internal Transactions** tab and toggle advanced mode on to see the contract call incrementing the contract. + +For more information about integrating support for Gasless transactions into your dApp, be sure to check out the [0xGasless docs](https://gitbook.0xgasless.com/){target=\_blank}. + + +--8<-- 'text/_disclaimers/educational-tutorial.md' + +--8<-- 'text/_disclaimers/third-party-content.md' From dd816debe6d7dc03bf665b9a7df7627cabe7a6c2 Mon Sep 17 00:00:00 2001 From: Kevin Neilson Date: Tue, 5 Nov 2024 17:37:50 -0500 Subject: [PATCH 2/6] add descrip --- tutorials/integrations/0xgasless.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/integrations/0xgasless.md b/tutorials/integrations/0xgasless.md index 140d1527f..b9485bf45 100644 --- a/tutorials/integrations/0xgasless.md +++ b/tutorials/integrations/0xgasless.md @@ -1,6 +1,6 @@ --- title: Gasless Transactions with 0xGasless -description: Learn how to enable gasless transactions using 0xGasless. +description: Learn how to implement gasless transactions on Moonbeam using 0xGasless, enabling users to interact with smart contracts without holding native tokens. --- # Enabling Gasless Transactions with 0xGasless From 49aecae1c95ffb13f2bd3274025f6e6891981f3f Mon Sep 17 00:00:00 2001 From: Kevin Neilson Date: Tue, 5 Nov 2024 17:39:46 -0500 Subject: [PATCH 3/6] rev --- tutorials/integrations/0xgasless.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorials/integrations/0xgasless.md b/tutorials/integrations/0xgasless.md index b9485bf45..bc32310ec 100644 --- a/tutorials/integrations/0xgasless.md +++ b/tutorials/integrations/0xgasless.md @@ -7,7 +7,7 @@ description: Learn how to implement gasless transactions on Moonbeam using 0xGas ## Why Gasless Transactions? -One of the primary challenges in blockchain development has been the requirement for users to hold native tokens (like ETH or GLMR) to pay for transaction fees. This traditional EOA-based model creates unnecessary friction, particularly when onboarding users who expect Web2-like experiences. +One of the primary challenges in blockchain development has been the requirement for users to hold native tokens (like ETH or GLMR) to pay transaction fees. This traditional EOA-based model creates unnecessary friction, particularly when onboarding users who expect Web2-like experiences. Gasless transactions can help solve this through Account Abstraction ([ERC-4337](https://eips.ethereum.org/EIPS/eip-4337){target=\_blank}), implementing meta-transactions that separate user actions from fee payment. This architecture allows dApps or third-party paymasters to cover gas costs on behalf of users while smart contract wallets handle the transaction execution. [0xGasless](https://0xgasless.com/index.html){target=\_blank} leverages these principles in its SDK, enabling Moonbeam developers to implement sophisticated features like social logins, transaction batching, and custom wallet controls – all while abstracting away the complexity of gas management from end users. @@ -99,7 +99,7 @@ Do not use the deprecated format: https://paymaster.0xgasless.com/api/v1/1284/rpc/INSERT_API_KEY ``` -The difference is the removal of `/api` from the path. Make sure your code uses the current format. +The difference is that `/api` has been removed from the path. Make sure your code uses the current format. ### Sending the Transaction @@ -131,9 +131,9 @@ Upon running the script, you'll see output that looks like the following: --8<-- 'code/tutorials/integrations/0xgasless/output.md' -Since the gasless transaction we initiated interacts with an [Incrementer](https://moonscan.io/address/0x3ae26f2c909eb4f1edf97bf60b36529744b09213#readContract){target=\_blank} smart contract on Moonbeam, it's easy to check to see if the transaction was initiated successfully. You can return to [Read Contract section of the Incrementer contract on Moonscan](https://moonscan.io/address/0x3ae26f2c909eb4f1edf97bf60b36529744b09213#readContract) and check the number stored in the contract. Alternatively, you can head to the **Internal Transactions** tab and toggle advanced mode on to see the contract call incrementing the contract. +Since the gasless transaction we initiated interacts with an [Incrementer](https://moonscan.io/address/0x3ae26f2c909eb4f1edf97bf60b36529744b09213#readContract){target=\_blank} smart contract on Moonbeam, it's easy to check to see if the transaction was initiated successfully. You can return to [Read Contract section of the Incrementer contract on Moonscan](https://moonscan.io/address/0x3ae26f2c909eb4f1edf97bf60b36529744b09213#readContract) and check the number stored in the contract. Alternatively, you can head to the **Internal Transactions** tab and toggle advanced mode **ON** to see the contract call incrementing the contract. -For more information about integrating support for Gasless transactions into your dApp, be sure to check out the [0xGasless docs](https://gitbook.0xgasless.com/){target=\_blank}. +For more information about integrating support for gasless transactions into your dApp, be sure to check out the [0xGasless docs](https://gitbook.0xgasless.com/){target=\_blank}. --8<-- 'text/_disclaimers/educational-tutorial.md' From 30703066efb612dc624cabcd201d60f606c53c5d Mon Sep 17 00:00:00 2001 From: Kevin Neilson Date: Tue, 5 Nov 2024 17:46:08 -0500 Subject: [PATCH 4/6] rev --- tutorials/integrations/0xgasless.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/integrations/0xgasless.md b/tutorials/integrations/0xgasless.md index bc32310ec..f89d9d6df 100644 --- a/tutorials/integrations/0xgasless.md +++ b/tutorials/integrations/0xgasless.md @@ -25,8 +25,8 @@ First, you'll need to [register for an account on 0xGasless](https://dashboard.0 Your paymaster needs funds to cover gas fees for sponsored transactions. To deposit GLMR into your paymaster, take the following steps: -1. Enter the amount you would like to deposit. -2. Press **Deposit** +1. Enter the amount you would like to deposit +2. Press **Deposit** and confirm the transaction in your wallet ![Fund Paymaster](/images/tutorials/integrations/0xgasless/0xgasless-2.webp) From 95e4545f777dffc64c21cd107a1fc9e5b8697173 Mon Sep 17 00:00:00 2001 From: Kevin Neilson Date: Tue, 5 Nov 2024 17:49:54 -0500 Subject: [PATCH 5/6] run prettier --- .../integrations/0xgasless/dispatch.js | 41 ++++++++++--------- tutorials/integrations/0xgasless.md | 12 +++--- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/.snippets/code/tutorials/integrations/0xgasless/dispatch.js b/.snippets/code/tutorials/integrations/0xgasless/dispatch.js index c9718888d..8121c5a40 100644 --- a/.snippets/code/tutorials/integrations/0xgasless/dispatch.js +++ b/.snippets/code/tutorials/integrations/0xgasless/dispatch.js @@ -3,31 +3,34 @@ const ethers = require('ethers'); const { PaymasterMode, createSmartAccountClient, -} = require("@0xgasless/smart-account"); +} = require('@0xgasless/smart-account'); const CHAIN_ID = 1284; // Moonbeam mainnet const BUNDLER_URL = `https://bundler.0xgasless.com/${CHAIN_ID}`; -const PAYMASTER_URL = 'https://paymaster.0xgasless.com/v1/1284/rpc/INSERT_API_KEY'; +const PAYMASTER_URL = + 'https://paymaster.0xgasless.com/v1/1284/rpc/INSERT_API_KEY'; const CONTRACT_ADDRESS = '0x3aE26f2c909EB4F1EdF97bf60B36529744b09213'; const FUNCTION_SELECTOR = '0xd09de08a'; async function main() { - console.log("Starting the script..."); + console.log('Starting the script...'); try { // Set up provider and wallet - console.log("Setting up provider and wallet..."); + console.log('Setting up provider and wallet...'); const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL); const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider); // Check connection and balance - console.log("Checking network connection..."); + console.log('Checking network connection...'); const network = await provider.getNetwork(); - console.log(`Connected to network: ${network.name} (Chain ID: ${network.chainId})`); + console.log( + `Connected to network: ${network.name} (Chain ID: ${network.chainId})` + ); const balance = await provider.getBalance(wallet.address); console.log(`Wallet balance: ${ethers.utils.formatEther(balance)} GLMR`); // Initialize smart account - console.log("Initializing smart account..."); + console.log('Initializing smart account...'); const smartWallet = await createSmartAccountClient({ signer: wallet, paymasterUrl: PAYMASTER_URL, @@ -35,10 +38,10 @@ async function main() { chainId: CHAIN_ID, }); const smartWalletAddress = await smartWallet.getAddress(); - console.log("Smart Account Address:", smartWalletAddress); + console.log('Smart Account Address:', smartWalletAddress); // Create a transaction for contract interaction - console.log("Creating contract transaction..."); + console.log('Creating contract transaction...'); const transaction = { to: CONTRACT_ADDRESS, value: '0', // No native token transfer @@ -46,24 +49,24 @@ async function main() { }; // Send the transaction - console.log("Sending transaction..."); + console.log('Sending transaction...'); const userOpResponse = await smartWallet.sendTransaction(transaction, { paymasterServiceData: { mode: PaymasterMode.SPONSORED }, }); - console.log("UserOp Hash:", userOpResponse.hash); + console.log('UserOp Hash:', userOpResponse.hash); - console.log("Waiting for transaction receipt..."); + console.log('Waiting for transaction receipt...'); const userOpReceipt = await waitForUserOpReceipt(userOpResponse, 60000); // Wait for up to 60 seconds - + if (userOpReceipt.success) { - console.log("Transaction successful!"); - console.log("Transaction hash:", userOpReceipt.receipt.transactionHash); + console.log('Transaction successful!'); + console.log('Transaction hash:', userOpReceipt.receipt.transactionHash); } else { - console.log("Transaction failed"); - console.log("Receipt:", userOpReceipt); + console.log('Transaction failed'); + console.log('Receipt:', userOpReceipt); } } catch (error) { - console.error("An error occurred:"); + console.error('An error occurred:'); console.error(error); } } @@ -88,6 +91,6 @@ async function waitForUserOpReceipt(userOpResponse, timeoutMs = 60000) { } main().catch((error) => { - console.error("Unhandled error in main function:"); + console.error('Unhandled error in main function:'); console.error(error); }); \ No newline at end of file diff --git a/tutorials/integrations/0xgasless.md b/tutorials/integrations/0xgasless.md index f89d9d6df..ad8402c74 100644 --- a/tutorials/integrations/0xgasless.md +++ b/tutorials/integrations/0xgasless.md @@ -45,13 +45,12 @@ PRIVATE_KEY=INSERT_PRIVATE_KEY RPC_URL=https://rpc.api.moonbeam.network ``` -Why do we still need a private key if the user is dispatching a gasless transaction? While this transaction will be gasless, you still need a private key to sign the transaction. The account associated with this private key: +Why are we specifying a private key in the `.env`? While this transaction will be gasless, you still need a private key to sign the transaction. The account associated with this private key: - Does not need any GLMR tokens - Will not pay for gas fees - Is only used for transaction signing - !!! note Never commit your .env file or share your private key. Add .env to your .gitignore file. @@ -70,18 +69,19 @@ const ethers = require('ethers'); const { PaymasterMode, createSmartAccountClient, -} = require("@0xgasless/smart-account"); +} = require('@0xgasless/smart-account'); ``` -Next, we'll set the critical constants. We must define the `Chain_ID`, a bunder URL, and a paymaster URL. You can get your unique paymaster URL from the paymaster on your [0xGasless Dashboard](https://dashboard.0xgasless.com/paymaster){target=\_blank}. +Next, we'll set the critical constants. We must define the `CHAIN_ID`, `BUNDLER_URL`, and `PAYMASTER_URL`. You can get your unique paymaster URL from the paymaster on your [0xGasless Dashboard](https://dashboard.0xgasless.com/paymaster){target=\_blank}. The contract address we've defined here is the address of an [Incrementer contract](https://moonscan.io/address/0x3ae26f2c909eb4f1edf97bf60b36529744b09213) on Moonbeam, on which we'll call the increment function specified by the function selector. This simple contract will allow us to easily see if the gasless transaction has been dispatched successfully. ```js const CHAIN_ID = 1284; // Moonbeam mainnet const BUNDLER_URL = `https://bundler.0xgasless.com/${CHAIN_ID}`; -const PAYMASTER_URL = 'https://paymaster.0xgasless.com/v1/1284/rpc/INSERT_API_KEY'; -const CONTRACT_ADDRESS = '0x3aE26f2c909EB4F1EdF97bf60B36529744b09213'; +const PAYMASTER_URL = + 'https://paymaster.0xgasless.com/v1/1284/rpc/INSERT_API_KEY'; +const CONTRACT_ADDRESS = '0x3aE26f2c909EB4F1EdF97bf60B36529744b09213'; const FUNCTION_SELECTOR = '0xd09de08a'; ``` From 1e5d9926331774d4e8d65e180b0fafec88f5677c Mon Sep 17 00:00:00 2001 From: Kevin Neilson Date: Mon, 25 Nov 2024 08:42:34 -0800 Subject: [PATCH 6/6] fix --- tutorials/integrations/0xgasless.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tutorials/integrations/0xgasless.md b/tutorials/integrations/0xgasless.md index ad8402c74..e07ad0794 100644 --- a/tutorials/integrations/0xgasless.md +++ b/tutorials/integrations/0xgasless.md @@ -85,21 +85,21 @@ const CONTRACT_ADDRESS = '0x3aE26f2c909EB4F1EdF97bf60B36529744b09213'; const FUNCTION_SELECTOR = '0xd09de08a'; ``` -#### Paymaster URL Formatting Note +!!! warning -The Paymaster URL format has recently changed. Use: + The Paymaster URL format has recently changed. Use: -``` -https://paymaster.0xgasless.com/v1/1284/rpc/INSERT_API_KEY -``` + ``` + https://paymaster.0xgasless.com/v1/1284/rpc/INSERT_API_KEY + ``` -Do not use the deprecated format: + Do not use the deprecated format: -``` -https://paymaster.0xgasless.com/api/v1/1284/rpc/INSERT_API_KEY -``` + ``` + https://paymaster.0xgasless.com/api/v1/1284/rpc/INSERT_API_KEY + ``` -The difference is that `/api` has been removed from the path. Make sure your code uses the current format. + The difference is that `/api` has been removed from the path. Make sure your code uses the current format. ### Sending the Transaction