From 0d5550e5d500561a9ee8c7aee68d4222af1ec363 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Tue, 6 Feb 2024 06:18:00 +0100 Subject: [PATCH 01/37] Add distribute page --- .../ballot/components/AllocationList.tsx | 194 +++++++++++------- .../distribute/hooks/useDistribute.ts | 20 ++ src/features/distribute/types/index.ts | 9 + src/pages/distribute/index.tsx | 101 +++++++++ src/server/api/routers/projects.ts | 29 +++ src/server/api/trpc.ts | 13 ++ 6 files changed, 289 insertions(+), 77 deletions(-) create mode 100644 src/features/distribute/hooks/useDistribute.ts create mode 100644 src/features/distribute/types/index.ts create mode 100644 src/pages/distribute/index.tsx diff --git a/src/features/ballot/components/AllocationList.tsx b/src/features/ballot/components/AllocationList.tsx index 1740398a..e92de283 100644 --- a/src/features/ballot/components/AllocationList.tsx +++ b/src/features/ballot/components/AllocationList.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import { type ReactNode } from "react"; +import { ComponentProps, type ReactNode } from "react"; import { tv } from "tailwind-variants"; import Link from "next/link"; import { Trash } from "lucide-react"; @@ -7,13 +7,19 @@ import { Trash } from "lucide-react"; import { createComponent } from "~/components/ui"; import { Table, Tbody, Tr, Td } from "~/components/ui/Table"; import { formatNumber } from "~/utils/formatNumber"; -import { useFieldArray, useFormContext } from "react-hook-form"; +import { + UseFormProps, + UseFormReturn, + useFieldArray, + useFormContext, +} from "react-hook-form"; import { AllocationInput } from "./AllocationInput"; import { IconButton } from "~/components/ui/Button"; import { type Vote } from "../types"; import { useProjectById } from "~/features/projects/hooks/useProjects"; import { SearchProjects } from "~/features/lists/components/SearchProjects"; import { ProjectAvatar } from "~/features/projects/components/ProjectAvatar"; +import { FormControl, Input } from "~/components/ui/Form"; const AllocationListWrapper = createComponent( "div", @@ -39,81 +45,6 @@ export const AllocationList = ({ votes }: { votes?: Vote[] }) => ( ); -export function AllocationForm({ - list, - disabled, - header, - onSave, -}: { - list?: Vote[]; - disabled?: boolean; - header?: ReactNode; - onSave?: (v: { votes: Vote[] }) => void; -}) { - const form = useFormContext<{ votes: Vote[] }>(); - - const { fields, remove } = useFieldArray({ - name: "votes", - keyName: "key", - control: form.control, - }); - - return ( - - - {header} - - {fields.map((project, i) => { - const idx = i; - // const idx = indexes.get(project.projectId)!; - // TODO: Get allocated amount from list - const listAllocation = - list?.find((p) => p.projectId === project.projectId)?.amount ?? 0; - - return ( - - - - - - - ); - })} - -
- - - {listAllocation ? ( - - ) : null} - - onSave?.(form.getValues())} - /> - - { - remove(idx); - onSave?.(form.getValues()); - }} - /> -
- + + + + )} + + ); +} + +function Distributions() { + const stats = api.results.stats.useQuery(); + const projectIds = Object.keys(stats.data?.projects ?? {}); + + const distribute = useDistribute(); + const projects = api.projects.payoutAddresses.useQuery( + { ids: projectIds }, + { enabled: Boolean(projectIds.length) }, + ); + + const payoutAddresses = projects.data ?? {}; + + if (projects.isLoading ?? stats.isLoading) { + return ( +
+ +
+ ); + } + const distributions = projectIds.map((projectId) => ({ + projectId, + payoutAddress: + payoutAddresses[projectId as keyof typeof payoutAddresses] ?? "", + amount: stats.data?.projects[projectId], + })); + + return distributions.length ? ( +
{ + console.log("Distribute", values.votes[0]); + + distribute.mutate(value.votes); + }} + > +
+ +
+
+ ( + + + Project + Payout address + Amount + + + )} + /> +
+
+ ) : ( +
+ +
+ ); +} diff --git a/src/server/api/routers/projects.ts b/src/server/api/routers/projects.ts index 04def312..a30ad2ce 100644 --- a/src/server/api/routers/projects.ts +++ b/src/server/api/routers/projects.ts @@ -5,6 +5,7 @@ import { fetchAttestations, createDataFilter } from "~/utils/fetchAttestations"; import { TRPCError } from "@trpc/server"; import { config, eas } from "~/config"; import { type Filter, FilterSchema } from "~/features/filter/types"; +import { fetchMetadata } from "~/utils/fetchMetadata"; export const projectsRouter = createTRPCRouter({ count: publicProcedure.query(async ({}) => { @@ -67,6 +68,34 @@ export const projectsRouter = createTRPCRouter({ }); }); }), + + // Used for distribution to get the projects' payoutAddress + // To get this data we need to fetch all projects and their metadata + payoutAddresses: publicProcedure + .input(z.object({ ids: z.array(z.string()) })) + .query(async ({ input }) => { + return fetchAttestations([eas.schemas.metadata], { + where: { id: { in: input.ids } }, + }) + .then((attestations) => + Promise.all( + attestations.map((attestation) => + fetchMetadata(attestation.metadataPtr).then((data) => { + const { payoutAddress } = data as unknown as { + payoutAddress: string; + }; + return { projectId: attestation.id, payoutAddress }; + }), + ), + ), + ) + .then((projects) => + projects.reduce( + (acc, x) => ({ ...acc, [x.projectId]: x.payoutAddress }), + {}, + ), + ); + }), }); function createOrderBy( diff --git a/src/server/api/trpc.ts b/src/server/api/trpc.ts index bbcc802b..4c996319 100644 --- a/src/server/api/trpc.ts +++ b/src/server/api/trpc.ts @@ -13,6 +13,7 @@ import type { NextApiResponse } from "next"; import { type Session } from "next-auth"; import superjson from "superjson"; import { ZodError } from "zod"; +import { config } from "~/config"; import { getServerAuthSession } from "~/server/auth"; import { db } from "~/server/db"; @@ -124,6 +125,17 @@ const enforceUserIsAuthed = t.middleware(({ ctx, next }) => { }); }); +const enforceUserIsAdmin = t.middleware(({ ctx, next }) => { + const address = ctx.session?.user.name; + if (!config.admins.includes(address!)) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Must be admin to access this route", + }); + } + return next({ ctx }); +}); + /** * Protected (authenticated) procedure * @@ -133,3 +145,4 @@ const enforceUserIsAuthed = t.middleware(({ ctx, next }) => { * @see https://trpc.io/docs/procedures */ export const protectedProcedure = t.procedure.use(enforceUserIsAuthed); +export const adminProcedure = protectedProcedure.use(enforceUserIsAdmin); From 18f693e64db8d768fb0840e45601d20d126db932 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Tue, 6 Feb 2024 11:48:11 +0100 Subject: [PATCH 02/37] Add Allo2 SDK and hooks --- bun.lockb | Bin 567077 -> 567897 bytes package.json | 2 + src/components/ui/Form.tsx | 9 ++- src/components/ui/Spinner.tsx | 2 +- src/config.ts | 6 ++ .../distribute/components/CreatePool.tsx | 56 ++++++++++++++++++ src/features/distribute/hooks/useAllo.ts | 30 ++++++++++ src/features/distribute/hooks/useAlloPool.ts | 30 ++++++++++ .../distribute/hooks/useAlloProfile.ts | 49 +++++++++++++++ .../distribute/hooks/useDistribute.ts | 1 - src/pages/distribute/index.tsx | 13 ++-- src/utils/classNames.ts | 6 ++ src/utils/fetchMetadata.ts | 4 +- 13 files changed, 191 insertions(+), 17 deletions(-) create mode 100644 src/features/distribute/components/CreatePool.tsx create mode 100644 src/features/distribute/hooks/useAllo.ts create mode 100644 src/features/distribute/hooks/useAlloPool.ts create mode 100644 src/features/distribute/hooks/useAlloProfile.ts create mode 100644 src/utils/classNames.ts diff --git a/bun.lockb b/bun.lockb index 00595ed4f176fe85875773fe0ab3865e2e85d3dd..8241b41046ceb090be01697d566b4d2fc5f75f90 100755 GIT binary patch delta 102902 zcmeFad7O@A|NnoT!^NB!3<=2=*+=$kG`K1$LyA%jg!q(y5(SP(v@rK2fbR9T6dEU40HO#4886VPBIDbs* zxGt0D&l*`)5Dtavg+k?@)Cz^_f@9zbV20I)S>6Xc8oe+xExni`&ZZ>gTMfG4ftn_N zKS(0HGpKUXCTACy7G#FH)Cq--f;Xg+rr?ai^!$v>P+6!nt#E1)1&*OK75EbgNLzj{ zL47bSd=>egId&+dy@sw@udyYywXJ)v!N^YyeKl&dW$Dsap`5 z)zD7358wrsh6QF{GFV4=Vp&jWYEh;ZA z%E_Kgx;>2z{{da;nu1hVUfbesP|e#;IwdJT4J)giKYN$QHElbIE+Lm63#z)>ppyRz zYB+xJ*2lFjD{pQJ%?FkDV+Ml+S_&sg`E_8kP^heYn=SbCHlYw&`C}k;m*+#oXSEH5jseF))Ue*i znF>09YT!DXzreazr*@`c>p@xTcH&jeM>f?A`G0g=j!I-2;WLG?VYlfj`NBVJxy*ry+(7&?GY$Rdw* zHt7$6IFzwu8G*YHlpu3vW?nH3IJ&E8X+d^gz6zMx&Gd9QsEj$FD%?go@iA}>Y+q0V z+X0lDHUd@dZ={puiVAYFi+iD+VElABIH4X9iwG#g={?Mt^|zR7dww19GIf4ldZrq5 zQDIs^L1`%TB?Zf8J_PH62fCQCUO^>vy8I5WXSnGxt$LeO8JR`Jh2+jH%vK?xCcVAM z;XyqejHKS==HyUhDD+swWc~$IXe3z;0DY1zd=zMs6U;b>a#{-&v2KvnfwU(<#3 z!u+D5i;6$)rIK<4> zjbKakrJ#KGO0W$$8Egxl3Tk#9Z{zEN^8N2lRsM5AmWp z?EJ#ajO?P&*~3kbP6K7R6D@WEH7S}8_ui}B%5|ZdoCiE7zI9ojGfiO~ES`9l@t79y zx}-|WE6q(SPS5(NOR#2?&+BXk=~Z|W;!EJHCFL2Q{IO(|G50B!{{feN=Gms6iEvGb zkKnYTtbDXhm^0dB>%z4JV13_8q zxG^UEi=eFbC@5=`E@Yze3#r~p;*bmFxZIiPy{^#x|AJ_6+zcYx|xQC3=a z@7Ov=m1U-zh(|zWn4X!Qo;E!cO3pAm=5RNXQ9ZaERJvO;O#|A&kA>%gYCu8ulv4T? zdT@$q=&a(*Jc9n`UAs=V@;Vh0df{H7^QQFRcl~A^o?o!SBI3@RE{jwJx-gE-U_? zT+^*-X{ALv8{Ec^(iLYF=9f&(3VjTh{pWy<<-(T`&{C9H%EG~wXZe%@(~XSGoHQ9E z(`LND;+<&fX2lHCofscNe0}r~4b~i+T+}yNS;OchGtEG{U^~+J z#B1Aa@6D*!wCp_kVuraet(YF?UaZE2LVZEiQk<2UCl6?8c_&b<8$v$$U9ftRKK@eM zs_eY%g0!Nd&n_``eH&fl)@8Oa!~0f$0Uj(Fj}uUVu|r=B|9-ZNojGG?%g>o@vTN*+ z_!pXt+G>2Agj1 zwd6Ykp&UU4euba{Vl(btxQ1pr@fwO)0kPG5oGmanvv6u=uTba_Z&dxJWqszFYWIWn z$aV5{W&rN4F!ssF!4J63vd!S=AMhw#_B$H)P-|kC09UhT%ro88#-Ei|9AY-*X*xew z!^BUyAwciL`aPX*y~GAh%icqgW2O~M4bFo#ZAsm3HaS{=a-mwF()~W)^#5OAGx)1u zb5I6N!qP>>r7ResP}Ty|l6nhGOS3@PZKB0P#N%>h`*s-jp z&GY$frV*R&GUNRWs7@{g<>%ksZ92Ql;tQY}=`1y!y`FS(>)xR1e3Z2Da3M-A@BFT-#`=CfHM;de-$JGaTih^d_Jh;DAcMZ?(bC zA2#~M7iDu63WX*|74#%hsDQT#hdP$;nTjo*xQ@PJhy*dlgiJahZRak)W#V zM^#F9J55u*yv$kjI21bn1!KEYK=r*7cnsLk>IXLlUQky4Apu3a3@YKHpp0}oDC67= zs-pW}HWkE{=WEeb;U%E_dU8q8Gp&_psug>$f9@Vkzt2a$=%Rn{yo;S=S zZ3@bRzIxqY4Y)e@DX7k#4mJko+H}*3k~r}74s9fT6Z8i`mDiba<(W6Yl`% z`C|9}ucIqn3hC7F@d0naeGn~D&mJa1m<6if-@U}6o4O~EOm?`y>v?q3vbygZUn`6= zz58glDQv+9#^$@h`lP-JYzXFqs`T3rO=CW{I2K)XyiIDmeVDsp$7_41Nu2sCI$s$XixF^E)#%F8S1v z-{ECSFy?#HldllOzo#J@!{5OKa1W@SWEZ6s7IH9u3$6laNMYvG%vmqO>%-TA4Z!;> zj{3dGDklej?RdxZ5R~c924(vH)3mkg&9L%u^jt0crH&KK^WktgQ$b;NQEpl& z^sl;3;8ct2nI6xwcs(c+rh%IGCwXp*6S_=m-~_HaGp#UN+kF~b12MT|$`qZXV|;7_ zZ*q$cWwGQj`SwI7m~y#kQ*y~2O3%*FT2ZyRr_IXFm(aPf>2*IEt0|h%TYE)8CaA|{ zp>9VREA(sP1b%ZID1Z41l$9D9Jf{3t63UDXLG?xjrKja&mV0fFZC!RzGt>IQre-jR zP0!ECFT4=0UbO@@QCBxN&8`pE1nhl`N&oxNrn--4uA14bh2eiM&qY_hQ;#+IYM9oP zmH$dXis;(X7&!rK1^COK2%7n~dzCF)ckb2MwDa$G-WPW< z2F}i#l9N3(tN1=Iu~kzycAI>I*Rxf}&YQcLG^3J??Vg2e@b+5HlZE0EZhv=qm$z!# zx$Fcp6!+d?R-h?4^or~6uZ_ld&$sH>zl78p;Mm?W8Ln2J3Cizc_sg+%4@OtrH+pSb zcXaedsA&@C1TXZwBr%X|eeMzec*~R909}~(%2fX>MoA%sh*_5IrPMd{k&67>< zrg878J23P5sUVcgGN>A7QCI7-_2g2Sy-zg$P?%%P@4?lNqf?BXYlHHadr7ZV=ga}7 z^dX?yP%wFV#*|(>85wB!aEm7ruiEBNo-j9~Z}6z=HR9oA;& z(2cghn1iNeoV{lFx6O%RKhZ#~@!^#<<* z#d9nl2%Z4%NV!@nI-O~TCO17N>7ulv+}h}6ZHTy{y%RhJ{*?q8f(OoWf`gq8%4hcy zuL4d6)xh+^^rYU@6ic7Z!#(+DEMFz*RPJSGn@HLP{*X0?cVam-Xzmy@1Uo@F-)ze> z#+tP!a?T*rqn6{$Ed8~e>G{7vZ3bP&n+n^4s&FHVp7e}-o(s=uig};&C>aKd}vl~ z4(GPQqCqyIfN*ACJ+v+6?N??|-bF=e1=-vq5#NT4fA`q>=<@J`7nr&IEhta^+)HfV zv@F*AJXG0WW_EEf+J*T!IVA<5&>K^o;J~y7)NR2nmd^$?h!a578U^J=EkSwFA5)AM zd}#4`Q1SOzeho-@W#!We=)NPfXmUw*PDZFA&)9n=sHJr>C^sEx^#PW5w((7^Uc>79 za?RlG1eI?isB_DMR$mON+^d6lR)``3jSwaU0#DXJc|ku=3EP5N6BBIwFF7XuQ&1Ib z1(iQNE!g2gX;@MN{P+8neI-UOm}wj%C%>d1lNBQL6M`}c%*=wM%o#a(R-XjQW1Et1ka`$* zi5dH%?5V+8se%jg^KQO>9(2w5-sBFamF1Qjc@HQVSlx>p&)AG$KIPo44~ z38+2)@_Ke`8h8CQ-ms1x+V;QJST70G{F`)*Nwj>9(XaF7cWhmDIjU?qC3|WK56?q| zaJ5-8E`yuEY`D6-ohmd2FM*0*2daGTbk_Fy-omzFCv(VI7aJp74_99<1y$uibd_@U ztpqlS@=j!(R{FDUH+?=8WDv{ByAn{us3m5Ko(ig<9-yXb2Qms1 z?lk&ucbL&S2-lQ<4pja}?=rd%s^Op7_%$})YoOMYZRFS1cMljm*U6+HcfDkDBs-0|WQoYo4{7WqIt0MeNy46kP@xO7R+j*bA*4kC_qPwC4M+(fVsT zojB{LhUcEsEUwe?M=~#YaE_wT#HFup^UTaOKbMK0C`g)aPqE3eAj*Z6E2z$fE z4szyr6=S2$i(Vy?Tg$66Zjh7WrH+d_SzZOdS9+EF-sidFqfUD-b$m2DsdgyT-y1PL z#aZN4qHgot^P_I-_)u_MYvj$pEZM6-c>?7`np)FKd?C3eB-u+Co8p}ArCtzq26+|y zF83<={kZ2&h=x02#nZin2`SDjuVO;fdETq!ce>|JjD}a?oCCZqRJqrym>6~2;oLn; z(c>b{XwRJ#bv!S1Qq=vEm;uDp@)B=Nj>lz0!d~K}huO zIqK%JSoR8v+&Vtu-08XLQTIJG8K{TssQUw4`AzBV zS&IgF3FA`2lL=v&n+AApW|ZMg&5VW%nCiU(OWxyEqW{|s4Db0 zq0~V8lhElwXbcO&$w6pQ5E_x0;_UP)r!qn~Ub44kYKmJ#C^hKK{0k!P(=fHXhBtp~ zB(7mIZ*$flXM*QuN5ji`v@<+t$`4*ecGT(PRc1%so0xh1f((fhBhJfS>a=KF{4w6# zX@kNeSU=ATM*2C=ogQ`LScc9GYLbmzwvw}A%sJpyl42(7NH9FXC_D!{l@yLQKP}?c zY-JoEV9rTiWe%02DMi@B&hB=Y%69@6>CD>FJ78OyAxJb zh11`wD2TfG?Mx>F6FZfjJ0t2QvQCX6SKzbZi(rG*6lc3vNo-3t!81*nlOoP^FSRi0 zE=B8OYRzT_z)aJ&PE3wxQPdd51$LWd8Qzv1!2>IbqHg_;9KC}A8FS|{&n=F+FQaLK z#d)KCPmb?YHQN}f@vt*YUCh1vU_D?VFOdoRj+a^zb)W4VbDPApNSxcn+gvipP3>YP zVr?&RVzSf48-~`kYpmIt8IxdibW)0Y522nETH6~{5^=8dQZJ%K-Aw+F`Vp>`6wD4h z_7p;z;EZ#6#JR#tofUPrqn(3R(;HP7aXWPng-(m5aPwdqpkQu0&v>b&nDhkGj+()I z9R)MX+x)`hnvjzMNBEJDieY3YM8fCN=~IIdU#w72yxeX#Oy&-ITgOG>TJ-dWT{0*< zfzI^s=AEAso=2!pknMRw^6pSjXI+*6Wy1|7C&xpq>!wAV_q@tWqwaTjuUY;mH{6ZE z?yi;E9ZyIj&qNp-ac=M`XGh%~2wGC`pS(!8PG6;EiFK0+rJ&bQm6)pXvZz~^>54@( zet6DlFy(|zjD%;2c?sDm?)`-LL-R&4<{uqSg^xFiRK*oX+@UbFllDxExL3h22y9eo zB)kE3qMy*&84#ar2Casy#K?vA<7U84H4L|23*(RCVBZq7 zgN<4GVl{O?@I7}rOkFg)!*UyEHkj{Vnw%K$_T+d{scr_9CF^RKqD%$v!&IH=!O;Uv z!`QR1?@E|b265qEV1u;bg!|$l{k(+q6n8oy_0cq8DNIuUpJO~e{YzYnsEISpJ0Etq z)`c+HI@o&LcVX&Qytno97U)?VDCH`?3gJQksXR%=R_hZpE>$be{3W6Fp zv*c}p$>Ko?94jhsj=J61Lb?SSD?@lZY^c_P@I8b$MzEfIO2~}w{0R~F6kJw}GR8*S zi!Eb@Ulws6g{dFLFMhVnw0HPfv29-7yAY<)2{tHq8_aqT>rvg2!J&r3(K&>o!7}O* zVxTpv*bU$DDsG8}TaOB2$ECzwHOiZNYqX}}?9L6aIBsrIyBEM%EG%0H!&sJm2V>${ zmc(h{aK1}m%yS#}_+OaD`*7T77)G)w?}CxovJNcqRV{b3VTWs20jnw{{3&d_H=-mZ zJPZH1AP8+IbY>9hJ4Pc+tos%rS(!Oi8gY-tpAL`rWSBgrrZ;~Rr}*2W?%OD)zig7> zUgI>XI`CXYsH!E-3tq*NsB^qmxg_e2VrnoTtPpsRv&l=nL+8GVJEHCl=bIQe*o1d` z?wwJNRjGGIh;xJZoXHtXbSYgJ}sQC2i|D)hyvGlq|66FgZ#v1`J!p zim1B}P2Dv0pO#g9yPXNs(j83ga3w4l%ebSmz0E5Jg@BjL>ODjtry525uk89B9n?75FbXuhX%wGkmWdK+`^4fktsOjCx)G5Fq1VTi80Wx!*n@LmW|UcV9Bxe4 zM#J4^1}pX2l<-1A+

zpHOBHI{BhdXu3kqgP!|D)OBW=H6MqX5OIF-D$u5tntLGT z^_>y-DOga4pE!VC!Mb@Pic`WTT^zLVvJ`hNA*H|ywD39UK_7J{{{wakjP^3OPx30( zN1a=}%JtE3<4alVg7alMAz6i!#Ij_4E`1_|W|6G)DxZluCwT6&QTK`~ zs+KdB%GY4@G&{v<=~WVY-Ibvb3x{Uk3(4`20RhpYfuyU<+~NjtS;Q&yDxbp~Xnjl* znD4c(HjS<2&p)20D;3X2-5Zf)FUOysL2qEDK>XQh;iYbjx@TQuCKSh#OOoRunnrQn z)|(^l7MQwZ&JeY)t(wiuypgcrp^AGmAw%`g98{>W$$1`Zl5Nfv(wCNC~W0P%r0gXSrALYSj6} zt9&&YKK3Ts;LW=w#XXOZELqb}oQ7k-G#42G`nwS}G+^pso%uFp@Q`N^Oy{J;pr*bz zn?9N8kq;Y294o?4$?=f0Aq>^dda|IZ zZrZgHCYv#ewCk9K#=7_r8*CxWxHg{atoBmhjJjWLR% zyCmW^TwL7+hQZD-`Ix-7z)W?N;=J#<+oNvRTa9z!KzB#NIj}Rmc@tBd{az)py>2t5 zHPnLO=D_4^te~uEZErU&U{<&am<(r5AFskD8%DiJOH2zf<5S7;5RH7Wak|@JV_`ud z;jVWCGoE|lTtX4_x@K;{N0h~+TUx&rEYKvoDDPOsje%Q@pq)E+VLIi z!WavWypO-F5!^&=CZw?sTseI7a=t_ep2OWmNXs3yQdY@|*qyG}R@gbQvfSfWR*$P#M8spvxJDkORuzFlJtf~O_*}t$Rt713fYScwAO$^iE zS6~_-%!DzUti~|G>VM)xRqHI9o(JRpAScEBn2_xfBj5L7Qx@AR&-<7aq?kfYW}pDH;sZtU@6S5aWvs4n3`aQZ{P;Q z*aq0%E`fC?&b-fA1M8z2{KS!%4yGEIJ1qRIpD~P$^eJY#WoATggsBEI)3?A>N-)!1 z=UMZ_yH4O2yl3!&fv2JGhKc*wcr zH&ID1@&#EHy9zeCioFXP6|hlXCda>2Jz*K_a3R}a!>h8je%aiPnF{h?1BkBm>g*rnRCuZTqv1DSkq>X-&Y6M zxL3Vl2L^@vzN)%Ml%}}Zgd!AzGjguF%X1I%A`?x+X>P5jyk=4~^tNt~xJzIoOr@OP z&fZeBrEoI20T#Gjuo!QIY5tfazyVkf7|t|<7ap&hl$eTpry5($D?OeSax;1cYzQe0 zn-3dY#kT(?Wy`m!=bHjMIhHcK95&n=@!NoS0?{hD=eFRs51D{k$MEoI<6+q0T7HBb zZb9;P^PJ7JrvxT%Xw1_so&e^tBk4N)5rVIvf1d znocyl7m>9(BgJjGD;BMBo&p;fBoR6ZI~byv3Z8o4s!wou!TiY&>ijac23f z^MTpZIP$HGgztp$sE7AK@gK%k7%fR>z=j5SRQdwg*)VgTaR4SGvFNRjxV=8AJ_tFm z^Gr(a?>53TpYUkR8T#0q2TVKW!X^fB+K2YQG=ofB_fJeIhFt~I9%a~enD%7D27hW! z6^1Q^9q#Eqn5MglJKZ`c8-h2IF4V*#l%)Q1Z-Qec>PuO76vbq(g!sB2#Ixh@-Lg&yFhL=Zz3E$g;lslc!$Uvw0 z;im#!k1@kP#0E>d)L@vL3IlMXe>DutjZXIPYrmh6E&uD}nm=N%U@l)y$oSDH zig^hpf5#s_`44l*OfP1tXah{i87VH#c*5*|jc@GF|*d%ydY zfP2c%rY_=2BknaYvr=fLJp;4dq#gBtF`Z*eH}HyFaWIX@>o7O6BGgpPUQR8W86v9_vKHJD|h8EwfYH zc86jI13eyj3D&QwDZjzafYB)&^7KCpGfwBhw50JWg${4=E81YiCVv{c1-p891dQ)N z_-12)pW2q_EfUlSFOi24At!c=#%^5>n^slTm#~Qe8?`pMrsL?QgBM40zoH%1PO3o?KOu|v?1b#9MmJu9*_8`+d`$|kDyRfj)#32-FlG`J zHx4^G7cLqQPe5ig7Fh-x7BEe)Z>w11wB(w#9J%@80r3P5=jc+q8Vk>YaVTZRJV(e{ zW=bU7G~NlN1fx?zC`t`D3eR94gAELhbKes(GlrVOLtMJ*CHMp437EbyW5U0|Ci^3D zumFEK%r+E#IShlcgEitWhnZsL??{e^$c4Ez;SlyPtWOpD5hfQiajWXl?kcwbFKi&6 z9?CC*l+Evkm5PPV4w@7L+Zbh+G5?S{8JN~9(s z-oiosOUt;2d4z}W4NOMtk`-}E{L~~eJcekRtQBGpObua=D2{}WYe<}zz^UduLPpoa zpqnh?BOB)ZW>_!s1a|}O@33Iy_DA($&E@a5iE)DdEZMI(feP%OqqQ9jB z=5AyxX^y4iVJb9u&h1?9SN6a=pGL$#rl+_+6S8|5GrWIee{N4~aD`vllNv@I6{~dA z#z=S$te+kgI?wnOy(lHSi4!`>pV!Mt39lr0iU}SfILHK3_#13CSW56TqdrS;pueT3 zlj3%2YAT?|yd$^)rgh02-`;|0F$nG%!twlNI=;I|ONkrQ%pca56{Z|TmNeae5*FNZ zyMGX}1IOywuenX-&+p|#+ybcaBq|KAgYkVIFN+As(OFejN8FQ-p@A@lo*U20U_)TI z)XkB&7mo2aVoEoWA5fvE*%9K*+&vN?;*h=k9E4b-!W@Qn%uW(dDV z@ML3=Q(Bs(g_~C<^&;4Wpd?MS{je01Y)C67xJwI?g>Qll)TR~QqL3-HPV1`FxdgSa z&>6Kfi`EOEozn6BN7Iy^|NNB+BEC&Qe;fZ{9&hI#0g2J$8?U3c?nF72|UBy zZ{z5|^T{>42QB8=RW6}ZgGZo`5HcgElgTeItsTL)Z0@`h%&429w!&msbDB=-^^Oh_)UA8R?%V(m!n|8_h0T( zLfWy}2+|@ooIZ|sUE08Kq>r)HfOrDkh-NrnOcv1!Mj1@ItuP#wni}*qEe~|}beK|^ z)nYbG1H#tAI|hgSOQ!w<>vki(9Y z*xxX-I-UiyZb`$hh1qqT@!kN_xCKt?eCwx<)&%75V(3I=hHnY1dr%MWJe*g3_Z*UR zNU55Kl*!xRYImcL!OkWFGmj}Uet_u$PR;1w3NxK$TMuuC@qX>90s1Sbl5t)f^c>5A zAqn3A;}twp_g#fZMPoN7*Bqq0TUMvIsf3Iy-@6uOi{hKD=V8Iz3Ac8Ai>MiA0g!-F?;Dzl^cDz@| z4-8K_88!I6U=g8U(GFIyoiNiPj&W||Q(~i`XTZZ@)^Yew8Dd6M)B9PNrf_h~cmDJ% z&*!G%#KC@@3s^>G3^q03!&voEnA*c0`7)meTH+_3g0q#JY6=Zre7g_8bQTQemHQ{m z%u|^?Wr&%9A%AOgGUh{Ngs>^+{=d@CpxUx+Z12;u;KdLKjuBYhZ`t{)Ek_iYplIgf0>D6NhtLgUzmr%R0lx1@(Oj zI~;f62q#ohmGW)aXc)5(gY-Pp3>=FAAIMDiD<|XmOA*JAggYye#GmEZr*$xAf?tu& z#D5KOQtqv8W!T3yt0+>q2_+)(nCM)1_u&$$w@p(I&6LBW_ZYBjSLgWLNtQ2<> zp-CjRW7F|$(}-IB*2PY;?@pm%pP)@833KS{rf%=RI->e?QYoNP5AipOdS1Ssm_L-sQdcxQbKIGym;It zjoijrn~X6{3pN0EI7|zW`Rvz+nVU6Dj(@?-qNdmU$Bm7}X=R)VGdFAV@u7_{*^)A` zWsh;OEk;|~Ww3Fo-5)g>&wyzH1g(ur9`D!5VQSAFuU4}pBtAhXxLM*pMOR1f@-ZE~ z=f-ezb-oikFK08PZT(={8_fE@8m7%F7(I8*1!lQ#7CffDdxB{xy=PYMgXwU~lPFl1 zi6+jx?w$$L_HI(%2h%D{OG){)WyWf~CYgyF+yS|l!GcNcC+5;a*hwbe#7JD?h5p=p z7L2JER#}hIR>Cx%>>aNs$3v6>CoN5mPcy4MJ>h$v^I;kRR^s)M8jI5$uWtH4_f=H0 zq3BppW3ov@TQ1{p0h1-@5j$21%y)@fNhu`t0q2wL?3!LyAzESrooUV29~dX|Et?6MRI_)|Q;3M;U5E+~OR+PASd0El+m=38ggaBZ8-! zV8?tXlxl(n1Z{$~ht)p_+O$IosuIj4XtQl2IKq_BW=55oP4FzEt|Mp@gbS+*JA%!73|{g43e$$u$WI)KGt4xO(o)Y0;x3=*4=X4CMpUgl%z=#&*SW~d4|4(;Y#A=X zmVGr$x1d3OcQZ^5YUX;aS+Vt#H7f43S$>@>DEnGebI8)lwZSs{p84`4>~Q`QO3j?- z^v9}l70ez7IAU*vnNqY*9)Ov-#IY*e<6=?n=Nc9-Z++V5Yx|bLmv753k z4uMsjFXAq_#2TJ_a){Mo;@eox>19ex++KBr29X{|M<>1D8B^n;B#{4w;j;7r-^8e* z*V(w>8J+t)Ono$~@^3J$`>gbQ?mhTAPM5)*)D?teXtUXDfT^ya4bE?Vs>e7NUSGAH zvHL#?J10n@2Gp8oRsu6u2E(cz__{a3RAq1j9ex+qO#>6x@CLumLJXCCqcK!)8g!S% zn4j3fiMXw9s>;m$!xWfCkED#yEier^`|J6U@LOWRJxBfd_NWnz_*j^oDH)OQ4Y1RK zr#9OO+3lRNj=I?i6;c*s$YJXm79tYF-^A3iAS_MUP2v74!mh^a{MATRfu`~F$1R4Z(y{zmlyeU?jS?&#g6$a zrvBFBc>lfFjPiO^RZM4i_!z#`up0jSe%Q-0bNsjirZq4q)!hpV+}9sfkCeBupAkn& z9wg_IM2UiC=Ly$N#BIXty`7D8+q=kajov zvxH|!clg8Zp$Fsdh)ojRSa!M7q`?4pMB;Mq^y@6;sL}@yMwbv3myn(5gb81JP^Mm65b5sMwtw??lBJ-Yx|={JCV3^@A2y_qwtTBS))l% zxwL9CqyKpOoU9!yId>;?J_xkQ~G7Y)Uycwbi znRupm=-H=#_QXS*qJr!4pDtf2q%md7eh{5ewN+2x#0R}TyyLcP#WSVoT*tWeJq zmOClV*M90sOnUlC9mf(@5V?yWr>*7$``^!41VOh=LF(rr2{VQZP*) z(`x5)-+hR|==6vg_u!*w_ad0QklE17i8xRCl}ht7s(igxFgY)Iv}zUMaQzfav&nQT zyv8gBX2zTh)2v{T%;Ev!;W+lpH(-a&;XGD-5g7P!&DLkFMwV5r2UnS?(3;4+^NX_$sIbuk)h@y}^$z zq4c--k-m)|T|)8g{HVYk{3zWnesuj4D*gLGEdOYY0&4Lm{KWI~B|j?Y8-8^CCnyVi z7o;|*3i!w(XxI-He*~3oKR>#JG_)*qfPgX_G(zZi%MXFNgkgT-l*pojH7pm_fhU0K zNMleHHL>ykgz8{(;`t})_=jF?LZM985>$e=K?458Sx^PGx7Y#HRUPZY``Gk-ZM;zB z^as_jsMUok?_^N_L|^C(0*V`#);8=e7_0~D@ z!6Gous^b;K^-ri$dAg&2`X*Fj(^W^+!aZYf{X1+#3XaXe1)4=*o)xO2q#LYW9n}zy zjzPB;+W3EmS}Qq%1dhc1A6$o#W4f-x!hg+X_S%3y^m#tW+;QN;UJ5-Y0Hr?){Vu5NH?xpm)wFDjw%3!T6b^;UO{Xtzq1yd|fiKG7t59F6B7!0Zbr&&w|HPuFd{0W_9ag+#` zP{Gj_&jnTC6i{pSbc+R``ZxpBB@{2rAfO6nTHzv_pgJnyEUOFEz)L_?P!1~oa;skj z>Jm!7TIeTiV5Ajuy_M%#ya7~k^A%}Pe1YZFQPM5^Qn9{`ua0WY?N%2mc!%X6CqFBw zfIEp$zn9tsLh!ofCpJ z@*|r}sAM05GRtS6qQ2ml?EJNeMWx#Z7k&dO>N|cN4c1~XYl8_I)W4ymhWt{##2U;* z1sd6e|AeZzDez(fi9tfz4#@)kJbMP6&11Z)lm&dF0%!kXcGuk-~dok)aw6)8lu5AzB($u{+k6| zLZwRuRpBtJ$FM9GsE$fFoP;v{NSi^Zg3ksOKicX->E~Kq|IdT2{}9Xml28SYu?3B_ zIL;O*R7K~5k|y#?=_i3|Xa*>!n+_^nE~x9@VX#N%*@Qxws?c(wHsC8P|4&dAUqyOt z3pdz&LIvmZOY!>eS#$;1%m&nx1w;rJ3jN(1?FnYFOoo>S!if=c(ijjxUx!I#mskG=sa-J7x5 z`6hz$Y}2p5p&IbE)vKclcn4khE~t^+1*(T%SbdMxzqWiIs7t7P-)MGfWBn6U0X6ug z2>m}`!Z4@?)VAn?a+&6!E}_ai7L?S+Vtb37K*e_jRbDqxmrxr>S&EGqU?ctswZNT1 zylx|h*>plR>5usRu_sduv{p<(Bdti@-4FQ)lumd+jO_qWT%mEn-y-ixCB%IcY^BSJ>XH`j<8q5D zEj|D${Ue|n_9&>UI%B?4;K(l6*O(;~v zYRiRc*c!|K9X27|Ceo?A*KGR#0}cuj%Jkc8hUzFMf5+-VHSAr>g^u6yEqqu8*kxs* zlD%(vbyT_!Z2X57KeG6-P5+;u%KyZs2h9fcsTHcD+W)!Lh06Ga#XX=J^rej#D*h{r zUt3+Mbl-ro(|#NOv(*p8eE1-OE}<6icsZa&HKZ;)n025EYy?Vg0?O4|*!VV9ZwD&f z@s@W2m9D$hdw{xn42T6l^`NK4UKV?UatZ#0f}p_upe|tzKViEa!9iB8jw&u{^?yRe zpG3Tpq}p^swR3zhF$P(8gKRCzam%D({AwV;fEyytdM zEn5kypa*S&>L?#vgRX+s+W3D$Rs6V(7plDVpc?Xw)rInmvW*0k;RR4FtF#G(s_;!v z8Q->gbyUGStS%Ja1-1Zx2K5NKp2pFl8eZRWA@!Dp8dyQ7hBUO8Xt5EfmL3hNfM%es z{{&TDbDO?8N4?qUKYCACJ^eh zuw1AHer&l=@t;`zGpqk682G;u%EVuRs(7C*pgO98Z_tI`+IV3HcoQuG7FA9Q%Y`bh zC8&I@tX>Uu%>BoR|6TXD?6P+fZ75HMXlKiBZ}E6r&p)Bs(ZR+GmA|v)LdACh)$VSV zCt1}0HYMNfrT=(K7lHqQDzLBBtD{_D5V{IJ$;Jy+;mMX)N2ME#F4r7p;}0YCt0iX= zp$bRY1l3U^F~;gbt=JcUs&E3Rd>7jI>Zpn*TV1I1>7evXP`+MVW)l?u1Ijxtv${|P zD7~;8R6$pOYQWW?E}=S9VYyK8*MsWF&7kso7Rwe8P>&Xax~iipxXtQ9@g<-NT59<+ z8!yyhWEH3iR)b3Sn2i@oUu(I=vOrM5PY|I~%!{B3dd()Rj%w&ubQScL#cdY1gDU48 zQ007T`DZr%b5Pe`k@1(HjC*Xty*8mx6@Fvmzq5LER6}%)wm-43ob&>Igqae#gsNvM zKgyRS?!UCgX>jB_Q%rH6HOq2Pmr$O2u2SF~pJYOpknLYNkl_?@KjB#C4oI@|~#;p5=bDk9=p!6MwyZIP#sTzBkny zrz78)nzO=@?@W(;XUbbWotEPDcF&@?zAx1!)UmQIs0TGizB65TXGkE`R??{cc%7h%Ie>j%2mX5*Z2SP?@ak;^8V}Z zOrI#N6W8tbGnVeV=8W}8SO2^|ZTPH?jX!*CW9PyBF3Ns$|I=$Tp1!;9S?=tS@uxj} z+NLRquVkK+`^52UzkTfK0lkLqe(cS@%WvC!?dIJW*$Ms8;18CpSNz5I#f|ZYE{{v_ z58W3x+^O_amm?gIuxdHNtNuX=D^?&(Sb?y`U%3Kd>`H`$l?ZS6<5nWX-;c0C!dBnC zA7Pz@-1`x>`RgTQJ%G^i0fe{x><18yLX3A$~2w1_|H$?plO(5^~of{OGTjko7o1%f}J+``M2pGiP#I ztat`t!ZQd7{>o<%#y*RX@GL?Df84VO@y{V_kdWxR&mpXnkoz1$V}HGbtmhG0K9A7E z&wd`E*+zsd5}NwWHzI73Fnc3HbH7r;tQQcHUO;H!m%f0|=|zOy5?cD5Uqsj;Vcv@f zt^J)6=DdV3@Fj${e#J`&$uA@9lhDrZ|1!cJ2}@o^INskYVbLoHBVIx1=r4W+Vdy4= zLlQdsshbcENLaNAp{svT!iq|S36%&*{>n;(v6~SRHY1$ikK2q8|0==;2|a!HRfKgC za$iO0?XQ=R^%_FU*AV*p*{>lq+k&t~Ld0*r1!0qf*;^3$`;`)Ay^fIdIzozH`Z_|V zHxPD980dF?17U}Rd2b*@{hbo#yooUIO@x#EiZ>CGw<7G5Fxcn?;^B(7vWq#`(1=)I}o-=7~?nJfv`!!>>UW> z{7MP4b|NJ0L^$6s-HFiYJ%rs7CitD-L)al<-g^j>{GAf!>_Qm03n9&~*oBb%KEgf; z>3;wB5%x$}@;*YQzgNPd-3TLgBTV%d??xE<0m2~(*?#H=abuim{@sG<{y{;GKk7pu z*Ix|_s4yN5dSg41_?8K_hS+k`e}k9e?8!5eL{50Plzt@vp+#-_9?;^ z2^aazKSkIiVfLp8rGBM^S^q*v`WM0_e(ApuI(>$)Tf%I=^JfSeCA>k{88~t%#A;f=;utCCn-~Ae4orK)45j=mr zgsgoCE%zZT^t1ONH2Vf&iv-_q{tdz=3A4XJSnO9ynDs3}(zgh=`K8|?bovfqw}d5r z=kE}9NSOB>!kzw333I+j82CNH-G0UQ2+2Pn?31w6@BahB9tlf+Kv?GQm9XeXgb_a? zEcX}xh%odgghLWm`l&x59FVZ;Cxi$5gA!KkN0_i5VU@pfKf>6b5fXkzc*r03GeZ0? z2pc3k;=8{ftdo%Y3&I+Iy@ad-2rUmFto5@GAT&FOutmZXe)EF}nyg>Q97M{euV#SJX&YIH3lI zj)g00U<{`Q#&F{m!)zc1W043*ke5r-V7R5eC*q z_}H(gjgTCVuusCLe*bucJrb70BYfuXm9WS~7~vv(;V*U(hSotiBw??gS_k2PgjIDA zzVZ)BSWy>YLS2M?{>r)tW9uO#)I<2zA6E|{J^^8agztSf0b!kl+ysOl{q+*E>LawQ zkFej*u8+{H0m2puzxd4?AZ(H_y8*&Mzf!`ih6qUw5q|Sa8zOW{MA$9ikl#5GVTXiy zi3orCJ0;9%gfOrXg5y^-LP&0muunn_zkg$dJrb5QMhN?RB`i7$VZ>1gwf)6MAq;JT za7cpdr#3-2AYoM#gu4Dg2`i39m~b>gg1_=;gt1K#5}G13@W(Yph;N3lK|-SMHbYn^ zA-5SqV}HGbtmX(Un zN(r+%AS87_NbyTMAav@8uv@}FzjH@~9TMhsM2Px3CCurBFt8KC$$mvAgyhZ$`y>qZ z`*%jzBVkErgdzT335&WQjOc=Jy1%#!!qBb=ha{x>sa+8cNLbYsVYq)#!isJP6S^Uc z@K<(27@LHUkc4oSKQ0L&zB|GO38Q?sJHk2%x!n;)`|BlSoq*8t1cY<_>=O{0^+4Dn zVT|9r2f`)^vwI+nJ!n|Gxll+|$=JZAw z*c&0uujq}C+y`Nwgmk}uAA~&;mh?f$^!G|w)E8kyUxcat;=Tw&`ym{XknN}TLpUH| zRX>F3{y_;VA_x;A2)X{s2*TK8goI>-e1BXrLVSOO4H9PfZhwSz5_0<^6#ajky$75W z#oE5TX@=xVhFumUDLF4o6p);g8ge+wc23e0X2vKDb_DFcyqRS%e zlrXF;!VudfVeqpE1)oJ2YD1ny$WspCgoI(1w;aMz3FFHl#M?0mqst?dD~~XO*H$Bx zs(^4=!bmG!0pWs#nH3O5*+mIcpF^no9Ku^R?Ky-R6%p=97-KanBHWU&v?9VdyDed1 zC4@GW5XRf0N(jv>BScn4m}o63BP6YYuu;NfOI8J8t%N>R5Z<+Q61rDK$Wj$yiuI_9 z5LFFfj|8*mY6v?e46BAP&2~u`Tpgicb%g0Qq&h;L8VDyO%(T2U5ROV1UjyMIJ0@Xt zO@wka5oX(%nh2$8AzYR)*GktyxFBI>Erj`YQNq;P2z6^CEU;;{5o**yxF=ze)vSYX zOTyAR2#f8ugoSkx+SEl@YK!V3G_QvcSr1{kwWx=Xv_8T{2`epGeT20V`qW4G%+^Wh z-T)y>1B6x9qX9xxLxepN)>w2ygq;$GHAGlvyCe*L9--j#2-5%kBgqiIT&e=r? zQ#&Bk?SSx$P3wSAqa(sS2^XzqM}%7vmUcw=&2CFr*a@LcCxpwks1ri-&Ipm65w2Q` z&In1nAZ(Oyoj2Mdtd-EG3&IUsC!u>+ge+YV{0}%=iM2N8=0}=APh;Twe7R&o0!cht1Uqr}i$0Up%givk}LUtQ7 zD7;x{4m%?!rwCzn-x87H?*!?7BJiF)s4qUN=lgVFQZY&rSuHjXVE zLU5ZQ1Q)bLLlBz3f)M!%LSbw13PRGM2pc67wPZsP)=KCz6d~5uN$CD6LY7w%;;hH3 z2vNfj_DCpc(ZdjSN*Fc_p|tIiF!(iug0CTzwIQz|RYD_?=J078jO&gDJOTs+~y{zU0 zgoP6kmQFzEW49$VpM=n6B0@h~G!Y@`WQ52`2>q?aB!smRHcA+1$tELoe+Qw@WQ0Ms zPD0eX2wC1ic-eZqgRoP=9tlG%`dtS3E7o7mP}?QvRm=V!&M+Gy=QTSZC*JZ-!5MBN z>CpGjc{*X~TKbCd+xtF3K5g6{q5iv1xL~+I2bOtmZVFw{5nZ z@pfCz1Z(&K&O}=zXOe|Z$C+#`aBS@i)pa@rf7g=DK!49V$eCj69PVvuuQ%*>()aO3fk5aDE^jYH zxFF%Og!xu_4#L#A2s7s(EU=3bYRp5ZI~QS*O`D5wOTs+~i>>B7goX1Fmd-<1YPTgc z{}`doe1zq;Xg)&H1qhKJBdoL*A0w=luu;NiyigFK`$B|13lLV>ItfvW5V9;pSYtgF zBJ7l~N5VRbUW73C6NF)l5Z2o+33(PH6#N8XqYe24;i!ZY5;j}j#R#L9AdFv(u+@%9 zD76%!+!BN@ZOjsc3lc6%*kPrYB1~O|Fmoxw*LG1tjpYb+mm%!3Y0D69Nw_Crx7A#Z zuy6&!(&Y$y?6!pFD-qhPK-g!CRv;w(6d`gY!U1cs5@GG9;ZNG8mEpBmfh7ABq5Ef8 z^!XHvL$*#r)aM9UK0`QSJw8L&DPfODwMqt_seUyX3aj!7uB7NOi4gtIng4Z;NpmnEFD(rXc>u0xo)7U37WD51s| z2zA#XT(oKH5N=7hC*e1%`31tl^$1J9K)7tTB{biF&}KctRa>+kA?Zeh$PEbBt;Gg} zwGuW;xM9gQB6Qz`&}SpUpSDgy)MkV%n-FeVk4*?WCG3%K+oCrk4Bmn;Y%{_g+a)2- zR)m6E5boKKEeJ;?oRDze@@_>Ky$xaf*6`Y4p+5UzYj{&D^(9KVZJ31Hm~EI`kZ@Uo z&q{xZFm*e^%r6m=*hL97b|BQa5q908?qbWsDu*|vRdA65k~Jp82>Fob~`4a)Lw*g zdk}Kkm^}y=BwUv8jFsMtFm)foOkJ;XTg80{HTEOS--nRbu1mNjp~-%P{5E?(!omXx zz5@sat>FQL<_8g0NhoZg2N9BfhtTC9LQz{OVXcJp-yy_Whwl)&e~++3LY$@k9wF)w z!l3UFO4?QlJ0(1G2%)s~KZG#&Fv1}TWi9(*ggi$OMjb{dX9px4l@NCXp@NM#f-w3h z!Z`^Qt>{sNQpXUc97U*XXCz#ZQ1uu>RhxVaVd@VEe@dus6@NgeaU5a(4+u5wx`bO2 znjA-{ZL^OfEc_9{_aj1GYxpBV^AiZGB-FRi69`F9B6K-{(9l*&SSum@NrXn$;Uq%$ zQwTdGG_lmD5TZ^a3_69-%(hC{DdCyZ2raDtX@tRN5DrObW!cXlbe0++WJtc9HZYgS$e ze>XJg^E`IUZ#oaRNq>dE_wJTv5y|=w=+v(>KjGAO&E;Ri$0hfb=GDlNcKz4zxeCtG zp;NyBeOasQ_ zGdw&rS^MV#zgjxH=bELr!pnvw8(oD5;YsI(HP`Kyd~Q>d{$g z=eE#@h_aCp1+tyKre;LM%i*D8*KAD^aVpf8W+X|eq>U8Q?k0;!8n){VyYg#z z(}2)Ec|=lQY88DC+Dx{J;G+;WfM#OMmhLNEmPbkHdffVi8I>Zm>4UU2S?-lHg z_QfKSuGvyIB2}RH$;$9hhsNtMyIwycHbedyB*9_gk=Aggm1z+1gKyRB(2(@XvbwUQ zx%%~uqhdF$Ps51gzBhQSax!H;GW@OmO(NFpZ{lm8+!zrZQ@r5bV6lCG&(2)&r?qQcEZKwyw&^a1D}&l<9yV+lmnkrPSbOs^__M)sKu+3 z^>*aI=ZrI?8TJ0~z~?8Y>F4FVUx7R8G(8IWkrLqZv(wZL-Y*HBbDDmo$M5XUJ56nK z)B8E$U!17-MSS8OSH0jgHP}+8U38k>4KmGXzdB7n>HVjgh|h0M(+}!~s@3^ia+-eo zHOy(3^==t8wcch`pLSF?TtQQKG(4xa<#XNHWx`*{X}>#7zusHbX*Zmf8Lhh0{yb9pA0K*e(36kL|Uh5Tz605ejYO&qFSvW^l^q6oM9m}@0V5e zjvFOd7`}8vBFbq+(6&2G@4%5=QTWJ#JRu|*li!ZLaIWOjzd@%M6u zSlV?Y)5t_;R>g(hDL6ep3d5jbV~Edbn>J2?A z120JrnFJax`O(xndJAnXa?@}r>>_iu4Oxyq8*WjjmBYW%X?oL-($m!Nsnd!(tpeKT z_~WzTiF4v}_}4hY5>Bg#))l`#C7o8u`K6UYQ;jRb>uwyCaduVEMmnvm)2gDq>9l8k z^uHokgB%o9gR#6zusZ&iL7(Sbcyrsl5+-fLQ_JG)wFFB7PNR|8E^YJ)Mh zH1KLWyE+t%s&UYj*X1!nc95)8u-{jhTi{)8yLR zrT4tko=0mTP5tk^P^l6AR?bi_S5n_KhBi)X>a-?kZJpN4X-(1EIjyw>02w}JjHuEvhE zwlL7yb#q!f)&E6j*xiZkF?_`t_CS-j1H9_A{?4u=n)l~}1JG2MPB6mR=`~ZzxHBx_ z4AbfN5}I0}3%mi^!L-)O3V`?`zV2mez}8|k#ZXjz^1hSU0?Wp~;rr@erd z!)b4#smA>wo-;yc%V;!$Lk39G8;x|jjB}9(;(x_yZ##{}WZ)?)e+93RTS%uD) z2~K+nzy4N0XUjyV=@dTg&WK4)8;o`!8ULyOCnKsfLtrnWPM7zbp_=c*pkc@qr>Xho zI_-U@y^1y;O{bMPZ5aLqPMhkq*U+Y+=}emz&}e^k!DK|8Y9Bbm;rPd(=`-DFBk+$! z)9E+EX|LlSj;7OZrqf2^AB3jUPp>Z1AbbN}cG^cy8-+GNuQ<>-Im?M};@4mF=`-7D zZ{e@#v^h>2jaJENbDcH@t%}p;Ic+RjH8h>#^PM&hzq(zgc>Kpse47LHyF{%$RO<1d z_k!qrUWlfFGy%RqYmfVh(sHFbBGTu3WO40!9j@o^)fU~F1wue3I1!Yi@$N&VzldeNsT6h-A-JB z|E@Fq)@e)8?mKOd)0UxyyOF-vY0J@eqG`_9=d>00S2#Pp?N60o2`!y=z+3-)il|cS z)Hvu2Kf|xL;pi;*&S{_H*I#UFbbs%(Rrsf&Y0w^W+G_l2ZVlSQPFsV2x$EL1PFt(V zXN?n&B63QEtbK>HK)`O#_X@n>^(C!DqcExXfBI&C9bPN$u6+9tGcnnU@A zoJLgmW{7YDU!pzbwDV5ej;6Ossnveb z`cL`ofccC71v~SSZx(IwOJ8ie> zpVEm}ocJv!*<7iwI&BYHX%bYz*PON&e_1qzT}M-X`=Fjn?}oG6k2ct8e>m-$RxEtB z^VQg|FZgn;N!Q=^TxfhlBwfRF@2y{F{}q0NOQ4^9KLSVL82kXo;YTDF5Cy5+3Ze~32aA1aNq;| zy>dY)1S8;e&^v;DgcEQIPQw}a$+o`eOI!3J-e2K2xCEEs3S0&K8TetS$NAL&w8`7Z zZolZu9bFBt_9$w=K8k)245nJ?LB5!tFXJ5yL!bk6gig=}xbFiF6cFudbQ;*)c68uXM?wzh9re#u+V3l2Ko9H{4*lBEuvizy+KzSY1%l`TXqkE z-Y+~C=E29-;w4|!WXticfR#4lC11wuU*cs?BV-2*gh8Nv485aS*Phn4j#%jz(#nhA z6VR*D2f~YXnV|S#cwYm(NVFZahYrvY^m=#Q*=Xg{7TQ63=l~r-3&S7Qx z!)*K+a5F+C9aGVlY1+Xs1hOzvqd+g9N(<>AJ){J^)k<%K(c5Cq!wJ$p38&#KZ01QD zUrhW?f_A|-@Gb0tRj?Y?z*_hM*271j_w>vFy?Ap1OoT~LoBGy+jU-(Sw=z5j#UTz# zfFF`TGDr?7!b8GCo(Q)}LwwcJHzm<#&>UJoOK4>?hxoEufg!%AOnL><02l}_!XS7F zUQTW&hxjtZuOQ=x*2y1 zY=dR60(h@(eBeexHx9bx$&Q!<@<3k52k%fX-5~0QQ1^Oze|0+QQU?2Hp#qGh?X>4J z0Y*c2=mQNw`#w#f8I*v<(kFeXUZ@g zX22|%OgY|xGx$G;HLxDG!gkQ@zwY*zfZj$s5hlSHI2y(mW_ruV3;95I?z&Ugow@GBb?03a zia{)#q-RgTX*dIK!Dz@wp|%j-1oS4jYqZQCI=}U1)tjIfss0UjKsU?xKsU%?bcb$= z{g4E{hNZ9!mV<7DSHc=t3!lJ9cmqa(Zi>Glon^2BJ_Un{^g^Y0{(FU5_MrKDL2u{- z{a_&aThJAMJ7@%xIIf0U9cn-=s05Xv7h!#%uj0^6i*QrmJ^@ccN=O4~K{w1RAbutP zeFm#RH_Yqc3z!A7VJb|6_n|x7pec33oE3jI&`q&!f}f>X!*C`H@A5pKWv2!Fq8!KvdkB#X%lDwFLSJW*&pFJY=^I4 zC+vc6U^jdVdtftcftjHD-6GnjDuJgYRG>tyKsU3uK=-e=DbyYK9(IH7VRc`sO%ZK^ zXwyS`9G`*qFm!*bH~O9+z3*T@l!G!*k@PA-WvBvGp_;WA=}Q@33vX>`4;`Q*bON3q z2741NgZ=4mjV zA@B+eg;!x1=xdA-@CJ;AG0*@S!7SKj+28QxkKd2?0IY*~Fdr7c$FLA?alR#iMG($; zSf3`E!pMIgCex4F7+t`qyh43CQLmzuIpJx@&VelG^ z(oS7IJo(`d3aW2Zmcv|_Oos2m2QVG-kYQf9N8$bfZF*=UMcYE!3`jJpYG46q>t8$l+QlCZ+NIa7`~av5)u09>PCJ(4I4Gf=`b#AE3!HK@>_%1b`1p1-*$yBTbo&p_f*YKLwps8FZj1hsEWGhV@e71(JFiJ$`7Ak|xw&z5NwI~1?AsQPU>wVBoKsLWpfJ30Y$N9uR zO@=$*YuE=L!!7iy#JvR1K`KZCf1?j;NFq5&EIXtI)%s%sP7$#-RD{ydnFvveh)r3J zy%F6IZ3kQ%%dqG3`4`&x%Mst7!#>a%IzdP1j-BqCbc)x+R)uYazX242+@NLXZO~nL z3%CW^KG_dn!4A-zrcDxUmegU$Xqv4u{= z=w6@(XmJt+np@I?_B}Kgs}Xc9NqmvhM&$RPEA?8K2;)HAt>r>b7#3EN2?vi_uqM=i zl$3BciB`v74Q}E86Lclm1K+^cYpRa*<$NN(CH|eT3%q!#i2toSmS0zr`?%lZ>guu= zp5!NDp2js-gVKK;iN$VwCgOA^bAs4J3w^4YGplGs9D$cq*LI%>t@K3@EL5l~gs8 zv6rdmN>jKJlj~)Y3;#2qjGhK9Q@spx;MdaC%goEjOUFyw6*B(cOubTj1}c;i^^Oz! z)r7@C-J_xSqdtEnptbxXF(HkdFM}@ zS>jC83OXZ1E&Sb~5!8h`FcAHD+y+n&>O({52Jv0_Pm51|89WZR3&)+I6SRZ2&J?a?|y2T-_>E^3B`Sp0OKFRx0f+0~mEUd2BYhQMHW z8D4@HZP9dJ?s#v$+f5pZGKe6#i#h%T7QriMn#(i~y@`JmjD$B}ICLiLHQW}2E4{?o zt5Xy%d)-02jynRB$5`E3YrlRBf*22PgI2*4aJ9HxgZm-Ogz4}Btbq4m3cL%e!C*Oj z0#o4~SO81mefSI(!3o$vhfaC@Qkgt<=h{9a+b+Ity3s#{brbp`px2TJZWcjD$#fz+Q~ zX}x|_M6b)1;9m@~RiAp5P{CD@#N$M1due$6KUw{+rt=EmHLsdU5xiP>g-}Fg{(lWf zZ`^p@lX&2Hg;#g2QvZJr-f6hzp~Srccm?wM`r$rRhF&4PeqWFF1$Z-oPTzwZ?}YDQ zKh)jFe|upMd<);eF8CUDz;@UGULs%O-vXKsHsfvtZyB-4qG$QC@}qWah$ND=43qg5 zYdDL|`mYF5YATNBsz{#Sb5#QQH8aUpHoG6vJX@7Gv8`w4#Z$P;mwjTFpcmO|6VK?t zLy09Wuvh3~*c^q!a0GsUS4j5+?s4!6BfqEr7zks;C7vIZ;28o=CyJ!@Ovd6`VM%d+ zCF~bC59i<~$d1j=xM!gV{tLJl;WyBiUHOQsZ<=)nQWUhK5eA|0{1*cEG5iPa!acYJ zB`~~>dkM1PzlM7iuE1qbqDt^D{C~n9a07k^?LK68$WH#7=YDf-Gg1)s#0Z)Lwy+{RVK;fxr z0&U&smM8-*H$WkH|8;ejGmO0G#rVKoM>YiAf@?S8djg^`*6u_m&^C+L-FQO?4(wj& zJ0U%<{NK(PrJDzGgL+uE_}Y)s-GXlOpAN6hk`Rw}KC}a(At7VArEvBAObLjEB2W;t zX`msLcu2{x7fc`QpU6C|Z;rvyCzJPgDWF_on=uJ7#hTD_#d>P^scTq*+d*i4w{z?w7&|W({ zDi3L1+TO76?5jVfTvQ-FP~e9ns@EUx@x;BY)>UUbuBTOC6`~eAJd(T)4R$7TVd56> zB6`g%V{eElg4c?P24~`AqgGul@OoX>#Gz^43&n zwcIdx5j1`V7z{@ZQUIOVuU?{u>!(j}lpnbHh`zD@IpvZ545*!ICy%Na`UM*xN z8@X?R3Zt6Vp#bA>Wj7XA1=1OzLQa73pmbFrFU>&9$v`Lp8F(2f;dfowJ1$(A%4RYo zE|hAfS}TsaKm~dqrhw8_#gwU+w%28{iRrVAfJ_ze8qAqE~2(Gv4{u6TT<9UoEwPaKqoBJeEeDT?Bt2Q3OfcE|wfAsy(e zx%MQI8dq0SZPTX0ue}p}g%=4);TmDT!a4W_mcn`X8A?%@%D4&>tHR+ zhq*8tW`P!3y1$GDEtW|?WDfc~xPrC@cQq^q-C2Ety9gG-$Dp%j0lZAuD%{UunfiVS zsAQkPr=T00<+!>BU52{?CXm5Or|F(liHN!AS_N(*Yy+%^jj#=r&Q{zluo=$4X;5pa zrB30GKgoZ`Km|Aohv9qp4qhVhgSZD^KkS3O@D1#O?XVNRhOb};?166~HyPxDtgxG7 z*~^~|-XUGB6#J5Y$aoI4@Hm9Q5%>Y}Vssq$M>qjzL4BwiU&OuO{J-H|hD&e-uENLI zT*pm9|Hyw2|6icn$s4%8gKjVX#Qj74FOMH%A4EVXXdR}AccBM|8Z5W*Q{s?*pj*CM z9Nz>bT!!${;KlhHeFAo|Xm{|dE5taEejiP?y`JVEv9SVV_z$l1Kms;?DG%w%UU9=I zn-@O}zs#j8id^m9dn6}Z-)rmp?PQ?i`s{3qn!Q%pxroH}KdNZb|rz&eQCpaLqQdky`>}TN+9^KQ{4!Ks|aRL-nRwOe4Ze z#LIjl375l8x;K?6GcVjLoGK)n`k+E2p86_t?Ah1CUn7Fy`WzkwRDz12FSV=VR)eab zr&FYFMpu~pdQwGk>OyVjR~&`a0S#S+ds;pGim$?XEu-)T@%*Qnd$mnmTIs5_CS9F? zI@uay-w5<=k)AI?3fYXGfBN<(WE0GWm7tn#fQ|T8wGIbIKKzTm%CU=6H* zWv~PmgPz(Q0-BuFEpqjF8BJeNy##~cMW{Xwd*U`9q5R+1GI*A&=y(& zUz&!r#8r}Qa22laXxig$q+$x|=CrQ3ouCWok)WQ?2l|8RDLjATJbFd&7mqSj;)6kr zBjZ`1#!%hIz(V{(aeqfwU2W?GUsRgn)Jlq;8+!}hjIfg{efg5j!v7I`XsJK-HH!ZP z?;=+MSH#lHu3!#A)KzJ^`!E$B+B4%~-d+ZzXPZ^B>j zC;SfA;Tl{8U0!t28-sg=;~(G{9EBrrSSR)&JO_b)A>Z@gqb_3Kp(nnGrRG?PpT)fl zzrnANnF3zKJpn($aq!}`rSS@rM=7{~9*ob4{SyagKo>$?fwN(7lH*fw8h(cJa1MTf zR@i9n*DMr?{}P&(09tGOf&T`m6}*~K%YXe^5@-)SmyhWcvc>aXR>%UG;R{Nc3WI3; zD&f~;qH!3-v6c)OaMM9rNDX?#_IKhW$JKIT4gRF~lYk$Zl7^lR^5H*Bcm(b}3ZzGL zwbqFb!*d5A8%BTQ-iJ`M5cmhbjPE*K#|qaE5u|{W5D8C!)-g}wrh$b-PmimD%Ae8c z3iApagFlo05J+FMKE;7*sS>H?@~eibiE5D@RC9CJob;reV(~v+cu4f~= zdZ4uGLNdau;MRibPz_{T3H1Dn?DgR3W%3Uxi$_nQYVqqutjuvmcn}M8r(Ivr_M>+D-nNZveMypE_yUbj$LnkMuJw%$ zFPb4FWO$RtQDJ!&tn3m-{NP7VLOesr&afgG$5dThBrLRT(L%)wk_jXbXml4@w0LpNqfFCINAJ_s{Lj+ z86Pl+EfmW_!un$ndfLX}WY*4c8WP%=`j-W3o~S$@yCQ|+RMv=a+p^IY6&FoIE345v zmi@4~!M(mpsW`<9ZWKO2$fW8wKOeU9sTUPeq)_p=LPg42htKSur(pZWcR$3uM6f;HvecV>u`%yrkPL&H zafK#48UOPh42l;jR=iML$P8<@ne-Oe5bXF##-(z$*cpkt><&)MQ6j6g%cn2&Q?rX- z{7hs@TBs=3FDtx-rqHS_Ee0>I*mrKyXA{>13}R&v5n-LT`0B((GPaa|>veAr{4Od< z8Z3$xqRWD}=T8!{tcyK)Z~VdR2_aA0?k&D7+;Y}Hm@x6tC2WgADe>~bo9G{212P)D7umz)=D)HWLLy2aNd z?2cvslIV$#qHXk-z5>ZUvo?n<|k); zq)fg7uYFs7YFOxNMFYJZ;&Ol9R@V2Y%NiJ`dRkcxa$(`vl!p)dUkD4;qiCs6Z1`4R zCVo-x#C>11ZP|{$gdN}Rs~a~C`*awbIed0!t};C|oMH==C{%(a8X;K;c{WeQYOT)h zzd{JzQ;H_qWNmh+2QkQo!IQ(Eh+LI=Q2DS>+CET@TQ(VkuqTq+k{vWLNxIep4=Pq%9vXCsh6m~#~p0wZO{Am;S(Whl?c5{DpOdondz23Cp?s^?kef~AE zG~9_b*s^@&FJSSzd}SXfPauAV2rLfR)m>zqj)_4@)GNGgPV_flrXvZaIVBOOhRZR- zQwVJ^jl{HL|EJ46mm(z%8mKt!wZ!^=L*c%+adIwL;XS^nuwsANW)8!Wq_au8ed&F1 zg^FfOXYIfBrH{ay+ah;+wz0%)#Zl%H3>r0U#chM0tKGTdFtq|5QnFByU?Ndr!4#rG zf49`%`eI$$4<9JYye)`(y2cy}nE_ z{g~rbl#OryQ7zroAzy`s=5v>t;kIwDuWGWnnOK@)(9A-w`Z7FKoV~`F;JMN~+l-tq zuY2+DKp~3gESg~r_mTcu>m}zqeti<>icLO<)7%Q`sGrp*nFx|`!Q< zp3qPopAPopfu7Bd)M-7lw+rD4`M73st@!`cT3*|`s(oR#zxRy@Oa6@Q{hkTS<>&&R zwhfi8Eo1iUl#hZ@E_FOm1&^ekMn1+(8Gjmu&jp} zeqnj6%weuwHSz>6^z-ta95<+D-da?b&QxzjZUp-t@R`k zTFFx12=-4SyNVFgnHcKPl{@wv$n(leOB0%Ikd3?U6(`W6Bz#}y8>knO!)UKk`eZ}A;?Jj zUx(mso2a3B+Oks2nCjU$i^;HP_slWppXxX}*c2+tEjxi-Scd#|>!`0QW3AmWihe48 zuz>p}F8Jg7{hKZ&H0rN}sEM|PUz(Rb)1B3XxRc|KEhTD9ngYS$9DAj4SAUgW(=c$8 zYc4y30lyP_M^0@U_5)5cD~uD@n^?Lo6>QclvelF_n~CLCATJT38Mu7DpVD4h__r3F zOpnZ_EG7df1dAM&uCRUj1JltsJNE-;)l_cAwdUG=+1I>cuFWNgrqik@TNoC59D~w~ z5KRN?51#M1aNy@}VW9N@2GwoEaZ1+5rXTm!bt5q5L)u;i{NeDEugq!s)vK6uVU_tp z%km@UE3M>@zExrAW9`b1zG7id#afONzB>GnZod=MuR3k5TD;fv=+|?8e0g!Ye_N$7 zAzWmJXWlwxMXmSJeMv~sSW+$)($zK+HD(Y7x~AvpeXVBod>Oh1qH=lA^8Pvo{Ni|u zli01oP8G7YOtXEt}7NkrT@=+xjjli)?3o!}-nZ z*=MQL09%g}^WC$-h1lQ=bKlGQ&yCnXHwMn`8g>oVmgof|gs98vGu((^13e>}%!uu`R9 z9h$VibpP*39sUaB6Ig$*v(#AeE74-sg3<@_sQTo8~ zN);0fM%qdYm>74+J~*qyTb7-YUxD>$tji;gXtJlVwRRK}f+ z$!c5i3%)ur(Y1p^Z|D>E%RNb&+;1ZtE`6*N^PJz?omh zvU63BDPAww5wH5&OfUEL{Jx~?216z5_bZLy=HlQb!`j~PWwP=IxCXjaL2#L1Wq2E`DSCkSGSX$yBR8H@mDYnUb|zOH4Tp8O>GwzEqP!(3-E-y zui4ia2f?g)lf_kC@e>wxfrU$SOa`uqS~q9@Z}X&$h3vWZra&8NALoz&E&@?|KJiNn7q5-yTZmj{<{D83XdCDR0CU_m*>7{yws#pIXJ=$!EK@!ijnOl4yfX{GE0S zuJ?+?WNQ(;UizQU`OgQ77hZOwh+G0^$iGX*L}%Nh6A#R+31`(nTbk%}dgR%XcuktY z+Wp}xnXoOfQmfTjw)+nT%qF`pCva3KTi_NdXX{}9wl7`v#Wb@!HP9dn6o6aK5q}c3 zo=yLg`}5Y=rDh!D|0^zImrn<>ymC85A$=^%U$oN*40M&hI(kID<++z}1FzK;wGWwS z4gc~@XG7rfUtCpBTiKh;tCuj>;Jle+*GuhJEotsrt0cSdZR~}cY=fn26U?#slGeXx z@BRGu0gJ$1TrWbnnH=74+8Y<0^5oc^Ep3!!*<8;ESc}cPyXZ=Eq&c2aLUHtqSbyLr-aZ|;ai_)%{1I04P9V41I58`+Q;$_0Qt5;BTdPI}3SC+ku6He7cBO3j zTz_=tN3K}i;IX}Td{K|+`@qtg&i}wd@3M-|**a)AyKAsB#3j^ry=fSZL}C3$4kPZftjX zSk;fTE6Y$*TQsx&{}6G%jl+pK*DKg&MLw8#@YQLxa$(1AsO)ao=6|@s4C!r^?)!>m z-oOJXTDea8YyPY+vV^~mVX;Di#c}%HHt9Z%wu$O!U|OX&eOZcTD2D}=Vd=o1Z&R@J zzLIzYJ8E@9uTRO(P`*PU)nWVN(lu`J;~z;Pf(u8Qxzv<)Y(xeAkD1daI2NDyWdHs$ z5i`1b=@tp8I%TOpf3ik>g9E+wlrzNXGUbjxmi^$uq5jyIp#-TFO5bT&rNaC5b%(B# zm=+#w!$Ps1ZWijV%U;U4P=Ef=UoCZ*KYM6biwz@5-0tll+ZaI$>3c?<#6zRX{@vrP#@v#4J2_))Oaztp4ukfY zAD?4}2>U(4UyS{_XMB{fz76#GV>7oH98AAxg?Z1sIx=-88i*}^RfQ$m>B9`RAAJ7e z?6Rly(^|o`_J3K1^|Cgyd&zpOWBXx}-=B#$P%TD^nLtJwJ~4~pPc8dpRY*clOtn)o zSd4+jLW{*Ye~ldV(gqATCDm5zENv1>T*x{kQUBV|By>(yTQ0Gw-IJKb;*;X!v7&O~ ztaehG=CxOX(?&2b?%SckPK!>Hrg!6)W+IO$fJS+88=# zdq^qn3IuW8s@AVE{@j{L&KVZO( zxD2}T^D?Ojx%KzZ?%SHb$BA7ZMYpYgTDcUYJ&r>)Wak!NZu>ChEMHjYODMWD9k8s) zNqZ@WIw^`Kzy4dkYUxS{YTeQDNBX<*#+-K}nJ$jJ z6>Q%5V`k;dS8opAIs|4RM&56BCelA5X7-q1pN{MN-pUO>>^g;k7A>^FN~>_lpTWoI z;(VO?BFSo*;l>U*Q$iqGCi#Tk9oK1#*ub!$Sf(2x3p$+D&P7YHji(dwo4 zmw9mMW*bub>jl%$o)p(2-S<<~Ayy#`y*6fY;BPaA7w-7}E0>EDV{WBeORGwGm`YPK zQg(N~oqclEv+8ioRm>2xZSrAyZ3R)&V6bsvobU57J2Zv3d3l>{O+!cQ#y~gdd+#4> zW$W5Z$ABGgW&X3JOzV#>aTf#4QaQh=(Y5>G+}Z@uX-7}_-wB>W@2AML=iOzQ{%~oN zdCWW3HmyG@tiU@qIIX`9?RqaAli$g-%$rjzZ90G5%thY|)+zGDvlm0+atHS%0{vU_ zJ$p4BU;DJhLK*k(-}}k9s8k;Zk}s-DMR!|H5-!!4->_2^tL|^v=w{g#bFov0v);I8 zMbopudSZ&zNv{N_1QY!E(ifSwWV?4DEL300Vn>4U7=}LR1h3GXcuYGMT$N-hQgguS ztyMM2xzi?w5EXLb^)^u_>fI0C_XqY_7g$UN22NY6pMj*mu=^wZ`EzdnfM+c*fA|`f zD%1;c11+#elE)4xi4|7v8CvhDjQ&h<>!$~sZT$Wul~T6uHq}*@-uRA?v?Tppv7+UF zY=5j(pp1bl@OkT#ktNyxt;l9#XwS&$S8BjQV+YBARI- zSjVu~S=K%inFfn&?_^@YJP;IZcQVl=bFFX;V{_B2VCzSxoYMA*o?{Cq)OjZ%YKt#;ldd2Kw(=$^~ zXJ&gd`!jh}_&5DS_EA21Z&`Mxi9fAj7VMMFx87O&QJK@s4~~X!a{Tz$jP37fcUdEs z&a6D&W?>Q24hv54;rT09+gkaJD}xd&f(7-m_Ik+`KUk-j$EWY?f@R}tt%elQtNW*R zrMudi0u@0I-@?TxuwJU;(mYiE9Z z^(rp3RJ6S7=$KaIq18t6GCgzVu9o&>LLLvcgH6gt7r2tS3jNzg51M*b{|38ixz0>n z|G4R&1a}bY)vB0pc)i^pBxJvq5G^!1l(}EM+>w)e5<-HF6|>J7umV2OH+WZhp8C9~c7< z1qNFGF^Rg`aI%aUP7<1gd`bFGT3_xH zCLt~GxSoU@vSE1=>dSQULVngU!Rds_ZzFetUfXyRNlb&4!S&*TyfJN79%_*~p)PH0 zX>L+;vx}QM+-x3Y>GCAZN^XLV=|j6{y<5I**oN<~CS`S%k5cLLA0^X#?3KZO&f_os zP%(L#YrVWo11UcXHud=lZ+HHn(kq$tEZui!Cf zi5Eq@mc2rsE>`UI_6a?3$FeYC#Wb1`yrShgb2sI^d_N6OFmN-PvrE1zxGbJ@^qW=_ zvs}-eU>BU^N<4!>YI3SrY;n$28`A6g?xwI3gd``V-q6u+`sQxdjkXJUZk1ihN4=v~ z2iFD#t5zu$b*yQkx#96~|D%!rv-`cP{v_;}t zc)Nub@vl;q))x795gFEEn`JA?m7$}RF3RLEbX#x?#J(7^L{49`#PDh6?1VW3H5d3^kZG|8?`O)xk`h{kV? zH$!R_SkV04gb;oMz8I58sxN~Bb<(wk-&T9++nfmoc?eO|I>#0bss4OeqFgI|X>S)} z5^3e)9=mh%tK^r@j!K9-&^8yN{DFIt^p-T%|5WHFmOGX`nN`*(mOYsV?ytfkcG@Sg zG_QAC!!3C19lIIpXUArjwJ*-#NW074lbdgs?JVxk=PSO8VP&c7`_t#FiT6ng`kxC^ zFJ!3|j`P>`Hrz7zVsAo2aqNJe8{5>ns4oEZojL7U{u}!=&Yv&mPrHK`&8pu9*9WoX zrc9f6b>~l5>MLd}0}obIWs9k53ICYT*0!$%{m^%B@OiYxE6ct%_Oo~13k&7JNm++X zAVk-J?!8B5jQsO&9(nOXmROdO^zk+f(qnM$pQlbHTksv@+cP*~4Kd)|#JzAbhwlqc z`p;zO?-w##;7L6k?8*#fe#iomLL5+k7yhX47zVl?Kb3C!wSvdrd@X38 zO8;hCF$fFaXDiBb!KuE_veo7+pJS8D&}L^XYk8b_Yg0Xu5xv!*R)P^b2Ne@ST~xbO zhK09F#yJx#dsR|fVjarjY_mQ}`=QulEhSlb5g(bq)q0jIt1Iz;ji32H`-4khU{rh2 zUD5j5o|^wzmqD9kZfJE?C8Xi~3-4!AA93Te{IG3*jv2{4vK3bDu(hwq4r{)O{!E#ZKNB3P z`=<3O*M02Y!G~l5D+*`k^^R+_2fOv5ig*L6r4_F9$g|YzkVn-tjPq(m4eI0S#U@6e zd4IF}B=&GEA1KGafx(RVUMJDGirLGVtQqTQ=NB)hOMN&QbE6v*-D711XewPKLF`xC+f0Vd&5%KBh~lpKxYiMg3er?9a7f`KXYI1+BzCWogspn7QVmurcbN*EDgnw$*) z9P*82G=B!7UbTPzZ_9*6&ydqgqNsUQLFtwm-5jk%ztDOXpz$AY;fcMu|)!%m8gIZ*XW_%U2Pqju&4f) zEpNi@UPQPJZ_3K5u-$7ypEtEVO&Lc;nsTk!Vzu!*=^?}a_}ipZq3&;&LIO!>&pr@4 zxVQd(bALow){EAlS>X0g-@VxCX5_KT4m2Z=1gn^@E(Y(=BUT@bX&v%{zLVD;0xQQO zmbE$efoCw#m2}LVUk7yG(?(BXYj?jif5B^gnsdcp${mhw3sZeK>fLWf9MqE#{H2h- zaR~lxM_9TvwxKy|5!`Fdsr&1etOZR`LJ5 z?Z-AwPVmVOJ$Q85HY3EWB4SE1?(@Nh?2|ie+D1fOtufecH^?FMpyh6fbHQ4*B>r{F z(iZ2gP2{LVvdh7FFK2wUyf-doOBcvh&mV_8{t1w*m+fRrwwH?&Hx(Iw_wnq;4Ps3< z^_nz@8+_y>gXL-EFA@5-wQEH&-^W@z9sjhM*zC81)%1r4x(~9WOr~f-*tBBzJI&&rjK`Dc6M?60q#{R(watn{DV90 zX_!Yn6cbkOnte-b?@^iGtYKR{sGG~ zaf1)v@IV8P?nHY>uI*ii2Gb2bM-q5G=-C*YEo(c6rRZP=KW2UqCEw7&l#0%@6YmI zp9u;)Q?$y)E6ycb(4K+KIRCLdeNgDH;7!|v*`GGKH*8;%gkE~xQg)yakN7href>yf zmROq(T+5%hW&ixls(n%i&fR6U7>z$L+R=fF$GKa*g|vy|%XSulI-QLeCx!#n^54I+EpC%h-vgb650N zI?4j)G#J6wB?gR(N%N@G)>A?l&z{MI*|C)RJh*Y7O2q{F`n=Pg@_Pecq zXHJR7TTwvUFTYt~PN-R2zb=B#ioCduuvKyv>%T2VFM&11F{-;Y>!sR8_Qg!#IdFb+D z?fdx)Jf!ht5v#ChThiTM?SIcBupo%GmfifB{^yKbm0z*@J*ZqT1y^&HW_5ca29}}e z{mexn!KG+aSnd-xqbE0cUfV3SeOQJyK4Dh|bDlhUq51#yb|qjnX5qhk>65i+QEpdd z9TY7xWSvpAA`J~uk|onnM%kC#LPE-t-Zq2`5fLVPvJ8?X%NYAIb}IWemjCaZ^Ig|c zGv@z1|G&rce0|?}&wI{!xAUHJ-t%6VLp?z~)jp+yhtcjSulW7yn7}`UCkkoN69X4c z)~BAQ|NptuDf?CbPLdDfmW6@)4*yvum;*H+L~{L%_od0h*M9qqfpYi$SHdI{Lr4Vc z>aky>0behRqG^g9{T~F_vX!LZOBah7eLJf42b`8Q7k0~2BI{5n;im%n3HfDmnq#+o zM<~p|WFTX!oi|(OXZ%rrI2MNTl&%*~C87UCcLgcRU>CFm*h zr?iH9-F)1Ckq6$P(y3IM)%^;i^VQZyHm1U4e`Zs@UZCY(zM%FKLHmEOm!BJl|2k8Y zMh-2`f3>XgAhr6c`Uk0_yvdS{=Z(q=?sAV^Z!@OfZ}x~&cW%nAyd#N46$M}7w_4w^ zqmeQu4*G*H&_&I7JehkjHE?C!eRH3e)76O~hbf>=61n4{DjS1}YXIF3N3%bZ{!~ow0W?Yjg^Z2B0W1%2J}y2* zr~+gReZP6})7qTWJg!oFn-uvQB5}BWw@TXM(jG*yqo5Ux_;9KhWJ99w}+_j=VZwT5{m9o!4@2b{8;30<5inKv~u5rE6* z75|!*=#DcMEkawAR0oR0jM}Z6Tutb%RH#kp@-)d>4zNj$s^x2H@Xo z$^(Fw+?J&gRp?Je6&Xdtcn_r9x!_|c`9)*lV6LJC?5l=u&z78OZci4HVz)lBs!i*? zRz`}(Dutdg_uLKIf?;f9mXbb+%;vfwWGwNHZHGwy#aev!MfG5Lm8! zB&k|p?4JeA0YOFZUPbL#M7Nc0R`h@|OUtz0R%9K6QH`WlF*qm%m7-%nB~kekLYP@G zMtsRl2WvZPRw4_9&$2;ccAMv8|S8?n-gK{rKk|3#@1UwZ}+N zf>iEoTjtA!=K$ui&HKpKT zsX{{c259B?vz-<->7VbwIg5!wBn4~$ zre&mFfX89FpN+>|^1_?4LVdE{ggu^y_+>eECO+g~>g2_Vjj(vv;&-c0X$!D(J`4~n z?9+ebE&J>>msu1I_K()1~(j-p!nn#>!)5W`i8gcz>+zMn+tH8S|y>{Oj?$N z*%nK7{uhgQ-;o3==2A%#I+jlrlO-n;FZ@6NW5=tib+(vxNW-bVn;rE`Mo9PtngakZ zI5!!B)3})+NKQtBJ97*7;(je$SV= za~vVE>~7gqx&{pJq5o!&r)0Vq9ZsjNixJTM4$&ZtOj$u|&lDK%Hsj*K1=KH9W6 zR~AcFh;o0w7$`o@-1Bj!obF42SY+(LK@GbmLYtRh0*RyJC@C!sk32}8hNn9&@C{!I z^e<_~QXt}Nwyh|Kqgk}1;-#RM%%xzmNdwGy@=imjjFSuPPD5cBon>F6se~m8fvBWY z{bl&DkbIUwVk)$zi_4&XW~~)`+h$^;lDj8twvH3aF&I=mErirY_%h|&%w5s$;1Ls$ zMGjsxZbL@PB}dD;fMAl{zRHE^FE-B$mL(fV9NN%;5Gclz9bn9052@xbIAZ>Bv#HAVt(xHCJC|@wxxF~Kp*)ASd( zf#Oh0{;2v=-@zjJS++T-ofGauh&l=)!z>JEt*3$~&Zt(|qf$Q$fK}-T)ISzlok{0afA1pUw{Wm+D1t zxxYFl3Qe*LhsdoU_dSsgv)aM*7EX~;*Sf<-e3fakAqRe6;zrmCZTY9}64qJh{s-`X zB_VU+w5U|y_EzZCK@W#S{nmB$-Vms_e2k}c)JpZxls>)5WfRm-?qbv?u*PIR4S0B*0 z^FG1}WPRip_JopsCUDMWkP5ay?IYHn$u%dQZdA8Y8zAHy(~6Xqvsxu7m48~kG0_qp zE>_lWqym z)>vFp=bs*{)-(fv?D)N+JOC)G`O(E4=+zL+pw$7GKE!{@a*vVNw&E#_FvQw^WVBQA zP`2}<0Xs3-b@da1JmQ6)eo({&cC~>;FheVP`%&gjI2i*0!ElXo>6PqKJ9C$=S6%4J zPE1du0Kf(|*{^)o_^?SVQxv;F2nE#m(UM#c3n@mL?n3xMoF5I^g~do0%GxD4waB*> zn`?)&+HD`wyNxy53ximI4_~e%Li`0sfWmyY@<*^oO#V>9yM+sXzdnFeacStLIrz^<|Bwy53U6CO+)i3X7e`kiV4&oif?L~xz zdYCZlbK>5n=G3izPY2K*C2Vbd>$~~}&pNv9*OmAUqsy#gQvkt2wA)wk3Ji@tJVXbP zfD$!I)Dt}wHz%Lb=t?#XBinr-{c#y0Iy9x>rRc6zbr6LkXdob3{53+X zKG{Ds~(Z>C-SVeRM56f~Pq8EeA-c&TS zGI_3ps~M7T0G((A05*{5@|i#RS9ck&185^xUD*0T-JjuRmtMmx~ zWGlNa4>qsxy|=0Z0GV~fHp+wv6nhA(u&1=@5OhyDi7p<3{%@v6hj9s~dN^G@48k=J z7hHK`ru1GtXn%;whl^ufP{LGk*Rs8i8NJs$)s+kjr>6O+8v_U~lk>gT4GuonR|l~S zB}|VTN*oGo^Rgp#CELSkGU}?%$aM$bo^Z5MTFOW5-lMF1sPTIMu$!`nlIqy(e{!wA z4n-X#B)3LMiD#dmdQL0R)0KQbmEHll(r+r6AHj}_lZG@$VDG&&f|^6qr~T-e6suvL zo+G!412uH)2xcHdpX=HsA1YQLQ zHVcJUJ~v>D`GGDR2bY4aC}Glezulgt4a2vIToZh0eqb7f9L3mAPoqDMVnjEn=oDtr zC#b;Abbf18zJHzbbMAA2VtH$DslM$o092kX1R=vB{?f!BTe<;&tsx-4GicN?$xUfB zowgp6T)uu^sP%EaFVz1yROgF*AqzzzVN-}D588Je)1rTr=$~HX?$w7ng|h2M@{Rl; zl(6~VEZ(O|*v!$`IOAR)$P^kywkI&HM?}%Y6PRgZqG&yyqA}(vA=9%NRWFp(XslVG zH0&Q*v70HT)^zXmHAzSM4Mr=DVhPo1CKVJykN8%V1$O5WPU1~Q-t460rCb$Fktfma z9NKacn+%Dh2F2NSUdvPXnoq+|L8M$`DD4zhtpj6(SA2R*^S~*4vWhsJ;l?7gU>mj3AE2tpe}Np$YySW> zMdltyq%DlQE7`L+Jlsa?auz~RA&w@qr)eB*U{9AgYEuj}H|gzJ+;!>{Crp8<(Z(GU z<3bX7AJHOyVG$*4eAZ*TZ3s2#wg)9Ft_z--*sxLg%K+Zzt~IG)Ee$8}e$je&+A7fbD+41;l^7Cp7Jx6k)kIUQ7tVjcN~C z6FvKpq~0tKH(jNd{4)IukUXxVuPGV1L- z4;v{zsrWpcKh6#_EASTCr8xBT_NP_dP8?!pjExCh-bmE$0$5Npi9+!N3szo`QWUS~ zyNg)C2!>!&J?7tNfm0e&FJX6Yp-wG;nCH=e3yZFJtTiqjM89$p$kr z@wj1lv)6iJ^0RKZBVVO9}%~yWjJqAPH~H$ z(Z0&@@W(+KjyKEC2Z@wm?0C!G&fa1LrCo)ZZlU>qG2^*{I$TGa?N(4RK0Eub5YC9t z#PnUyOfRq3QpZUz93^ZP?RjqE+q3)Jg}ZeCDJ$rg zYiLu9l^$NlWs)_t;u@}W?4-Nbq}HGHrp5krvCsA*)$m%>*8I5w6;{#qH&Mc_Q0AMx zHmzj%=!~`zXu(Q~#Yj}@mEz#gaP7OyG0FAdFfjfihp61ok9yHk{4F!VLK(Ay2K@D* zpZaXF5M;tteehVzEHMBv7yz}9a zS?Aeh=FK;uZG7ZBpO(mzg%e9Af2y*Yw6iZH&s%8n5n0`aoL0YNZql;bn6Y}Vqd#t=YiFq9 z9aujfYI_IA&fV4v?O%U!#{-)%+gcczY}^E585K^IF}Je0{4Y)cHC4_65~(z}^5dEAcMJXOaDV=#tzkF=sj{JH^rdyRgpa*vq?8MDYRoP)qCKR(;f1^2l5(h5N2`7*qwGkC-rd>2|n?C=ugP2 zte8oM?n$oow1~?lx9B_XU4(@f&wgN` z$^syp0Rk`Yo0Xlt#u06>O-vYyziDX^WLbAzN2NY3^2qKZZjkXm_>Pj0PN!-Hsd_q@?r4HrU3t5*)_DBNdRRSkH zj7H&6Jq%$~%u@cka-!z!?m)W$G-G zpYYDs?L})1YK#iL`S&Dx#E`zA>JRzD0S}uK<3*(l-jmY%thKhdfOkfQh??Hj-kUVP ze49ow=xa0|k6J}d1tYHAKM)W+)BK?3i5Nj%Mo#`i>8`>wV8n=#cB6*|1UKVDHuzM~&ncQx zVbamY9ZE&j%JvhpcZ{&R6#{ok%2KM#N8mW!>al*2CDx7Z!fLj delta 102659 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

`$bsu&|}^mp=J!onJ!zzRe!ZJL){# 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 YX9pWbbf;NS25O2MY-pA8X|$pGzek2KY5)KL diff --git a/package.json b/package.json index 7620c4a1..d8c9196f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "eas:registerSchemas": "npx tsx src/lib/eas/registerSchemas" }, "dependencies": { + "@allo-team/allo-v2-sdk": "^1.0.39", "@ethereum-attestation-service/eas-sdk": "^1.4.0", "@hookform/resolvers": "^3.3.4", "@next-auth/prisma-adapter": "^1.0.7", @@ -59,6 +60,7 @@ "siwe": "^2.1.4", "sonner": "^1.4.0", "superjson": "^2.2.1", + "tailwind-merge": "^2.2.1", "tailwind-variants": "^0.1.20", "viem": "^1.20.3", "wagmi": "^1.4.12", diff --git a/src/components/ui/Form.tsx b/src/components/ui/Form.tsx index f3adcc97..ae360f89 100644 --- a/src/components/ui/Form.tsx +++ b/src/components/ui/Form.tsx @@ -5,16 +5,16 @@ import { type PropsWithChildren, type ReactElement, type ComponentPropsWithoutRef, + type ReactNode, forwardRef, cloneElement, useEffect, - ReactNode, } from "react"; import { FormProvider, useForm, useFormContext, - UseFormReturn, + type UseFormReturn, type UseFormProps, type FieldValues, useFieldArray, @@ -22,8 +22,7 @@ import { import { zodResolver } from "@hookform/resolvers/zod"; import { createComponent } from "."; -// import { Search } from "../icons"; -import clsx from "clsx"; +import { cn } from "~/utils/classNames"; import { useInterval, useLocalStorage } from "react-use"; import { IconButton } from "./Button"; import { PlusIcon, Search, Trash } from "lucide-react"; @@ -149,7 +148,7 @@ export const FormControl = ({ ) as unknown as { message: string }; return ( -
+
{label && (
); diff --git a/src/features/distribute/components/CreatePool.tsx b/src/features/distribute/components/CreatePool.tsx index 3a8eae7d..ce03667f 100644 --- a/src/features/distribute/components/CreatePool.tsx +++ b/src/features/distribute/components/CreatePool.tsx @@ -1,3 +1,10 @@ +import { z } from "zod"; +import dynamic from "next/dynamic"; +import { formatUnits, parseUnits } from "viem"; +import { useAccount } from "wagmi"; +import { useSession } from "next-auth/react"; +import { type PropsWithChildren } from "react"; + import { Button, IconButton } from "~/components/ui/Button"; import { Alert } from "~/components/ui/Alert"; @@ -6,7 +13,6 @@ import { Spinner } from "~/components/ui/Spinner"; import { useAlloIsMemberOfProfile, useAlloProfile, - useAlloRegistryAddMember, useCreateAlloProfile, } from "../hooks/useAlloProfile"; import { @@ -20,16 +26,10 @@ import { useTokenBalance, } from "../hooks/useAlloPool"; import { allo, config, isNativeToken } from "~/config"; -import { formatUnits, parseUnits } from "viem"; import { ErrorMessage, Form, FormControl, Input } from "~/components/ui/Form"; -import { z } from "zod"; -import dynamic from "next/dynamic"; import { AllocationInput } from "~/features/ballot/components/AllocationInput"; import { useFormContext } from "react-hook-form"; import { MintButton } from "./MintButton"; -import { useAccount } from "wagmi"; -import { useSession } from "next-auth/react"; -import { type PropsWithChildren } from "react"; function CheckAlloProfile(props: PropsWithChildren) { const { isCorrectNetwork, correctNetwork } = useIsCorrectNetwork(); @@ -37,7 +37,6 @@ function CheckAlloProfile(props: PropsWithChildren) { const createProfile = useCreateAlloProfile(); const isMember = useAlloIsMemberOfProfile(); - const addMember = useAlloRegistryAddMember(); console.log("create profile,", createProfile.data); if (!isCorrectNetwork) { @@ -79,26 +78,6 @@ function CheckAlloProfile(props: PropsWithChildren) { ); } - if (!isMember.data) { - return ( - -

- You must add the Strategy contract as a member to your Allo2 profile - before you can create a pool. -

- addMember.mutate()} - disabled={addMember.isLoading} - > - {addMember.isLoading ? <>Updating... : <>Update membership} - -
- ); - } - return props.children; } @@ -107,7 +86,7 @@ function CreatePool() { const profile = useAlloProfile(); const approve = useApprove(); const balance = useTokenBalance(); - const poolId = usePoolId(); + const { data: poolId } = usePoolId(); const allowance = useTokenAllowance(); const token = usePoolToken(); @@ -116,24 +95,8 @@ function CreatePool() { const profileId = profile.data?.id as unknown as `0x${string}`; - console.log(poolId.data); - if (poolId.data) { - return ; - } - - console.log(createPool.data); - const createdPoolId = createPool.data; - if (createPool.data) { - return ( - - Add this to your environment variables and deploy again. -
-
- NEXT_PUBLIC_ALLO2_CUSTOM_STRATEGY="{createPool.data}" -
-
-
- ); + if (poolId) { + return ; } return ( @@ -160,9 +123,7 @@ function CreatePool() { onSubmit={(values) => { console.log(values); - // TODO: If token is ERC20 - approve spending const amount = parseUnits(values.amount.toString(), decimals); - console.log({ amount }); const hasAllowance = isNativeToken ? true : (allowance.data ?? 0) >= amount; @@ -170,24 +131,22 @@ function CreatePool() { if (!hasAllowance) { return approve.write({ args: [allo.alloAddress, amount], - // args: [allo.strategyAddress, amount], }); } - console.log({ profileId }); - // return createPool.mutate({ profileId, initialFunding: amount }); - createPool.write({ - args: [profileId, allo.tokenAddress, amount, config.admins], - }); + return createPool.mutate({ profileId, initialFunding: amount }); }} > - {/* - */} + + +
+ +
{ + onSubmit={async (values, form) => { const amount = parseUnits(values.amount.toString(), decimals); console.log({ amount }); const hasAllowance = calcHasAllowance({ @@ -244,27 +203,21 @@ function PoolDetails({ poolId = 0 }) { allowance, decimals, }); - console.log("FUND pool", { hasAllowance }); if (!hasAllowance) { return approve.write({ args: [allo.alloAddress, amount], }); } - fundPool.mutate({ poolId, amount }); + fundPool.mutate( + { poolId, amount }, + { onSuccess: () => form.reset({ amount: 0 }) }, + ); }} > -
-
Fund pool
-
- Wallet balance: - - {balance.data?.formatted.slice(0, 5)} - -
-
-
+
+
+
Wallet balance
+
+ {balance.data?.formatted.slice(0, 5)} {balance.data?.symbol} +
+
+ ); +} + function FundPoolButton({ buttonText = "", isLoading = false, @@ -296,8 +261,8 @@ function FundPoolButton({ const amount = BigInt(watch("amount") || 0); const hasAllowance = calcHasAllowance({ amount, allowance, decimals }); - console.log({ hasAllowance }); - const disabled = isLoading || Boolean(formState.errors.amount); + console.log({ hasAllowance, amount }); + const disabled = !amount || isLoading || Boolean(formState.errors.amount); if (!address || !session) { return ( diff --git a/src/features/distribute/components/Distributions.tsx b/src/features/distribute/components/Distributions.tsx index aa774cee..bf69253a 100644 --- a/src/features/distribute/components/Distributions.tsx +++ b/src/features/distribute/components/Distributions.tsx @@ -1,5 +1,5 @@ import { FileDown } from "lucide-react"; -import { useCallback, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { z } from "zod"; import { EmptyState } from "~/components/EmptyState"; import { Button, IconButton } from "~/components/ui/Button"; @@ -16,6 +16,9 @@ import { import { useProjectsById } from "~/features/projects/hooks/useProjects"; import { api } from "~/utils/api"; import { format } from "~/utils/csv"; +import { usePoolAmount, usePoolToken } from "../hooks/useAlloPool"; +import { Address, formatUnits, parseUnits } from "viem"; +import { cn } from "~/utils/classNames"; export function Distributions() { const [confirmDistribution, setConfirmDistribution] = useState< @@ -109,8 +112,30 @@ function ConfirmDistributionDialog({ distribution: Distribution[]; onOpenChange: () => void; }) { + const { data: token } = usePoolToken(); + const { data: balance } = usePoolAmount(); + const { isLoading, mutate } = useDistribute(); + const { recipients, amounts } = useMemo(() => { + return distribution.reduce( + (acc, x) => ({ + recipients: acc.recipients.concat(x.payoutAddress as Address), + amounts: acc.amounts.concat( + parseUnits(String(x.amount), token.decimals), + ), + }), + { recipients: [], amounts: [] } as { + recipients: Address[]; + amounts: bigint[]; + }, + ); + }, [distribution]); + + console.log("balance", balance, amounts); + const amountDiff = (balance ?? 0n) - amounts.reduce((sum, x) => sum + x, 0n); + + console.log("amountDiff", amountDiff); return ( 0} @@ -122,13 +147,31 @@ function ConfirmDistributionDialog({ This will distribute the pools funds to the payout addresses according to the table. + +
+

+
Pool balance after distribution
+

+
+ {formatUnits(amountDiff, token.decimals)} +
+
alert("not implemented yet")} + onClick={() => + mutate?.( + { recipients, amounts }, + { onSuccess: () => onOpenChange() }, + ) + } > {isLoading ? "Confirming..." : "Confirm"} diff --git a/src/features/distribute/hooks/useAllo.ts b/src/features/distribute/hooks/useAllo.ts index 711eb81d..752f341b 100644 --- a/src/features/distribute/hooks/useAllo.ts +++ b/src/features/distribute/hooks/useAllo.ts @@ -3,6 +3,7 @@ import { Allo, Registry } from "@allo-team/allo-v2-sdk/"; import { decodeEventLog, type Address, type Chain } from "viem"; import { useMemo } from "react"; +import { type JsonFragment } from "ethers"; const createAlloOpts = (chain: Chain) => ({ chain: chain.id, @@ -19,7 +20,7 @@ export function useAlloRegistry() { export async function waitForLogs( hash: Address, - abi: unknown[], + abi: readonly JsonFragment[], client: PublicClient, ) { return client.waitForTransactionReceipt({ hash }).then(({ logs }) => { diff --git a/src/features/distribute/hooks/useAlloPool.ts b/src/features/distribute/hooks/useAlloPool.ts index f45f67b6..45fd7baa 100644 --- a/src/features/distribute/hooks/useAlloPool.ts +++ b/src/features/distribute/hooks/useAlloPool.ts @@ -7,74 +7,39 @@ import { usePublicClient, useSendTransaction, useToken, - useWaitForTransaction, } from "wagmi"; import { abi as AlloABI } from "@allo-team/allo-v2-sdk/dist/Allo/allo.config"; import { allo, config, isNativeToken, nativeToken } from "~/config"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useAllo, waitForLogs } from "./useAllo"; import { type Address, parseAbi } from "viem"; -import { ethers } from "ethers"; -import { type Fragment } from "ethers/lib/utils"; export function usePoolId() { - return useContractRead({ - address: allo.customStrategy, - abi: parseAbi(["function getPoolId() external view returns (uint256)"]), - functionName: "getPoolId", - }); + // TODO: Store in config database + return { data: 28 }; } +export function usePool(poolId: number) { + const allo = useAllo(); + + return useQuery(["pool", poolId], async () => allo?.getPool(BigInt(poolId)), { + enabled: Boolean(allo && poolId), + }); +} export function usePoolAmount() { + const { data: poolId } = usePoolId(); + const { data: pool } = usePool(poolId); + return useContractRead({ - address: allo.customStrategy, + address: pool?.strategy as Address, abi: parseAbi(["function getPoolAmount() external view returns (uint256)"]), functionName: "getPoolAmount", watch: true, + enabled: Boolean(pool?.strategy), }); } -function useWaitForEvent(event: string, abi: unknown, hash?: Address) { - const tx = useWaitForTransaction({ hash, enabled: Boolean(hash) }); - const iface = new ethers.utils.Interface(abi as Fragment[]); - - const data = tx.data?.logs - .map((log) => { - try { - return iface.parseLog(log); - } catch (error) { - return null; - } - }) - .find((log) => log?.name === event) as { args: T } | undefined; - - return { - ...tx, - data, - }; -} - export function useCreatePool() { - const create = useContractWrite({ - address: allo.strategyAddress, - abi: parseAbi([ - "function createPool(bytes32 _profileId, address _token,uint256 _amount, address[] memory _managers) public", - ]), - functionName: "createPool", - }); - - const event = useWaitForEvent<{ strategy: string }>( - "PoolCreated", - AlloABI, - create.data?.hash, - ); - - return { - ...create, - isLoading: create.isLoading || event.isLoading, - data: event.data?.args?.strategy, - }; - const alloSDK = useAllo(); const { sendTransactionAsync } = useSendTransaction(); const client = usePublicClient(); @@ -82,14 +47,14 @@ export function useCreatePool() { async (params: { profileId: string; initialFunding?: bigint }) => { if (!alloSDK) throw new Error("Allo not initialized"); - const tx = alloSDK.createPoolWithCustomStrategy({ - profileId: params.profileId, + const tx = alloSDK.createPool({ + profileId: params.profileId as Address, strategy: allo.strategyAddress, token: allo.tokenAddress, managers: config.admins, amount: params.initialFunding ?? 0n, metadata: { protocol: 1n, pointer: "" }, - initStrategyData: "", + initStrategyData: "0x", }); const value = BigInt(tx.value); const { hash } = await sendTransactionAsync({ ...tx, value }); @@ -110,8 +75,12 @@ export function useFundPool() { if (!allo) throw new Error("Allo not initialized"); console.log("fund pool, ,", poolId, amount); - const { to, value } = allo.fundPool(poolId, Number(amount)); - const { hash } = await sendTransactionAsync({ to, value: BigInt(value) }); + const { to, data, value } = allo.fundPool(BigInt(poolId), amount); + const { hash } = await sendTransactionAsync({ + to, + data, + value: BigInt(value), + }); return waitForLogs(hash, AlloABI, client).then(async (logs) => { await queryClient.invalidateQueries(["allo/registry/member"]); diff --git a/src/features/distribute/hooks/useAlloProfile.ts b/src/features/distribute/hooks/useAlloProfile.ts index 8c5be89b..5b5ed345 100644 --- a/src/features/distribute/hooks/useAlloProfile.ts +++ b/src/features/distribute/hooks/useAlloProfile.ts @@ -48,30 +48,6 @@ export function useCreateAlloProfile() { }); }); } -export function useAlloRegistryAddMember() { - const registry = useAlloRegistry(); - const { address } = useAccount(); - const client = usePublicClient(); - const queryClient = useQueryClient(); - const { sendTransactionAsync } = useSendTransaction(); - return useMutation(async () => { - if (!address) throw new Error("Connect wallet first"); - if (!registry) throw new Error("Allo Registry not initialized"); - - const profileId = getProfileId(address); - const { to, data } = registry.addMembers({ - profileId, - members: [allo.strategyAddress], - }); - - const { hash } = await sendTransactionAsync({ to, data }); - return waitForLogs(hash, RegistryABI, client).then(async (logs) => { - await queryClient.invalidateQueries(["allo/registry/member"]); - return logs; - }); - }); -} - export function useAlloIsMemberOfProfile() { const registry = useAlloRegistry(); const { address } = useAccount(); diff --git a/src/features/distribute/hooks/useDistribute.ts b/src/features/distribute/hooks/useDistribute.ts index 01f41776..199be055 100644 --- a/src/features/distribute/hooks/useDistribute.ts +++ b/src/features/distribute/hooks/useDistribute.ts @@ -1,36 +1,36 @@ import { useMutation } from "@tanstack/react-query"; -import { type Distribution } from "../types"; import { useAllo } from "./useAllo"; -import { usePoolId } from "./useAlloPool"; -import { encodeAbiParameters, parseAbiParameters } from "viem"; +import { usePoolId, usePoolToken } from "./useAlloPool"; +import { type Address, encodeAbiParameters, parseAbiParameters } from "viem"; import { useSendTransaction } from "wagmi"; export function useDistribute() { const allo = useAllo(); + const { data: token } = usePoolToken(); const { data: poolId } = usePoolId(); - const { sendTransactionAsync } = useSendTransaction(); - return useMutation(async (votes: Distribution[]) => { - if (!allo) throw new Error("Allo not initialized"); - - const { recipients, amounts } = votes.reduce( - (acc, x) => ({ - recipients: acc.recipients.concat(x.payoutAddress), - amounts: acc.amounts.concat(BigInt(x.amount)), - }), - { recipients: [], amounts: [] } as { - recipients: string[]; - amounts: bigint[]; - }, - ); - const { to, data } = allo.distribute( - Number(poolId), + const { sendTransactionAsync } = useSendTransaction(); + return useMutation( + async ({ recipients, - encodeAmounts(amounts), - ); + amounts, + }: { + recipients: Address[]; + amounts: bigint[]; + }) => { + if (!allo) throw new Error("Allo not initialized"); + if (!token) throw new Error("Token not initialized"); + + console.log({ recipients, amounts }); + const { to, data } = allo.distribute( + BigInt(poolId), + recipients, + encodeAmounts(amounts), + ); - return sendTransactionAsync({ to, data }); - }); + return sendTransactionAsync({ to, data }); + }, + ); } function encodeAmounts(amounts: bigint[]) { diff --git a/src/features/distribute/types/index.ts b/src/features/distribute/types/index.ts index 294ced20..dad82f86 100644 --- a/src/features/distribute/types/index.ts +++ b/src/features/distribute/types/index.ts @@ -1,9 +1,15 @@ +import { type Address, isAddress } from "viem"; import { z } from "zod"; +const EthAddressSchema = z.custom( + (val) => isAddress(val as Address), + "Invalid address", +); + export const DistributionSchema = z.object({ projectId: z.string(), amount: z.number(), - payoutAddress: z.string(), + payoutAddress: EthAddressSchema, }); export const CalculationSchema = z.object({ @@ -19,3 +25,4 @@ export const SettingsSchema = z.object({ export type Distribution = z.infer; export type Settings = z.infer; +export type Calculation = z.infer; diff --git a/src/features/projects/components/ProjectItem.tsx b/src/features/projects/components/ProjectItem.tsx index 5e603bff..76ab2ed1 100644 --- a/src/features/projects/components/ProjectItem.tsx +++ b/src/features/projects/components/ProjectItem.tsx @@ -49,7 +49,7 @@ export function ProjectItem({ export function ProjectItemAwarded({ amount = 0 }) { return ( -
+
{formatNumber(amount)}{" "} {config.tokenName}
diff --git a/src/pages/distribute/index.tsx b/src/pages/distribute/index.tsx index 630e88d4..95310749 100644 --- a/src/pages/distribute/index.tsx +++ b/src/pages/distribute/index.tsx @@ -1,5 +1,5 @@ -import { useCallback, useState } from "react"; -import { z } from "zod"; +import { useFormContext } from "react-hook-form"; +import { Alert } from "~/components/ui/Alert"; import { Button } from "~/components/ui/Button"; import { Form, FormControl, Input, Label, Select } from "~/components/ui/Form"; import { Skeleton } from "~/components/ui/Skeleton"; @@ -7,7 +7,10 @@ import { Spinner } from "~/components/ui/Spinner"; import { config } from "~/config"; import ConfigurePool from "~/features/distribute/components/CreatePool"; import { Distributions } from "~/features/distribute/components/Distributions"; -import { CalculationSchema } from "~/features/distribute/types"; +import { + type Calculation, + CalculationSchema, +} from "~/features/distribute/types"; import { Layout } from "~/layouts/DefaultLayout"; import { api } from "~/utils/api"; @@ -29,12 +32,12 @@ export default function DistributePage() { const calculation = settings.data?.config?.calculation; return ( - }> - {new Date() < config.reviewEndsAt ? ( -
Voting hasn't started yet
- ) : ( -
-
+ + + {settings.isLoading ? (
@@ -43,39 +46,36 @@ export default function DistributePage() { defaultValues={calculation} schema={CalculationSchema} onSubmit={(values) => { - console.log("values", values); setConfig.mutate({ config: { calculation: values } }); }} > -
+
- - + -
+ Update calculation +
)} -
+
+
+ } + > + {new Date() < config.reviewEndsAt ? ( +
Voting hasn't started yet
+ ) : ( +
{setConfig.isLoading ? (
@@ -89,14 +89,32 @@ export default function DistributePage() { ); } +function MinimumQuorum() { + const { watch } = useFormContext(); + const style = watch("style"); + + return ( + + + + ); +} + function VoterCount() { const voters = api.voters.list.useQuery({ limit: 1000 }); const votes = api.results.votes.useQuery(); return ( -
- -
+
+

+ Ballots submitted +

+
Date: Mon, 11 Mar 2024 16:24:03 +0100 Subject: [PATCH 23/37] Store PoolId in db --- prisma/schema.prisma | 1 + .../distribute/components/CreatePool.tsx | 20 +++++++++---- .../distribute/components/Distributions.tsx | 5 ++-- src/features/distribute/hooks/useAllo.ts | 1 - src/features/distribute/hooks/useAlloPool.ts | 28 ++++++++++++++++--- src/features/distribute/types/index.ts | 1 + src/layouts/AdminLayout.tsx | 22 +++++++++++++++ src/pages/distribute/index.tsx | 18 ++++++------ src/server/api/routers/config.ts | 11 ++++++++ 9 files changed, 85 insertions(+), 22 deletions(-) create mode 100644 src/layouts/AdminLayout.tsx diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7b1b6ae1..fbb31f23 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -72,5 +72,6 @@ model Ballot { model Settings { id String @id @default(cuid()) + poolId Int? config Json } diff --git a/src/features/distribute/components/CreatePool.tsx b/src/features/distribute/components/CreatePool.tsx index ce03667f..4e5c9589 100644 --- a/src/features/distribute/components/CreatePool.tsx +++ b/src/features/distribute/components/CreatePool.tsx @@ -25,11 +25,12 @@ import { useTokenAllowance, useTokenBalance, } from "../hooks/useAlloPool"; -import { allo, config, isNativeToken } from "~/config"; +import { allo, isNativeToken } from "~/config"; import { ErrorMessage, Form, FormControl, Input } from "~/components/ui/Form"; import { AllocationInput } from "~/features/ballot/components/AllocationInput"; import { useFormContext } from "react-hook-form"; import { MintButton } from "./MintButton"; +import { api } from "~/utils/api"; function CheckAlloProfile(props: PropsWithChildren) { const { isCorrectNetwork, correctNetwork } = useIsCorrectNetwork(); @@ -86,7 +87,7 @@ function CreatePool() { const profile = useAlloProfile(); const approve = useApprove(); const balance = useTokenBalance(); - const { data: poolId } = usePoolId(); + const poolId = usePoolId(); const allowance = useTokenAllowance(); const token = usePoolToken(); @@ -95,8 +96,16 @@ function CreatePool() { const profileId = profile.data?.id as unknown as `0x${string}`; - if (poolId) { - return ; + if (poolId.isLoading) { + return ( + + + + ); + } + + if (poolId.data) { + return ; } return ( @@ -261,8 +270,7 @@ function FundPoolButton({ const amount = BigInt(watch("amount") || 0); const hasAllowance = calcHasAllowance({ amount, allowance, decimals }); - console.log({ hasAllowance, amount }); - const disabled = !amount || isLoading || Boolean(formState.errors.amount); + const disabled = isLoading || Boolean(formState.errors.amount); if (!address || !session) { return ( diff --git a/src/features/distribute/components/Distributions.tsx b/src/features/distribute/components/Distributions.tsx index bf69253a..39289f06 100644 --- a/src/features/distribute/components/Distributions.tsx +++ b/src/features/distribute/components/Distributions.tsx @@ -19,6 +19,7 @@ import { format } from "~/utils/csv"; import { usePoolAmount, usePoolToken } from "../hooks/useAlloPool"; import { Address, formatUnits, parseUnits } from "viem"; import { cn } from "~/utils/classNames"; +import { formatNumber } from "~/utils/formatNumber"; export function Distributions() { const [confirmDistribution, setConfirmDistribution] = useState< @@ -132,10 +133,8 @@ function ConfirmDistributionDialog({ ); }, [distribution]); - console.log("balance", balance, amounts); const amountDiff = (balance ?? 0n) - amounts.reduce((sum, x) => sum + x, 0n); - console.log("amountDiff", amountDiff); return ( 0} @@ -157,7 +156,7 @@ function ConfirmDistributionDialog({ ["text-red-600"]: amountDiff < 0n, })} > - {formatUnits(amountDiff, token.decimals)} + {formatNumber(formatUnits(amountDiff, token.decimals))}
diff --git a/src/features/distribute/hooks/useAllo.ts b/src/features/distribute/hooks/useAllo.ts index 752f341b..46485202 100644 --- a/src/features/distribute/hooks/useAllo.ts +++ b/src/features/distribute/hooks/useAllo.ts @@ -24,7 +24,6 @@ export async function waitForLogs( client: PublicClient, ) { return client.waitForTransactionReceipt({ hash }).then(({ logs }) => { - console.log("logs", logs); return logs .map(({ data, topics }) => { try { diff --git a/src/features/distribute/hooks/useAlloPool.ts b/src/features/distribute/hooks/useAlloPool.ts index 45fd7baa..87785526 100644 --- a/src/features/distribute/hooks/useAlloPool.ts +++ b/src/features/distribute/hooks/useAlloPool.ts @@ -13,13 +13,17 @@ import { allo, config, isNativeToken, nativeToken } from "~/config"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useAllo, waitForLogs } from "./useAllo"; import { type Address, parseAbi } from "viem"; +import { api } from "~/utils/api"; export function usePoolId() { - // TODO: Store in config database - return { data: 28 }; + const config = api.config.get.useQuery(); + return { + ...config, + data: config.data?.poolId, + }; } -export function usePool(poolId: number) { +export function usePool(poolId?: number) { const allo = useAllo(); return useQuery(["pool", poolId], async () => allo?.getPool(BigInt(poolId)), { @@ -41,8 +45,10 @@ export function usePoolAmount() { export function useCreatePool() { const alloSDK = useAllo(); + const setPool = api.config.setPoolId.useMutation(); const { sendTransactionAsync } = useSendTransaction(); const client = usePublicClient(); + const utils = api.useUtils(); return useMutation( async (params: { profileId: string; initialFunding?: bigint }) => { if (!alloSDK) throw new Error("Allo not initialized"); @@ -59,7 +65,21 @@ export function useCreatePool() { const value = BigInt(tx.value); const { hash } = await sendTransactionAsync({ ...tx, value }); - return waitForLogs(hash, AlloABI, client); + return waitForLogs(hash, AlloABI, client).then((logs) => { + const { poolId } = (logs.find((log) => log?.eventName === "PoolCreated") + ?.args ?? {}) as { poolId?: bigint }; + + if (poolId) { + setPool.mutate( + { poolId: Number(poolId) }, + { + onSuccess() { + utils.config.get.invalidate().catch(console.log); + }, + }, + ); + } + }); }, ); } diff --git a/src/features/distribute/types/index.ts b/src/features/distribute/types/index.ts index dad82f86..663506fe 100644 --- a/src/features/distribute/types/index.ts +++ b/src/features/distribute/types/index.ts @@ -18,6 +18,7 @@ export const CalculationSchema = z.object({ }); export const SettingsSchema = z.object({ id: z.string().optional(), + poolId: z.number().optional(), config: z.object({ calculation: CalculationSchema, }), diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx new file mode 100644 index 00000000..5ade68bc --- /dev/null +++ b/src/layouts/AdminLayout.tsx @@ -0,0 +1,22 @@ +import type { ReactNode, PropsWithChildren } from "react"; +import { useAccount } from "wagmi"; + +import { type LayoutProps } from "./BaseLayout"; +import { config } from "~/config"; +import { Layout } from "./DefaultLayout"; + +type Props = PropsWithChildren< + { + sidebar?: "left" | "right"; + sidebarComponent?: ReactNode; + } & LayoutProps +>; +export const AdminLayout = ({ children, ...props }: Props) => { + const { address } = useAccount(); + + if (config.admins.includes(address!)) { + return
Only admins can access this page
; + } + + return {children}; +}; diff --git a/src/pages/distribute/index.tsx b/src/pages/distribute/index.tsx index 95310749..834e3761 100644 --- a/src/pages/distribute/index.tsx +++ b/src/pages/distribute/index.tsx @@ -37,11 +37,13 @@ export default function DistributePage() { sidebarComponent={
- - - {settings.isLoading ? ( -
- ) : ( + {settings.isLoading ? ( +
+ ) : ( + + +
+
- )} -
+ + )}
} > @@ -116,7 +118,7 @@ function VoterCount() {

{votes.data?.totalVoters} / {voters.data?.length} diff --git a/src/server/api/routers/config.ts b/src/server/api/routers/config.ts index 07c01d4f..57e416b9 100644 --- a/src/server/api/routers/config.ts +++ b/src/server/api/routers/config.ts @@ -26,4 +26,15 @@ export const configRouter = createTRPCRouter({ }) : ctx.db.settings.create({ data: { config } }); }), + setPoolId: adminProcedure + .input(z.object({ poolId: z.number() })) + .mutation(async ({ input: { poolId }, ctx }) => { + const existing = await getSettings(ctx.db); + return existing + ? ctx.db.settings.update({ + where: { id: existing.id }, + data: { poolId }, + }) + : ctx.db.settings.create({ data: { poolId } }); + }), }); From 2a5d8c78b8279894a14f6dc1ead5846ef554f04e Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Mon, 11 Mar 2024 16:24:17 +0100 Subject: [PATCH 24/37] Remove unused file --- src/lib/eas/createApplications.ts | 274 ------------------------------ 1 file changed, 274 deletions(-) delete mode 100644 src/lib/eas/createApplications.ts diff --git a/src/lib/eas/createApplications.ts b/src/lib/eas/createApplications.ts deleted file mode 100644 index 9234daca..00000000 --- a/src/lib/eas/createApplications.ts +++ /dev/null @@ -1,274 +0,0 @@ -import "dotenv/config"; -import { config, eas } from "~/config"; -import { type MultiAttestationRequest } from "@ethereum-attestation-service/eas-sdk"; -import { - fetchAttestations, - createDataFilter, - type Attestation, -} from "~/utils/fetchAttestations"; -import { createAttestation } from "./createAttestation"; -import { ethers, providers } from "ethers"; -import { createEAS } from "./createEAS"; -import pLimit from "p-limit"; -import { formatEther } from "viem"; -import { createInterface } from "node:readline/promises"; -import { stdin, stdout } from "node:process"; - -const limit = pLimit(5); -const wallet = new ethers.Wallet(process.env.WALLET_PRIVATE_KEY!).connect( - new providers.AlchemyProvider( - config.network.network, - process.env.NEXT_PUBLIC_ALCHEMY_ID, - ), -) as unknown as providers.JsonRpcSigner; - -console.log(wallet.getAddress()); -wallet - .getBalance() - .then((r) => console.log(formatEther(r.toBigInt()))) - .catch(console.log); - -const applications = [ - "L2BEAT", - "EthStaker", - "Dappnode", - "ZK Email", - "ethers.js", - "rotki", - "beaconcha.in", - "Ethereum Attestation Service", - "wagmi", - "Protocol Guild", - "eth.limo", - "NiceNode", - "Ethereum on ARM", - "Blockscout Block Explorer - Decentralized, Open-Source, Transparent Block Explorer for All Chains", - "Otterscan", - "Stereum - Ethereum Node Installer & Manager", - "Somer Esat Ethereum Staking Guides (Ubuntu)", - "eth-wizard: An Ethereum validator installation wizard", - "OpenZeppelin Contracts", - "Candide Labs", - "TrueBlocks and the Unchained Index", - "PLUME: Pseudonymously Linked Unique Message Entities, aka Verifiably Deterministic Signatures on Ethereum", - "Blockhead: portfolio tracker, block explorer and web3 browser", - "Ethereum Staking Guides by CoinCashew", - "Ephemery Testnet", - "D4C : Fuzzing the Ethereum Network", - "eth-pkg", - "ethRPCtoREST", - - "Revoke.cash", - "OmniBTC", - "JediSwap", - "Hey.xyz (formerly Lenster)", - "DefiLlama", - "IDriss - A more usable web3 for everyone", - "Umbra", - "BrightID 🔆 Universal Proof of Uniqueness", - "IDENA", - "Hypercerts Foundation", - "Carmine Options AMM", - "The Tor Project", - "Tape (formerly Lenstube)", - "Iron Wallet", - "Sybil Defense for Public Goods - with privacy", - "Kleo Network", - "POAPin", - "Mirror", - "Starksheet", - "Citizen Wallet - an open source wallet with account abstraction for your community", - "Zk- Block | Tools for Zk & Web3 Dapps", - "Impersonator", - "Web3 GPT", - "4EVERLAND", - "Glo Dollar", - "Gitcoin Passport Plugin for Discourse", - "Astral", - "Gitcoin Grants Data Portal", - "Sybil-scorer", - "ZeroPool", - "Rouge Ticket", - "Commons Stack", - "ZKP2P Fiat On Ramp", - "Epoch Protocol", - "ENS Wayback Machine", - "1Hive Gardens", - "growthepie.xyz 🥧📏 - Layer 2 and Blockspace Analytics", - "dm3 protocol - the interoperability initiative", - "Open Source Observer", - "Qortal", - "Ether Alpha", - "WTF Academy", - "DappReader", - "dSentra", - "zkVRF", - - "Trustalabs", - "EtherScore", - "ZachXBT", - "Giveth", - "Alpha Insiders", - "Olimpio Education", - "Revoke All", - "BanklessDAO", - "GreenPill Network", - "Wizard Bridge EVM", - "Metopia", - "Week in Ethereum News", - "EtherDrops Bot", - "ITU Blockchain", - "Funding the Commons", - "The Rollup", - "Castle Capital", - "Bankless Academy", - "OpenCivics", - "Unitap", - "Rhino Review - Ethereum Staking Journal", - "Web3beach", - "MetaGame", - "Fractal Visions", - "LexDAO", - "Web3 Security", - "Regens Unite - bridging the gap between web3 and local regens", - "Decentralized Science on Gitcoin", - "LensPlay", - "Public Nouns Operations", - "DeBox", - "The Noun Square", - "Gravity DAO", - "Blocktrend(區塊勢)", - "Boring Security", - "Upgrading Ethereum Book", - "Urbanika and the Self-Management Neighborhood Course", - "ENS DAO Newsletter: Providing news and core ENS development to the global Ethereum community.", - "ReFi DAO - A New Chapter…", - "The Blockchain Socialist", - "Coin Center", - "Metagov", - "Crypto Altruism – Accessible Web3 Education for Nonprofits and Changemakers", - "Biteye", - "Psychedelic Puppet Show DAO", - "JobStash", - "Public Good Africa", - "Onlyfun", - "Dream DAO", - "OpenData Community", -]; - -async function createApplications() { - return Promise.all( - applications.map((name) => - limit(() => - fetchAttestations( - [ - "0x76e98cce95f3ba992c2ee25cef25f756495147608a3da3aa2e5ca43109fe77cc", - ], - { - take: 1000, - where: { - time: undefined, - ...createDataFilter("displayName", "string", name), - }, - }, - ).then(async ([attestation]) => { - if (!attestation) return null; - const application = attestation as Attestation & { - applicationMetadataPtr: string; - displayName: string; - }; - const [profile] = (await fetchAttestations( - [ - "0xac4c92fc5c7babed88f78a917cdbcdc1c496a8f4ab2d5b2ec29402736b2cf929", - ], - { - take: 1000, - where: { - time: undefined, - attester: { equals: attestation.attester }, - }, - }, - )) as [Attestation & { profileMetadataPtr: string }]; - if (!profile) return null; - - const profileAttestation = await createAttestation( - { - schemaUID: eas.schemas.metadata, - recipient: profile.attester, - values: { - name: profile.name, - metadataType: 0, // "http" - metadataPtr: profile?.profileMetadataPtr, - type: "profile", - round: config.roundId, - }, - }, - wallet, - ); - - const applicationAttestation = await createAttestation( - { - schemaUID: eas.schemas.metadata, - recipient: application.attester, - values: { - name: application.displayName, - metadataType: 0, // "http" - metadataPtr: application?.applicationMetadataPtr, - type: "application", - round: config.roundId, - }, - }, - wallet, - ); - - return [profileAttestation, applicationAttestation]; - }), - ), - ), - ) - .then((res) => res.flat()) - .then((res) => res.filter(Boolean)) - .then(async (attestations) => { - console.log(attestations); - console.log(attestations.length); - - if ( - ["y", "yes"].includes( - await createInterface({ input: stdin, output: stdout }).question( - `Do you want to create ${attestations.length} attestations? (y/yes)\n`, - ), - ) - ) { - const EAS = createEAS(wallet); - // Only attest 10 at a time (could cause error if more) - return chunk(attestations, 10).map((parts) => { - const data = parts - .filter(Boolean) - .map((att) => ({ ...att, data: [att?.data] })); - return EAS.multiAttest(data as MultiAttestationRequest[]); - }); - } else { - console.log("Exiting"); - return; - } - }); -} - -function chunk(array: T[], chunkSize: number) { - const result = []; - for (let i = 0; i < array.length; i += chunkSize) { - result.push(array.slice(i, i + chunkSize)); - } - return result; -} - -createApplications() - .then((res) => { - console.log(res); - console.log("Done!"); - }) - .catch((err) => { - console.log(err); - console.log("Error creating attestations"); - }) - .finally(() => process.exit(0)); From f6e173d7afef0519bc5b34d647dc90c2f88c94bc Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Tue, 12 Mar 2024 08:50:28 +0100 Subject: [PATCH 25/37] Minor fix --- src/features/distribute/components/Distributions.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/distribute/components/Distributions.tsx b/src/features/distribute/components/Distributions.tsx index 39289f06..d63eda5d 100644 --- a/src/features/distribute/components/Distributions.tsx +++ b/src/features/distribute/components/Distributions.tsx @@ -17,7 +17,7 @@ import { useProjectsById } from "~/features/projects/hooks/useProjects"; import { api } from "~/utils/api"; import { format } from "~/utils/csv"; import { usePoolAmount, usePoolToken } from "../hooks/useAlloPool"; -import { Address, formatUnits, parseUnits } from "viem"; +import { type Address, formatUnits, parseUnits } from "viem"; import { cn } from "~/utils/classNames"; import { formatNumber } from "~/utils/formatNumber"; @@ -156,7 +156,7 @@ function ConfirmDistributionDialog({ ["text-red-600"]: amountDiff < 0n, })} > - {formatNumber(formatUnits(amountDiff, token.decimals))} + {formatNumber(Number(formatUnits(amountDiff, token.decimals)))}
From 6147b2b1891473dc1c14cc59c500881d8af267e3 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Thu, 14 Mar 2024 09:55:36 +0100 Subject: [PATCH 26/37] Fix create pool --- .../distribute/components/CreatePool.tsx | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/features/distribute/components/CreatePool.tsx b/src/features/distribute/components/CreatePool.tsx index 4e5c9589..1da96489 100644 --- a/src/features/distribute/components/CreatePool.tsx +++ b/src/features/distribute/components/CreatePool.tsx @@ -26,7 +26,13 @@ import { useTokenBalance, } from "../hooks/useAlloPool"; import { allo, isNativeToken } from "~/config"; -import { ErrorMessage, Form, FormControl, Input } from "~/components/ui/Form"; +import { + ErrorMessage, + Form, + FormControl, + Input, + Label, +} from "~/components/ui/Form"; import { AllocationInput } from "~/features/ballot/components/AllocationInput"; import { useFormContext } from "react-hook-form"; import { MintButton } from "./MintButton"; @@ -129,7 +135,7 @@ function CreatePool() { strategyAddress: allo.strategyAddress, tokenName: "ETH", }} - onSubmit={(values) => { + onSubmit={(values, form) => { console.log(values); const amount = parseUnits(values.amount.toString(), decimals); @@ -149,11 +155,12 @@ function CreatePool() { - - - + -
+
@@ -206,7 +213,6 @@ function PoolDetails({ poolId = 0 }) { })} onSubmit={async (values, form) => { const amount = parseUnits(values.amount.toString(), decimals); - console.log({ amount }); const hasAllowance = calcHasAllowance({ amount: BigInt(values.amount), allowance, @@ -214,7 +220,7 @@ function PoolDetails({ poolId = 0 }) { }); if (!hasAllowance) { - return approve.write({ + return approve.writeAsync({ args: [allo.alloAddress, amount], }); } From 80f8b107e6b5a129ebec39bd65e95cf2a68010c1 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Thu, 14 Mar 2024 09:55:45 +0100 Subject: [PATCH 27/37] Fix submit ballot --- src/features/ballot/hooks/useBallot.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/features/ballot/hooks/useBallot.ts b/src/features/ballot/hooks/useBallot.ts index c2bef7f4..b0d25ead 100644 --- a/src/features/ballot/hooks/useBallot.ts +++ b/src/features/ballot/hooks/useBallot.ts @@ -61,25 +61,28 @@ export function useSubmitBallot({ onSuccess: () => Promise; }) { const { chain } = useNetwork(); - const { data: ballot } = useBallot(); + const { refetch } = useBallot(); const { mutateAsync, isLoading } = api.ballot.publish.useMutation({ onSuccess, }); useBeforeUnload(isLoading, "You have unsaved changes, are you sure?"); - const message = { - total_votes: BigInt(sumBallot(ballot?.votes)), - project_count: BigInt(ballot?.votes?.length ?? 0), - hashed_votes: keccak256(Buffer.from(JSON.stringify(ballot?.votes))), - }; - const { signTypedDataAsync } = useSignTypedData({ - ...ballotTypedData(chain?.id), - message, - }); + const { signTypedDataAsync } = useSignTypedData(); return useMutation(async () => { if (chain) { - const signature = await signTypedDataAsync(); + const { data: ballot } = await refetch(); + + const message = { + total_votes: BigInt(sumBallot(ballot?.votes)), + project_count: BigInt(ballot?.votes?.length ?? 0), + hashed_votes: keccak256(Buffer.from(JSON.stringify(ballot?.votes))), + }; + const signature = await signTypedDataAsync({ + ...ballotTypedData(chain?.id), + message, + }); + return mutateAsync({ signature, message, chainId: chain?.id }); } }); From e2421f9fa9cf5ca1ef821868b3f9361088418fc5 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Thu, 14 Mar 2024 10:02:32 +0100 Subject: [PATCH 28/37] Update .env.example --- .env.example | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index aee55a50..18f67b32 100644 --- a/.env.example +++ b/.env.example @@ -116,9 +116,9 @@ NEXT_PUBLIC_EAS_SCHEMA_REGISTRY_ADDRESS=0x42000000000000000000000000000000000000 # ------------------- # Allo2 Configuration # ------------------- -NEXT_PUBLIC_ALLO2_ADDRESS=0x1133eA7Af70876e64665ecD07C0A0476d09465a1 -NEXT_PUBLIC_STRATEGY_ADDRESS= -NEXT_PUBLIC_TOKEN_ADDRESS= +NEXT_PUBLIC_ALLO2_ADDRESS="0x1133eA7Af70876e64665ecD07C0A0476d09465a1" +NEXT_PUBLIC_STRATEGY_ADDRESS="0xa3c5a2ea8ca2060e00761069b23da5171146a747" +NEXT_PUBLIC_TOKEN_ADDRESS="" # ---------------------- From 8c361926124eb09af00705316750b5b96339cd59 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Thu, 14 Mar 2024 10:15:56 +0100 Subject: [PATCH 29/37] Cleanup --- .../distribute/components/CreatePool.tsx | 16 +++------------- .../distribute/hooks/useAlloProfile.ts | 18 ------------------ src/pages/distribute/index.tsx | 8 -------- 3 files changed, 3 insertions(+), 39 deletions(-) diff --git a/src/features/distribute/components/CreatePool.tsx b/src/features/distribute/components/CreatePool.tsx index 1da96489..310bf32b 100644 --- a/src/features/distribute/components/CreatePool.tsx +++ b/src/features/distribute/components/CreatePool.tsx @@ -10,11 +10,7 @@ import { Button, IconButton } from "~/components/ui/Button"; import { Alert } from "~/components/ui/Alert"; import { useIsCorrectNetwork } from "~/hooks/useIsCorrectNetwork"; import { Spinner } from "~/components/ui/Spinner"; -import { - useAlloIsMemberOfProfile, - useAlloProfile, - useCreateAlloProfile, -} from "../hooks/useAlloProfile"; +import { useAlloProfile, useCreateAlloProfile } from "../hooks/useAlloProfile"; import { useApprove, useCreatePool, @@ -36,16 +32,12 @@ import { import { AllocationInput } from "~/features/ballot/components/AllocationInput"; import { useFormContext } from "react-hook-form"; import { MintButton } from "./MintButton"; -import { api } from "~/utils/api"; function CheckAlloProfile(props: PropsWithChildren) { const { isCorrectNetwork, correctNetwork } = useIsCorrectNetwork(); const profile = useAlloProfile(); const createProfile = useCreateAlloProfile(); - const isMember = useAlloIsMemberOfProfile(); - - console.log("create profile,", createProfile.data); if (!isCorrectNetwork) { return ( @@ -54,7 +46,7 @@ function CheckAlloProfile(props: PropsWithChildren) { ); } - if (profile.isLoading || isMember.isLoading) { + if (profile.isLoading) { return ( @@ -135,9 +127,7 @@ function CreatePool() { strategyAddress: allo.strategyAddress, tokenName: "ETH", }} - onSubmit={(values, form) => { - console.log(values); - + onSubmit={(values) => { const amount = parseUnits(values.amount.toString(), decimals); const hasAllowance = isNativeToken ? true diff --git a/src/features/distribute/hooks/useAlloProfile.ts b/src/features/distribute/hooks/useAlloProfile.ts index 5b5ed345..6f5fdd5a 100644 --- a/src/features/distribute/hooks/useAlloProfile.ts +++ b/src/features/distribute/hooks/useAlloProfile.ts @@ -4,7 +4,6 @@ import { type Address, zeroAddress } from "viem"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { solidityPackedKeccak256 } from "ethers"; import { useAlloRegistry, waitForLogs } from "./useAllo"; -import { allo } from "~/config"; export function useAlloProfile() { const registry = useAlloRegistry(); @@ -48,23 +47,6 @@ export function useCreateAlloProfile() { }); }); } -export function useAlloIsMemberOfProfile() { - const registry = useAlloRegistry(); - const { address } = useAccount(); - - return useQuery( - ["allo/registry/member"], - async () => { - const profileId = getProfileId(address); - - return registry?.isMemberOfProfile({ - profileId, - account: allo.strategyAddress, - }); - }, - { enabled: Boolean(registry && address) }, - ); -} function getProfileId(address?: Address) { return solidityPackedKeccak256( diff --git a/src/pages/distribute/index.tsx b/src/pages/distribute/index.tsx index 834e3761..4d303b85 100644 --- a/src/pages/distribute/index.tsx +++ b/src/pages/distribute/index.tsx @@ -17,14 +17,6 @@ import { api } from "~/utils/api"; export default function DistributePage() { const utils = api.useUtils(); const setConfig = api.config.set.useMutation({ - onMutate: async (data) => { - // Optimistic update - utils.config.get.setData(undefined, (prev) => ({ - id: prev?.id ?? "?", - ...data, - })); - }, - // Trigger re-fetch of votes (with new calculation settings) onSuccess: () => utils.results.votes.invalidate(), }); const settings = api.config.get.useQuery(); From 2a5cd405eb3b99e0724c8b6ede089830ad6df01a Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Thu, 14 Mar 2024 13:01:30 +0100 Subject: [PATCH 30/37] Fix add selected projects in projects page --- src/features/projects/hooks/useSelectProjects.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/projects/hooks/useSelectProjects.ts b/src/features/projects/hooks/useSelectProjects.ts index b7825715..fe83b1bd 100644 --- a/src/features/projects/hooks/useSelectProjects.ts +++ b/src/features/projects/hooks/useSelectProjects.ts @@ -7,7 +7,7 @@ import { export function useSelectProjects() { const add = useAddToBallot(); - const { data: ballot } = useBallot(); + const { data: ballot, isLoading } = useBallot(); const [selected, setSelected] = useState>({}); @@ -21,7 +21,7 @@ export function useSelectProjects() { return { count: toAdd.length, - isLoading: add.isLoading, + isLoading: isLoading || add.isLoading, add: () => { add.mutate(toAdd); setSelected({}); From 850f2d3aff6f72a626e648b7502cd690bb14504e Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Thu, 14 Mar 2024 14:10:24 +0100 Subject: [PATCH 31/37] Add adding to ballot state in ballot button --- src/features/ballot/components/BallotOverview.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/features/ballot/components/BallotOverview.tsx b/src/features/ballot/components/BallotOverview.tsx index 2cf7f72b..f3cd174a 100644 --- a/src/features/ballot/components/BallotOverview.tsx +++ b/src/features/ballot/components/BallotOverview.tsx @@ -26,6 +26,7 @@ function BallotOverview() { const router = useRouter(); const { data: ballot } = useBallot(); + const isSaving = useIsMutating(getQueryKey(api.ballot.save)); const sum = sumBallot(ballot?.votes); @@ -106,6 +107,10 @@ function BallotOverview() { + ) : isSaving ? ( + ) : canSubmit ? ( config.votingMaxTotal} /> ) : allocations.length ? ( From cb5dafb76f53cedd2f5ada4fd2285b8dfab11d11 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Thu, 14 Mar 2024 16:09:45 +0100 Subject: [PATCH 32/37] Only show published ballots in distribute --- src/server/api/routers/results.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/api/routers/results.ts b/src/server/api/routers/results.ts index 3750a757..16d7c77c 100644 --- a/src/server/api/routers/results.ts +++ b/src/server/api/routers/results.ts @@ -63,7 +63,7 @@ async function calculateBallotResults(db: PrismaClient) { // Fetch the ballots const ballots = await db.ballot.findMany({ - where: { publishedAt: { not: undefined } }, + where: { publishedAt: { not: null } }, }); const projects = calculateVotes( From 1295a0bbc3a3b9381b9d608fabee0a6fd839cb60 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Thu, 14 Mar 2024 16:27:51 +0100 Subject: [PATCH 33/37] Remove unused config --- src/config.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/config.ts b/src/config.ts index 6ec72fef..002a231e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -31,8 +31,6 @@ export const config = { export const nativeToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; export const allo = { - customStrategy: process.env - .NEXT_PUBLIC_ALLO2_CUSTOM_STRATEGY as `0x${string}`, alloAddress: process.env.NEXT_PUBLIC_ALLO2_ADDRESS as `0x${string}`, strategyAddress: process.env.NEXT_PUBLIC_STRATEGY_ADDRESS as `0x${string}`, // eslint-disable-next-line From e93156ef0b1ad13520d3fbdef3762ba1c91511d2 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Thu, 14 Mar 2024 16:49:08 +0100 Subject: [PATCH 34/37] Remove Test Mint button --- .../distribute/components/CreatePool.tsx | 2 -- .../distribute/components/MintButton.tsx | 23 ------------------- 2 files changed, 25 deletions(-) delete mode 100644 src/features/distribute/components/MintButton.tsx diff --git a/src/features/distribute/components/CreatePool.tsx b/src/features/distribute/components/CreatePool.tsx index 310bf32b..3a8a7097 100644 --- a/src/features/distribute/components/CreatePool.tsx +++ b/src/features/distribute/components/CreatePool.tsx @@ -235,8 +235,6 @@ function PoolDetails({ poolId = 0 }) { {(error as { message: string })?.message} - - ); } diff --git a/src/features/distribute/components/MintButton.tsx b/src/features/distribute/components/MintButton.tsx deleted file mode 100644 index 76edeac3..00000000 --- a/src/features/distribute/components/MintButton.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { parseAbi, parseEther } from "viem"; -import { useAccount, useContractWrite } from "wagmi"; -import { Button } from "~/components/ui/Button"; -import { allo } from "~/config"; - -export function MintButton() { - const { address } = useAccount(); - const mint = useContractWrite({ - address: allo.tokenAddress, - abi: parseAbi(["function mint(address to, uint256 amount)"]), - functionName: "mint", - args: [address!, parseEther("10000")], - }); - return ( - - ); -} From 1f6bfdbd9093976e464bec8bb22c78d0a590a017 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Fri, 15 Mar 2024 10:42:59 +0100 Subject: [PATCH 35/37] Update env.js network names --- src/env.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/env.js b/src/env.js index c1e0613c..a96bde3b 100644 --- a/src/env.js +++ b/src/env.js @@ -41,10 +41,10 @@ export const env = createEnv({ "ethereum", "optimism", "optimismSepolia", - "optimismGoerli", "arbitrum", "linea", "sepolia", + "base", "baseGoerli", ]), NEXT_PUBLIC_SIGN_STATEMENT: z.string().optional(), From 1f96a8541db27c776eb536fadc055989aa2fc61e Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Fri, 15 Mar 2024 10:43:29 +0100 Subject: [PATCH 36/37] Fix create list --- src/features/ballot/components/AllocationList.tsx | 2 +- src/features/lists/components/ListCreateForm.tsx | 10 ++++++---- src/features/lists/components/SearchProjects.tsx | 4 ++-- src/pages/lists/index.tsx | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/features/ballot/components/AllocationList.tsx b/src/features/ballot/components/AllocationList.tsx index 3fb70d22..eb5dcff4 100644 --- a/src/features/ballot/components/AllocationList.tsx +++ b/src/features/ballot/components/AllocationList.tsx @@ -81,7 +81,7 @@ export function AllocationFormWithSearch() { - + - + {/* */}
@@ -175,7 +175,7 @@ function CreateListButton({ -
{formatNumber(current)} OP
+
+ {formatNumber(current)} {config.tokenName} +
); diff --git a/src/features/lists/components/SearchProjects.tsx b/src/features/lists/components/SearchProjects.tsx index 31495163..9926edeb 100644 --- a/src/features/lists/components/SearchProjects.tsx +++ b/src/features/lists/components/SearchProjects.tsx @@ -55,7 +55,7 @@ export const SearchProjects = ({ addedProjects, onSelect }: Props) => { {projectsData.length ? ( { > {item.name} diff --git a/src/pages/lists/index.tsx b/src/pages/lists/index.tsx index c829c6e0..c0fdf6f4 100644 --- a/src/pages/lists/index.tsx +++ b/src/pages/lists/index.tsx @@ -7,7 +7,7 @@ import Link from "next/link"; export default function ListsPage() { return ( -
+
From 5359815239c365e036651b1c0cd03ed3f02e6519 Mon Sep 17 00:00:00 2001 From: Carl Barrdahl Date: Fri, 15 Mar 2024 10:47:14 +0100 Subject: [PATCH 37/37] Fix votes in results --- src/features/projects/components/ProjectsResults.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/features/projects/components/ProjectsResults.tsx b/src/features/projects/components/ProjectsResults.tsx index 4e3fffc4..6559e237 100644 --- a/src/features/projects/components/ProjectsResults.tsx +++ b/src/features/projects/components/ProjectsResults.tsx @@ -21,7 +21,9 @@ export function ProjectsResults() { className={clsx("relative", { ["animate-pulse"]: isLoading })} > {!results.isLoading && getAppState() === "RESULTS" ? ( - + ) : null}