From 5468ce133f9040dc07588d1ac6fb8828bfc8b829 Mon Sep 17 00:00:00 2001 From: Akira <156126180+akiraonstarknet@users.noreply.github.com> Date: Sun, 15 Sep 2024 02:22:42 +0530 Subject: [PATCH 01/12] Revert "add community page" --- src/app/community/page.tsx | 195 ----------------------- src/app/globals.css | 74 ++------- src/assets/illustration.svg | 301 ------------------------------------ src/assets/x.svg | 3 - src/components/Navbar.tsx | 62 +------- 5 files changed, 17 insertions(+), 618 deletions(-) delete mode 100644 src/app/community/page.tsx delete mode 100644 src/assets/illustration.svg delete mode 100644 src/assets/x.svg diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx deleted file mode 100644 index a00f42b4..00000000 --- a/src/app/community/page.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import { NextPage } from 'next'; -import React from 'react'; -import x from '@/assets/x.svg'; -import illustration from '@/assets/illustration.svg'; - -import { - Box, - Button, - Image as ChakraImage, - Container, - Link, - Text, -} from '@chakra-ui/react'; - -interface CommunityPage {} - -const CommunityPage: NextPage = () => { - return ( - - - - - Community Program - - - Earn points to level up and collect NFTs that signal your loyalty. - These NFTs unlock future rewards and exclusive incentives. - - - - - - - - - - - - - - https://strkfarm.xyz/?referralCode+undefined&refer.... - - - - - - - - - - - - - - Your Stats - - - - Coming soon - - - - You will be able to check your points and claim your NFTs here soon. - - - - ); -}; - -export default CommunityPage; diff --git a/src/app/globals.css b/src/app/globals.css index 00737548..41c3f85a 100755 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -32,10 +32,11 @@ body { } } -.pagination-container { +.pagination-container { padding: 20px 0px 2px 0px !important; } + .glow-button { width: 220px; height: 50px; @@ -51,21 +52,10 @@ body { .glow-button:before { content: ''; - background: linear-gradient( - 45deg, - #ff0000, - #ff7300, - #fffb00, - #48ff00, - #00ffd5, - #002bff, - #7a00ff, - #ff00c8, - #ff0000 - ); + background: linear-gradient(45deg, #ff0000, #ff7300, #fffb00, #48ff00, #00ffd5, #002bff, #7a00ff, #ff00c8, #ff0000); position: absolute; top: -2px; - left: -2px; + left:-2px; background-size: 400%; z-index: -1; filter: blur(5px); @@ -73,12 +63,12 @@ body { height: calc(100% + 4px); animation: glowing 20s linear infinite; opacity: 1; - transition: opacity 0.3s ease-in-out; + transition: opacity .3s ease-in-out; border-radius: 10px; } .glow-button:active { - color: #000; + color: #000 } .glow-button:active:after { @@ -98,30 +88,12 @@ body { } @keyframes glowing { - 0% { - background-position: 0 0; - opacity: 0; - } - 45% { - background-position: 400% 0; - opacity: 0; - } - 50% { - background-position: 400% 0; - opacity: 1; - } - 70% { - background-position: 0 0; - opacity: 1; - } - 75% { - background-position: 0 0; - opacity: 0; - } - 100% { - background-position: 0 0; - opacity: 0; - } + 0% { background-position: 0 0; opacity: 0; } + 45% { background-position: 400% 0; opacity: 0; } + 50% { background-position: 400% 0; opacity: 1; } + 70% { background-position: 0 0; opacity: 1; } + 75% { background-position: 0 0; opacity: 0; } + 100% { background-position: 0 0; opacity:0; } } .chakra-collapse { @@ -133,6 +105,7 @@ body { padding: 3px; } + .embla { width: 95%; margin: auto; @@ -155,27 +128,12 @@ body { } .theme-gradient-text { - background: linear-gradient( - to right, - var(--chakra-colors-purple) 10%, - var(--chakra-colors-cyan) 50%, - var(--chakra-colors-cyan) 100% - ); + background: linear-gradient(to right, var(--chakra-colors-purple) 10%, var(--chakra-colors-cyan) 50%, var(--chakra-colors-cyan) 100%); -webkit-background-clip: text; color: transparent; } -.theme-gradient { - background: linear-gradient( - to right, - var(--chakra-colors-purple) 10%, - var(--chakra-colors-cyan) 50%, - var(--chakra-colors-cyan) 100% - ); -} - -td, -th { +td, th { border-color: var(--chakra-colors-bg) !important; vertical-align: top; } @@ -186,4 +144,4 @@ th { .orange { color: orange; -} +} \ No newline at end of file diff --git a/src/assets/illustration.svg b/src/assets/illustration.svg deleted file mode 100644 index 9cfc1630..00000000 --- a/src/assets/illustration.svg +++ /dev/null @@ -1,301 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/x.svg b/src/assets/x.svg deleted file mode 100644 index e2cad703..00000000 --- a/src/assets/x.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 37008abb..ce3b6ecc 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,15 +1,10 @@ -import { ChevronDownIcon, HamburgerIcon } from '@chakra-ui/icons'; +import { ChevronDownIcon } from '@chakra-ui/icons'; import { Avatar, Box, Button, Center, Container, - Drawer, - DrawerBody, - DrawerContent, - DrawerHeader, - DrawerOverlay, Flex, IconButton, Image, @@ -19,7 +14,6 @@ import { MenuItem, MenuList, Text, - useDisclosure, } from '@chakra-ui/react'; import { useAtom, useSetAtom } from 'jotai'; import { useStarknetkitConnectModal } from 'starknetkit'; @@ -204,8 +198,6 @@ export default function Navbar(props: NavbarProps) { })(); }, [address]); - const { isOpen, onOpen, onClose } = useDisclosure(); - return ( */} - - - - - {!props.hideTg && ( */} - + + + + From 114a45657188b43b0118b337fc875c349c43522b Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Sat, 14 Sep 2024 23:22:02 +0100 Subject: [PATCH 04/12] ui: change stats bg color to match figma design --- src/app/community/page.tsx | 35 +++++++++++------------------------ src/app/globals.css | 5 ++--- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx index 1dbe2de1..2567471e 100644 --- a/src/app/community/page.tsx +++ b/src/app/community/page.tsx @@ -90,25 +90,7 @@ const CommunityPage: NextPage = () => { padding={{ base: '0px 10px', md: '0px 10px' }} border="1px" borderRadius="5px" - zIndex={1} - _before={{ - content: `""`, - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - borderRadius: 'inherit', - padding: '1.5px', - background: - 'linear-gradient(90deg, #6F4FF2 0%, #61FCAE 100%)', - WebkitMask: - 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)', - mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)', - WebkitMaskComposite: 'destination-out', - maskComposite: 'exclude', - zIndex: -1, - }} + borderColor="#3B4A3E" > https://strkfarm.xyz/?referralCode+undefined&refer.... @@ -130,8 +112,8 @@ const CommunityPage: NextPage = () => { display="flex" alignItems="center" padding={{ base: '5px', md: '10px' }} - border="2px" - borderColor="#61FCAE" + border="1px" + borderColor="#3B4A3E" borderRadius="5px" _hover={{ bg: 'color2_50p', @@ -160,7 +142,7 @@ const CommunityPage: NextPage = () => { width="100%" justifyContent="space-between" alignItems="center" - padding="10px 15px" + padding="15px 15px" border="none" borderRadius="5px" zIndex={1} @@ -173,7 +155,8 @@ const CommunityPage: NextPage = () => { bottom: 0, borderRadius: 'inherit', padding: '1.5px', - background: 'linear-gradient(90deg, #61FCAE 0%, #6F4FF2 100%)', + background: + 'linear-gradient(90deg, rgba(111, 79, 242, 0.2) 0%, rgba(97, 252, 174, 0.2) 100%)', WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)', mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)', @@ -186,7 +169,11 @@ const CommunityPage: NextPage = () => { Coming soon - + You will be able to check your points and claim your NFTs here soon. diff --git a/src/app/globals.css b/src/app/globals.css index 00737548..e5419d4b 100755 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -168,9 +168,8 @@ body { .theme-gradient { background: linear-gradient( to right, - var(--chakra-colors-purple) 10%, - var(--chakra-colors-cyan) 50%, - var(--chakra-colors-cyan) 100% + rgba(111, 79, 242, 0.3) 0%, + rgba(97, 252, 174, 0.3) 100% ); } From e710c4dfecc6cd275f11fecb5c968496a166de8e Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Sun, 15 Sep 2024 05:54:46 +0100 Subject: [PATCH 05/12] feat: add share intent link to X button --- src/app/community/page.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx index 2567471e..171fd0be 100644 --- a/src/app/community/page.tsx +++ b/src/app/community/page.tsx @@ -108,7 +108,8 @@ const CommunityPage: NextPage = () => { Date: Sun, 15 Sep 2024 05:58:01 +0100 Subject: [PATCH 06/12] ui: reduce border brightness of know more button --- src/app/community/page.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx index 171fd0be..cb596d7e 100644 --- a/src/app/community/page.tsx +++ b/src/app/community/page.tsx @@ -58,7 +58,8 @@ const CommunityPage: NextPage = () => { bottom: 0, borderRadius: 'inherit', padding: '1.5px', - background: 'linear-gradient(90deg, #6F4FF2 0%, #61FCAE 100%)', + background: + 'linear-gradient(90deg, rgba(111, 79, 242, 0.4) 0%, rgba(97, 252, 174, 0.4) 100%)', WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)', mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)', From 4bb671580dfad6bc4cbdb7a0d7042739539f0faa Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Sun, 15 Sep 2024 13:10:06 +0100 Subject: [PATCH 07/12] feat: implement copy referral link --- src/app/community/page.tsx | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx index cb596d7e..c3263c05 100644 --- a/src/app/community/page.tsx +++ b/src/app/community/page.tsx @@ -1,7 +1,11 @@ -import { NextPage } from 'next'; -import React from 'react'; +'use client'; + import x from '@/assets/x.svg'; import illustration from '@/assets/illustration.svg'; +import { useAtomValue } from 'jotai'; +import { referralCodeAtom } from '@/store/referral.store'; +import toast from 'react-hot-toast'; +import { getReferralUrl } from '@/utils'; import { Box, @@ -12,9 +16,21 @@ import { Text, } from '@chakra-ui/react'; -interface CommunityPage {} +const CommunityPage = () => { + const referralCode = useAtomValue(referralCodeAtom); + + function copyReferralLink() { + if (window.location.origin.includes('app.strkfarm.xyz')) { + navigator.clipboard.writeText(`https://strkfarm.xyz/r/${referralCode}`); + } else { + navigator.clipboard.writeText(getReferralUrl(referralCode)); + } + + toast.success('Referral link copied to clipboard', { + position: 'bottom-right', + }); + } -const CommunityPage: NextPage = () => { return ( = () => { borderColor="#3B4A3E" > - https://strkfarm.xyz/?referralCode+undefined&refer.... + {`https://strkfarm.xyz/r/${referralCode}`} Date: Sun, 15 Sep 2024 13:57:43 +0100 Subject: [PATCH 08/12] feat: enable referral code button conditionally --- src/app/community/page.tsx | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx index c3263c05..b1caf4f4 100644 --- a/src/app/community/page.tsx +++ b/src/app/community/page.tsx @@ -110,8 +110,11 @@ const CommunityPage = () => { borderColor="#3B4A3E" > - {`https://strkfarm.xyz/r/${referralCode}`} + {!referralCode + ? 'Referral link loading...' + : `https://strkfarm.xyz/r/${referralCode}`} + From 51cf7e75a83f9aad0873174fd0ca176810b60dc0 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Sun, 15 Sep 2024 14:00:34 +0100 Subject: [PATCH 09/12] feat: enable referral code button conditionally --- src/app/community/page.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx index b1caf4f4..0bf8ad78 100644 --- a/src/app/community/page.tsx +++ b/src/app/community/page.tsx @@ -131,16 +131,16 @@ const CommunityPage = () => { diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index a7062321..5dba832d 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -285,8 +285,11 @@ export default function Navbar(props: NavbarProps) { bg: 'color2_50p', }} display={{ base: 'none !important', md: 'flex !important' }} + onClick={() => { + mixpanel.track('community_program_click'); + }} > - Community + ✨ Community Program @@ -310,7 +313,6 @@ export default function Navbar(props: NavbarProps) { bg: 'color2_50p', }} display={{ base: 'none !important', md: 'flex !important' }} - className="glow-button" > Join Telegram @@ -433,10 +435,13 @@ export default function Navbar(props: NavbarProps) { { + onClose(); + mixpanel.track('community_program_click'); + }} mt={4} > - Community + ✨ Community Program diff --git a/src/components/TVL.tsx b/src/components/TVL.tsx index 614c9dde..4318d67a 100755 --- a/src/components/TVL.tsx +++ b/src/components/TVL.tsx @@ -2,7 +2,7 @@ import { addressAtom } from '@/store/claims.atoms'; import { referralCodeAtom } from '@/store/referral.store'; import { strategiesAtom } from '@/store/strategies.atoms'; import { dAppStatsAtom, userStatsAtom } from '@/store/utils.atoms'; -import { getReferralUrl } from '@/utils'; +import { copyReferralLink } from '@/utils'; import { CopyIcon } from '@chakra-ui/icons'; import { Box, @@ -18,7 +18,6 @@ import { import { useAtomValue } from 'jotai'; import Link from 'next/link'; import React from 'react'; -import toast from 'react-hot-toast'; const TVL: React.FC = () => { const _strategies = useAtomValue(strategiesAtom); @@ -29,18 +28,6 @@ const TVL: React.FC = () => { const address = useAtomValue(addressAtom); const referralCode = useAtomValue(referralCodeAtom); - function copyReferralLink() { - if (window.location.origin.includes('app.strkfarm.xyz')) { - navigator.clipboard.writeText(`https://strkfarm.xyz/r/${referralCode}`); - } else { - navigator.clipboard.writeText(getReferralUrl(referralCode)); - } - - toast.success('Referral link copied to clipboard', { - position: 'bottom-right', - }); - } - return ( { textDecoration="underline" fontWeight="600" cursor={'pointer'} - onClick={copyReferralLink} + onClick={() => { + copyReferralLink(referralCode); + }} > {referralCode} @@ -124,7 +113,12 @@ const TVL: React.FC = () => { )} {address && ( - + { + copyReferralLink(referralCode); + }} + /> )} diff --git a/src/utils.ts b/src/utils.ts index dfc2c871..81e53797 100755 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,7 @@ import { MenuItemProps, MenuListProps } from '@chakra-ui/react'; import { num } from 'starknet'; import { TOKENS } from './constants'; +import toast from 'react-hot-toast'; export function getUniqueStrings(arr: Array) { const _arr: string[] = []; @@ -92,6 +93,9 @@ export function generateReferralCode() { } export function getReferralUrl(referralCode: string) { + if (window.location.origin.includes('app.strkfarm.xyz')) { + return `https://strkfarm.xyz/r/${referralCode}`; + } return `${window.location.origin}/r/${referralCode}`; } @@ -101,3 +105,11 @@ export function getDisplayCurrencyAmount( ) { return Number(Number(amount).toFixed(decimals)).toLocaleString(); } + +export function copyReferralLink(refCode: string) { + navigator.clipboard.writeText(getReferralUrl(refCode)); + + toast.success('Referral link copied to clipboard', { + position: 'bottom-right', + }); +} From 2fdab8103c9d4bb35f3b98db463f4327dd54625c Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Wed, 18 Sep 2024 17:46:13 +0100 Subject: [PATCH 11/12] feat: add OG Section in community page --- src/app/community/page.tsx | 202 +++++++++++++++++++++++++++++++++++++ src/assets/og_nft.jpg | Bin 0 -> 29519 bytes 2 files changed, 202 insertions(+) create mode 100644 src/assets/og_nft.jpg diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx index 0bf8ad78..9dadc65e 100644 --- a/src/app/community/page.tsx +++ b/src/app/community/page.tsx @@ -1,11 +1,21 @@ 'use client'; import x from '@/assets/x.svg'; +import og_nft from '@/assets/og_nft.jpg'; import illustration from '@/assets/illustration.svg'; import { useAtomValue } from 'jotai'; import { referralCodeAtom } from '@/store/referral.store'; import toast from 'react-hot-toast'; import { getReferralUrl } from '@/utils'; +import { + useContractRead, + useContractWrite, + useProvider, +} from '@starknet-react/core'; +import { Contract } from 'starknet'; +import NFTAbi from '../../abi/nft.abi.json'; +import { atomWithQuery } from 'jotai-tanstack-query'; +import { addressAtom } from '@/store/claims.atoms'; import { Box, @@ -13,11 +23,88 @@ import { Image as ChakraImage, Container, Link, + Progress, Text, } from '@chakra-ui/react'; +import { useEffect, useMemo, useState } from 'react'; + +interface OGNFTUserData { + address: string; + hash: string; + isOgNFTUser: boolean; + sig: string[]; + totalOgNFTUsers: number; +} + +const isOGNFTEligibleAtom = atomWithQuery((get) => { + return { + queryKey: ['isOGNFTEligibleAtom'], + queryFn: async ({ queryKey }: any): Promise => { + const address = get(addressAtom); + if (!address) return null; + const data = await fetch(`/api/users/ognft/${address}`); + return data.json(); + }, + refetchInterval: 5000, + }; +}); const CommunityPage = () => { + const [progress, setProgress] = useState(0); + const [isEligible, setIsEligible] = useState(false); + const [hasNFT, setHasNFT] = useState(false); + const [isEligibilityChecked, setIsEligibilityChecked] = useState(false); const referralCode = useAtomValue(referralCodeAtom); + const isOGNFTEligible = useAtomValue(isOGNFTEligibleAtom); + const address = useAtomValue(addressAtom); + const { provider } = useProvider(); + const isOGNFTLoading = useMemo(() => { + return ( + isOGNFTEligible.isLoading || + isOGNFTEligible.isFetching || + isOGNFTEligible.isError + ); + }, [ + isOGNFTEligible.isLoading, + isOGNFTEligible.isFetching, + isOGNFTEligible.isError, + ]); + + const ogNFTContract = new Contract( + NFTAbi, + process.env.NEXT_PUBLIC_OG_NFT_CONTRACT || '', + provider, + ); + const { writeAsync: claimOGNFT } = useContractWrite({ + calls: [ + ogNFTContract.populate('mint', { + nftId: 1, + rewardEarned: 0, + hash: isOGNFTEligible.data?.hash || '0', + signature: isOGNFTEligible.data?.sig || [], + }), + ], + }); + + const { data: ogNFTBalance } = useContractRead({ + abi: NFTAbi, + address: process.env.NEXT_PUBLIC_OG_NFT_CONTRACT || '0', + functionName: 'balanceOf', + args: [address || '0x0', 1], + }); + + useEffect(() => { + if (isOGNFTEligible.isSuccess && isOGNFTEligible.data?.totalOgNFTUsers) { + setProgress(isOGNFTEligible.data.totalOgNFTUsers); + } + + if (ogNFTBalance && Number(ogNFTBalance.toLocaleString()) !== 0) { + setHasNFT(true); + } + + console.log('ogNFTBalance', ogNFTBalance); + console.log('isOGNFTEligible', isOGNFTEligible); + }, [ogNFTBalance, isOGNFTEligible]); function copyReferralLink() { if (window.location.origin.includes('app.strkfarm.xyz')) { @@ -31,6 +118,25 @@ const CommunityPage = () => { }); } + function handleEligibility() { + if (!address) { + toast.error('Please connect wallet', { + position: 'bottom-right', + }); + return; + } + + if (!isEligible) { + if (!isOGNFTLoading && isOGNFTEligible.data?.isOgNFTUser) { + setIsEligible(true); + } + } else { + claimOGNFT(); + } + + setIsEligibilityChecked(true); + } + return ( { + + + + OG Farmer Limited edition NFT + + + + {`${progress}/100 Selected`} + + + div': { + backgroundColor: '#795BF4', + }, + }} + /> + + + + + + {!isEligible && isEligibilityChecked && ( + <> + You're not eligible, but you can still earn one.{' '} + + Learn how here + + + )} + {isEligible && + isEligibilityChecked && + '🎉 Congratulations. You are eligible.'} + + + + + + + + cX#*T9-M^)cS&$}_uwAfB?NbZ1_|V?z~1@yf6uw+ zo%inj?ssR^H&fGHQ(axw-7~Z1*W#}Y0E)D@lsEtk3>-iJ`T>5e07L+g5D>os5&{wm z5)uj;9tIk;A;Q7K!XqQ1pdce6BcY<>V4$L5qah(<5@2HE;^O1uqhJsc6XFr$;Nj!_ zW&#EY1qBTSjQ|6KfQO2ViueC{`_%(Lg#`zI10cZ20N|)#5U60k`amLr0l=WZzySZ+ zAi=<)pkW|jLA7|G3J9=&s|5oG^#c0WG5`Sr3;>P-fdT-49d)tHh|f1^>;H?%KQL@< z?vF)J)axaD0tFlYDE|kFtqlOLJ%~^3Z?msL|B`$wD&SM%B<#v+1f0;40Fd}%YRuAkW=JYn8bP-}lLEo;gR;r@Fn zQc#{v|AN~o%=F#$SI>dRh`pHr*JJ-GITBYUq3QNt0F&Jl3-7(PY=gRfi@HAV*NQhD z1;^(go&wplUUwR+Q8(vbls9EWg|q=$=jm-)bo01FaN^L=We{s0AmKF`uV~nq9w0f86Z;} ziuzmd>)n~p1c+M_EA9hs*fw*m)Fu;mn@ZS%qfG>y>ov7Le7yHx zB5QrlyBF5Eo-8Ks+`eVDwKy@}wTI+CUn(2MFBQZ@(K=725p6QY0LA#X;=Z%tJm}IY z{w4xT@7Z%7q!eUsbG`Uxd}`~y(Iz2&S(wd_g)Z7fG+cgJ<>U2~vY*cM*u4M|^8?Y1 z{ydWW!{Mjr20lWNdYkO@rC>DHTjzVK4a7lO$=Ek0uyLd=Ymk2!-lBe6@l5xsX-&cZ zbZfE0hhc>w0o%;{^{RS;+|Wo$#$GG^!oKr<)$@|e-eIC;$L?&y>O}(}HWsp~&GCE8HnazF zudR;u8snffl~*eA$D*mYl17c4aooCbpIZS)rdbp@a2&4}oq4YKnn+G@+sv4n`BjB~ zw)1K1wemiO^SR1+h5!DUm~An$?@zX61!B5gND7)>^*NrB zYu=Da!QW=pT}S5+ta>Z|Oa?%Ew~j7=#NXl8g}+QJv#TeAn{u{CpZ-844*7-gJXJSY zM`LXD7ogEefaFDpj2;UUAB^VsvxyzyD>zRCmp^*W{=D}c0>N)-Y}VQZ2R-wbkZ)wYCf}OUnF?+VnN%KnaSwW z|Ky2K^+yYa3pa>+Vuq^;iVnzvrz-mmt%wI`Ql?lpEZt4&;- z#PTcZk$MyP%wK*JLOYjE zdwkepH@Ws1SHYa*)X_2cQE_{1hwf9J#qL=l!6`Xz#+j9pz3x<7cizRe{R?2@l&$mg zX9fRDc;h2=)Trd?rJ(7zuY=pDp%OFZzxzOF@X1X!cB zUkZu#Z-K#Tjxs3uI9r@3kH$BsK6ysy|7mZu!gl$KuaKW1h>Xu~eB#G1fXYz!A-{T# z@1_N3mk;-fiH9e1&B?OgLFfuJKJWWNpF+1c26A55vOACb6Ng_eE2D>48rKqLIjxUI zSc()#B!9Pv&(u(-lBe~%i&H%fXqOlrfBTi*N6lqUpVlDx-z{>>9mTj2oe3hB+Vc>e zlpTBMO<{YgFRwM(cq22bF9eJlvwu*bBj`*&)02pG`NEVn5$#4upT1~IBr=oh=%aJk zNgeIExIzMA1+cvznEEJR#YEB3Vp5{ruUe5erhe6UWW`P{6?0ThcSzRY1&#wRW+kw52?~SXogSP_x65GL%jv(@c z)7hkFr5C{hY2+9AFYWk>_if4K@vlCE*dZ3FFV`K+nf0YKF-K=7iwSYTHvH*rnc52H zYp)=ELlB{Wo3A(T{<&-?b&9--0Qr1MlBrfw*TmZ8lU^o72F`fg zEGHZYJ;C1s0BCg{2!lC;M>_OViETD}*j>}3W!{ywH8!WUHE(8W8bSxtr|6{)<6F$V zU)eWTa51bHE!2AGk%^YSDUlJ*88rm?&Iw%Yr5Bf;l@sNa)18hhRr2(am5uQ1-dMz`tGm48@Pm;$5| zP$Frlk32iqI#a;ti|{~}NT;6fBomubAzD{e|BC{Y5ZWZN=w&RV)svc1D-P`Dr_IF^ z@TG$#L2k(0zO^rPoh0v{7cHf_9H*?K&L~j7=D0!|yu=k=%y*i4icxDJw)x4>E_AW` zI16?*eqTI4-A?jmq{&*eQLP4*G0C{bHpx`fhGGRX+BAcV{C2FCKG#N1A=!hVyE_#? z*_UE@soeNw`9Czye+GWm{?w_QR7iA@R5<%-J#)cg`o6N$fqRLuM?X~_Saj^Af!y)# zDW~HKVHX*f;1OT{X29iu*m{!PDatkz*GnC{QbV!9{Pf;GYlj~K`=dY+F}lNkeWZQU z)ch!OE!hflEj@aYtGvdhb68UNvnUr8P4aHjN5($=e-bktDhc4M{Z*6m{R$zytiUzB z+<{O7XS86MivAn9nSvS3o$G^f*2$*cJUMi;kr{1X1@*$jox6(pnyUB z1*wcW9oBK`GcGeZWXA;aI^9pgMQ1kNrz9zwIKsiKl!T}m937s zHd+3fWq5L;XbmcZs0=Dv-+v#$FLY1QLHD)$o?cm?))*nW+`8Dh5?iCK{M$<_`sPb> z=P^@r>yt2rBO>TR=*s(PwME|pO2jMybc*ot%7^gN)ygFiaF`|9^Q~%XRE^l`4~>yG zgJT$tQLQVVG1TDcXo{8dW-AU1WCX|^=zU+Om1vW|(+g?q!7sTMV2VyDiCHo=9z*2C8}{~}JZC`sP-P>-{?LTlF=b=}Nq?KEdbZlj-xH#Y}Pk>aNG zXYpd(gQe5#rxg6yQP`m*tN8d*H-u^jT$Kzh$f0NLSM<}863P?lT^%VOKdlH1VRox_aQj!y&1q0$5Y(_fFK zQYoBacuJ*q{F+c;)E4qcxQ!lJ@jFe2eQjy;Pg#Kv!V}GM%6pPyB`oE)bGM;$I+P~= z)sI%>XbL;2hCFTxwhwVMlw9EUYe$Ub8O?NaEi;;`2E{B+-^iwd6Jqr}v6$#tL?RJZ zcn11p@>)Ls2f|wV(udoq0A3Gmmg~q7kV@bf#1GaM`yY=#eoJ&G6qxCKqtV|k!)5iM zm}_>3#@bEia))y0KxIZH*A<5DV=*1PE>=T93v-QSPb6G;dj*gnfcWPF)?9!SHW0Y? zE-ft}f78{L>!Bn@YScAp1CtA_uQ16|c`6i*VF1RYM;YIIx4)eJpd&Hn64g@&jTc}W#5Wg~MO?~>%f@1Dn! z-?$|CN#_A6!l44mBCZ%`*mYRq(2Q%uO_j25XZ5rc3P$8S3$aREye(ewVQE*pvGIxR zc;j&B9mNfyw((F&s(djorQ}=^Le>4SY!bgIRn$3CA0ZNqj>uH9slS%k)}iIlCjVcu8yRZYS)GmU{GhruC;kvGp!2TRFz>~baKCo8ECt|bUZwEhy# z65LR@DN{TZjyFjWGHM=mn*z`VM)djm8Y&F>X#@f2wF-ACr<9M7RbTlwT5yyf71Jps zDXDpamK+-0JmyRFVx-qp`g-$yDJd7om*GbaOOGk9o^6HlKw~9OCt?t#&-_ZBWfRVd z7FmTbh_NKJ>03WHBLNl^+nhvfDy+^I)Id$nRmL*L2Hj&Sm&lPeV+j(G@=e z>6JJEI9fjo`e-XuR=8HBg_gF}_=eSo& zkO&_y7W2NNqZe){_Mlyv4r7+5?}jnK2#O>v6iN8Rl^Tnb1;z|RonTyLr5{^4;vm^x z8LD4G5CBszQ>cfRm>FZr2VcdEtOwV5_eVWP5Vf8(fSCYg%Yf1HcYr( zMGImVMj*{&k%c|4!AC$tn-SJtHQb}k`*2Q@bzDkFcF{-yqHU07$)^~n#?*{oc0lN7 zMN5>}I<)~l#XQ~Qa-IlB*iF|$az7KB;Z{iZ1?ebWi^3dQ-ZNZYq?I}gAo6%tVELhRSg5Q+e z_d7faRK)~Rn<>AzE^SuPEjP+9z$}Le979(4u}LAXH$riureiw%cZuU=PUMv8CP*>t z+L9NWV|*Eh0y+?nCvCXV-^K^QDe8#!MtkG=P6t6(PoY~4-kxNR?LQ_dyhu}U?3C%vS5;U%fH}6N#I< z46YTO5f)<}_|68Z$Td`s=|CR)+XsogB8sRKJRbA2hFyH|?RUyE`^2j}B)pxXs1Hj{ z2s)>Y;eO`&hnKWG?V$=!X+*I5Mw`4SFz-yOAx_%mNg3UXNK#o;+8D83Br5h8E0|+L z)C_uJxuX9UpgvsUBG{k!z7+dm88#YW@!mVoF5Cp7q9TR+=&vBa_aZ-xraWuFNl5V@ zB(lN-8kt)oa9PU0vV5IWjCSg}x#>hZMKMK1Qx#W}X7Wixr4ZsG<{zKtI*HKX%YL+Y z5hnNStyHB-gD6)M1>fxm^jbIC?0aPyF5V|Df$=)Xw%~?M2UZbI&0GOs@r7gf%s3TL zG*?^sm}=g$FTdhG3A42I5bK>S;r)VSd){v!)Z3*%N7a%W5|$igpe2=CM~nrEg_JeNC6X;ijy1retlio#Bd*pj(qB7Jx^=lY$3|qsvRmagt(e2MY^9vPnU{7n`!7fIl}Wu%Eww zac}I_CKJD@)HgkTQ9ZKgqa@0SoV2(n!R<@I>B5e|lvHR-;6mV1Fgo$XaZ09y(n=vR zzQ**a6be79xox^IVV?&>iFl<_OxPkM{cZo1eMROfX;mxO)J+jrG?KC1WVE|LUy`rV zB1y>^NE0y>j8c^A@J7{3k0&#mnZvGWP*k6u9fgm}vQo^opthqiDZt8!{>bA8)OGD@ z7#^xc#X^CkN+oFn!w4xFmIi)IPU28}3H*vxL1z;p2G82d)s*5vVrDI{)6;}R#IpZ2 z;K5s}u*3&)7)z(ajk3s*45Wr8T8V=o)haX*Ls1EqiVG9NC6os9N3!nd>DC3FZt^@K2Iv4&@h`tA$X*j$|^A1qz~MhJK8CA0cXD0M%D(w2Eiky zD}0@(oD%YKG1F-;n%H93<~j_j*}TtW_2=i}iL;G; z_mM8!cj+u4+UCxWnYsPM#xFu;`~nak*4L^Qkt%J8n|H`qC70CIl_UoYYl%)LydXKLS=2GN!!*T)#c1@11Y?iLe_$+ zHg8%`4_q2lKIlnPcqUoBE0A#b1;DG5E25hEPGdzeV^ys1L0kE!in`UOnVsPnJo!@D zGO;J8*yOGIe81@D*-S}an##&uUP3!8F=T)suN_@0c zY3)?PM*tloobm-sV$%GU8Y#A$7~zc}bh>!Fcvkhh^J;CPA&l|B(!7D}M_SgMFCVEU zNo8~l%I3(gY{hn51XVwX_ajN0s&oMbrp=l9^fg4TCAV#bGp}HjtmDGQWn(z%Ur$e7 z%7~0JB7@M$$x9KDaYSSeju&+71ev}5^_R&@ev#iO$Lq1lOLmd5@!#mkzQ zB`%d9zn7G!RNkpjEnt%!YQVT+qR>|mxfXozFF=$OtwQDs4&tP$VMJ8Uds+`Wl-PB+ zcNvu~F7L87F_VlfffwxgDst_MVCiAX!%-DYza@P;-T8kCk>8-C%=xiGd^uK~ec;OW zgt;OyMaxMyKRqW?ep+-?u#mLikMzy4hb1!PhwGbU`Q*|9<2vye9iUpRE1mW@RlW${ z>_SQ^0Xa2OWN#sjpvaXCot{^vpr>DgV7~w&Ntc=Ph%{FB;0LG4@Nuvx@6buvKGpa8 za??cJPMiqpi3h2JZC3N48}##l&n^t}%mjX|e{{vY`V>{CN|n>+jQ{qLaDkOYK0+TL zYsW36Xf(+tl0(}fW}{OUFyq=r!X*#BOPip`rns4mn@5-@30KMF6N`j{p{ti)q8o)I zJjbiam%GcFQ4kAB8dms34-`stjmSI=Eg2GKtUgh4#>^iop{3e40N$a1QXlxwfo}7$ zPipkZhndmu26`Lt=MVanV@J?4rLpB^-DPv8t4}h#)ej5mepKSYaX1wV*ZDD*&PB@hUG!mh5%Pt8PWjHLcS7iWc zre1yT7NPPq4NOklb!;&oetf;+H=O0O0>ogM66^1gY=9n?f)b!DLkPnU{llsWLfazN zbTyP0+zmc4ga5gW<_s#=OM7}VEFaZ43aS=}9E5j}-&hK{gKwh5`UDd0J)6*tzjWI5 zO`EIEXYG^5$0I>;4(|n~aiiQYAMBCTJ=YEjQZNDv;;r4j9UIJyH~X(3Rr!%m_skkv za4-g{&Yo=M`m6GF+o^BDbxka{sFOPwD3!#Xlk5E2|2}Dl01!t9&{Oud|(d3B>L?U=tJ2_gpw~VY*N196%XhkcUhu3TxDNQnCJ=1T9i z{gC2KAi0LCoT|?q6@&Ev;}gJnRqQ=rG+3i2O8_B+N{l~71XS=i8plyfl1Af2Gzk}DcLhZp$o@RE7F z>Ds?yH(PSkOZn#xledjbBc|n_rRf)=`g1&{_a8F#gXC;_1e-+FtwXSKg_=jJy6pKb zSP#XSIiAh1{RLIYBbuy@`g|qUzoV)8)d>U=6yI%8Q3(ob)%mUv{}CO$fR=7(=l384l#QSRIg zK|IY&Z`o|R`WA(hW@5#w#KY^yHfB``wV}b{_K&ZvMl6fOB+8h3c5cpTgM{~rb(GPh{tFXk{ zmTrh&NMaK9@@g6UMJOV1K2Sw9ZA5t zmy#*@ySJ40GEDwk!zT9b7{kh#l&4~pIlPn;#Oqq9bB;bZ*WV5bHjj;%i4K{Pz@icZS^MRF<-)g9$ z)G}^`6-Gs;sgPX!lBt&lgL^G3DTR}VVcf_nPScMwXmVGdC+}a?Me$qH7wS{6!YHkh zAvG=H^ZTx9dP##+Ip?%{-ZBO)~tk!+;u;cCZVpacdSCxF&ibtDX zaI=XYyMzS?Gi~ek!>0%Gi@K=qU>*vFe_N?NJv&oY<5gPSY}}FT&xo+?xz(SoD<3lq zoG;5x{ak$~zEri`&HWJM>)hL2SpB*D5TB6Hw*A}T4Q{roV7h^;b*$bv9Z~uN%qG}1 zk&FGu#y9_s0^@_ByRY}j+X`7|!ZS%1Om9}Pp_^BA@oPYdL;~gaU=0lg3kwMW1NHmu zl;^=35(SNvm7R=5=v_QI+gl|gr??sta!z4o=YTvYMdKU{mnjaB!0vC~&oL=f3{7(X zx~~i^2)=V5?%WcE)11e%8v|p?BR+A~AU9xf*-{_tjgsiDOmY&2G4X_wuVkMzVbotB z(KXvmMZmh&ba{(?pSl+x&VO^8D^Gp-U7Y+EKrFVsN|RUOCwns^w&q%kc^B~iLa^dn zzdoAV7rk}E^edd)YeV{+^VvrU8kbLjj)J(CMlN!AS20+%Iel}KdG{rL%j_S30_5@$a`VyIE>^d5`FobbsL&iC(B z9isOt@O3ja9wrc*i3)#EV{Pt&pFRaeCr3$7=1YAoX=2u0f(U9P7N)RtT-{f^J0&{t zskb-$>{`gWI;OXZ8Oi~D&7_aAi~9=@y!CK*W!|(obw1nwm3Ay94x6ZVz7XK(HdRl> zH@m&0l?``pYq<0fu@D<{s~J^+8WQcn}Bcgi)RpedXn!bM*H<)owdUHlGU#`@a5uB=3sMm1GK z)S=Yfja0UF@(7+Nafkh(=mvl;^&`zIe+ips`TP&tKggBgS&V?J-lgvNxN;~(72l0} zvB*_PbO>iJcoxbgj&SQXF%qdwn6jBJmSvOpkm=Au!TH;=c)XTX1>aaJj(~CTU)Df(&uPdXfrXfeOYij-bz@bU{$>b`J_BMJ^0yKrAsR?_7b&Ddow0OV-IH43S) z{qVX0ETLrJN`$R8gN~9BJDEpQwF`}RlF(No z*Zq4X_i-h6J{<21zdm9Dxvy|b`?@fJv87Sx<5ncU09^%+yb5P<9;f9!k$I?1J>je- ziJBNWH07?39UahEJ0RG?X7Kpcrp(C5{4D!+(^@OzRsmpelu~xBgHtRDnrmYB!-GA#o zy4edoR}j{SkAEPck|>m>W^>C)9CaqFny7%3tF5?1nH-wGRJ2NUF)qd>kTFOsa^yoT zI719RDxCYAK_ALgO{$ zzN$oW-7f%1-w#(89zh1Rzk)FDXWbNqL#e{_E72|e*!1fUb?cbLW{q7UN)bn)h;Z{x z-Pl5GSRD?Q`>}5{OOb9bL79?M;?5!$`(uHzp7RxxGAy7))4fsK=$!((NrqXsSP;t* zQsTgR7{njp)yQ{@`MT{6eq6$u7lqB|Ck)yI@YaKXELQSYjIbe$cY4%h;%CZt#-mK*7J|JH=EpVWSo zgR$XBsXRIqc6XYCW*5mfd*{7d^ zgr+?dH=;eaMa&{&IpyV}r${M@$n)2u zZ8=kZl79s6l-d_6M|WBbLKd{q@`7BkBFwP_LTENb#ldxiNGX)S8E-Q>XXkQ3TNxpo ziF0q(-*nrEt{FA^N_$%(d2b-ud<5>R8pP8Db-uo4bstyliuHmH>0Dva{RN=3PBQF0 zvTL>{kUGiHsd?jE_82b!kL5H`bj4ySI(@+ zBqGVJaTx60JdiFG{~Y20DM<7V{O`OkbBDC>Zi)$rjkGgcwYxyKq7!4BCReQb?R$Wt zYH=O}%4pj^8&Y{A(lEc(-j!gs9yQx9Qrp~Cn4?(Ab3&xX7bD^R*c;p=A)NCwbndSZ zEcr;fydQm=_U@is)0fQsp;XS|7a+H=M>~8_y1A_(m@c2Yaw1mVbkq(`)>b7(Dqmm^ zo%Ip*7ocdG47PU4Rtsf&-$`5hCJn_43VECBZ=q$M^p^LXv?AqvHc-%)GpA0sR5QG$ zmu|ufH>C&Bf(zeROK)!=vB=wt2q8V4^dS3B^fYlaFVX>akA7S@0pA>%q zQtbW?%e0?u4U^j|;x-AkTQb;k`nDf(^frCV5l_b{?^5F~68f>FR_9G9m1I^O1c@zbC#p`P<+s{wZ#) zJZ4k+Z$BQqWh#j1X||*2t&6cXG=e6GdYH*x8R{u|W!S3u>L`V`Lk8EqFd)T(XbvLY zgQ#9-`iEOFbLf#ek*;W$l*8Sx;T*WqNtni*Rp$6J{T?WP=Uz3|HaWuOZ))o?JY~x>R~kLv>0#8K%B%+` zuTe>V@RrF+x`qbU#RN`AT|QCFEJWltfhIc(iYxU>D}Tpk@Sd|?u3sqw`FlQr<*D*1 zHjUv?sRGN+;Y0cggLiw3xa~L0dg5`n=?=<9lx@^16=}Um8)Xf#IS6pmr%E!O(Nhl7 zf7x|@QNhuU4|bCJD{L*j3=u1gCY(04!KVuh9woqxqwP%a6KS>Z)hOB-}G z+bfxMH^_ZiN7pwj)8r2|x-YW~8zd`|C|F_Q%~>Z&2-v_q+TKFZpv4UW93CZczj3asw;Z}W2zdr(e9*>jE6 zyQ^Z~^edI+o~Y7(^1tsH+pYSkaFX=jWLbokhQ@7zOJVec4p8bIAnoYK<{8VBs(cxE z1dvQ7`$AGRUNZ>ymtmC)_c8p2y^u(=^$z`vI3E!W>W-sQ$RY!)Z46PlFza{VSvX5uOC(fc&yrrxnK+Lc4*G(ms5=!zBGI< zFJ6bZuaw3ogOPT>lKk`&M&%`SWXWOgaF5P}h?)~@ExS>L^_WV=Pdf*n8T3u*3=bUy z1nz9E?;Eb%)6|rB$ziwM(?iD~APo%VZtSM>rgCp`sY$HUeb{>&EM=I+06)vavUpm( z#3T;+Mw>gHcwp)ko_CKc6+$+{GGXaHS6+(l)NGD&7VNti*Z8l*`{{;n8}H=yOjM#Y z-$k)l{zxprut9uWptqSDnSDUTz+(-EUP5cZ8Qab+HY!JIc&(xZj1}bK zu$=SG;wQq!hyZ2}4h($o;QeUs6~KBL@hwm6DrJy#z^W=$wWJnNrru1+%UJfMF!CZ} z=O744n4PzyBm8at+j@M*dI}Xg?u0;oim68xQjy(yDLjR58?D5KnQav1B6&@Tr_?v% zX>zIIo}ae9s=h*Gn&yF$fZiI8$Q6r(?D&uwWAwH+uO$}ml&pM-s~o>zId{d%K6`30 z9p_aH4a+GVz%j0CT-?bDeC8}Z$f#gh2RsaNzbyeRyC(`>7F-)~1ziso2gA3C21{FR z5Rh{k^G`LxOGx8Xf4gH4b^{{*?PFXUMBg3h=atsp3^E^|of|BD-R!FC>NgeqUjq<_ zKhV~1`V40L{V!nI!+xfo>kFe^_!4e{=~$D7d<-=_VAl$vhP)FNd;^K?7!fa8p4c^{ zQPni7=RvUD>_{CJj#?B)pxzPLq4;r=oxf~?Z%%JY?6dnE&d!~T%*12ga9Z5z+hVsR z8352tM)A>TJkze{WYS&`YlK34nB8Xpz|?Y3Cn7VVT#Rq~`qbQAo}u!hF>Ct*H%fQ_ z;;GYZU`F*Kszv9iyW!#NYKM8SLZp#4`1;8a6*ZU^D-ZlI@UyzCJb10KBW42n`|{LG zDAT?T`u|%ir?I#zweb{g8=?H4B82+<`~ZE$KfF@+l0v<2Jf8f)&r~2U;*j0?43#UB zjy?Jda6h|}Aq~ZX-e3GC)g#fQ>Cc=XI)e7z^1EAK1jEyc2B}!5d&eF&Cg@feJqqZa zI{5FqV1NFI1%^t>Dx~xs<*kuZK-@VAOHNJq)HlUHuTct;`g>+HUElX5rkBSJoR&Hg z;kd3i{{q-)QJPPRkhPXO+IK+Q+OBnV4e-%-*K@onVs3i;kzmO~W9=ez5mz!w+4XQ{ z6DsY0t4_=K0ZlW3{C!(!)v!BBk7-v%w!LS|?%x0Y0~I#&nQ>G+pG@^yYc!V?X<5;6 z_jHz^P^X7G4;2TNOeAXaR&G1D)NxCMtKGCwZ@OskAj6M(1a38Bf{NkBE(9lbChod< zojUGXBLj%@m8BdR>etDlwHyeNm!q$pZargb92NRU3J zJ|y@H$J@o}i(K)x=0}xiziXU}RZvFpQtLl=52pDpf#*DglKX_0Qn<;uVi*HH6GdG0o&DtlH%ak4sT zs#<&Fb*0L1j*A}3l1D0raEq=LT_q*ACCT`_sZY72`cE2pZqbI3D>Ubx($P1QvhJHRZ0%SV|f*o4#rLuxI?`KTlulgl))ND&@@Ozcmm4Lh{9=-p?73Yt&rG zP)VyPE4IBYtfAmxzUcT(iZ-7}TPys8^B`X2Rw(Of@SfB@HRI$7?cdn_JNJGUrd&JO zb1{C3x|OnLb#YRzV2uLLy<@)Kn-}+*&!)(!nD*~Nq>wJbERkfrAvk>Ui4CFyne5t4 z{)+^rk>lmnTZCn&@tSciX&;=DL$yQ+n9rFJbHqG5kEESi#??|TtI2x--%ad;Z>;YG zvNQyMDjx=R8c%-^!6V4|>`SP^7p7aM;*7O63Yw|GWNif=au%mf_x(UHT?r)N$WiXz zsw7vmH||+iO>})iMXf2hBbYVE(2pB_LTZ>MV72l`DVQth^ON7sNpHuEmeBnERh)F|Di zcSg8&zqiAzSJ&FX;Pc zAHrLR<}BU$c{nngn9J%khIst8$~qnN%d#grTFP7AKjJmY&sf#5^gUR)qQ+U{67*eL zS`I76Bc>h%S)aY1hU26^fOJBK8^%a@9sYCH019qWx%DfIPgn}rL>?@|0eUYg_6bpV zaa{V4rW+P+uZ9zmKmDnJj(JsV7c>=oWmxPij~~bmA5EO22oQ?DvLUS55cDKtFbOXO zNRWS*4WQx?HOY1e9q;oE$rNN=qnIK>ICwmylA_kM?%^1CGR)d{Zhk~e@080N^%qr= znV2cvYh{zB)Ju)DKSdwB4c_T9HeZ7*U2!AFcU-Ri674vpH0!sln9h5*n2owGbP>6v za~RrTtl6^yjfpn8pD)?CU`*CcyTNTUuQaywhWFI2Z2O9uK%+=)!i5sQ?@OL8YwULD z08a#W0Gg&MxE=5dHJi!1dF38Nvh<=J9G8XVs%}7F$s=~RbGjPM+XPf|+OFW_@Go@! zQrD}a8bg9cAOVGCwBRE7f_D$MqhF)cCoB0@aSW8U?DF9EXnS%PyvM&J0B=@WWR>K5 zUIpo3>EoX+tai z-lEkMelge36Q33d8a_m6k-u;q6gdsDWbMenI$_g8m+8^qB+3m=Di_M5XBeMhBZY1H zL^tltb6s?l12wB%+%KsAS=7lQz1RFRt)8(CX9geoeEHKcpUE*;fl zokt{xI&b(xeeS|e9_iJ+tL_g&^XXI(HSs=GRik<`G$|!)SmAFH>g*Lb#pRU!FeL$M z=-Aqyb!T)qugKLZ$#t6)=Ptak7V6X{+#~L!TuAMgd!{FK&P~!N3gXV<`^1B(ECbNs z=hfOUj@`^gosiM3rEktm+vKJw<&{bBY2N@-lpH$e&g zh!hcl3K=j@W<&31Eqfdho!O=pkz{6Jn5%(6Cat)|aJ1E0S(UIf=e*_~O14x`rn*hJ zGsAYMwZu@Oc;P=~R~O1*D?d%BQ}Rl{J@t%f_EpwtKG&1>n!CpE@nOqpVS7|ppoEW@ z)8$;BT_>RuMi%58JZTQCjEl(hYz8T>L=GdshMR-zxKI8B^4^oi=luY6e<&XFkhtHXG zYEeIqRB=i<$dP zb<(b0>szTju+B=7OUshhH_v@u4H4j<|EyP#Jev6q^^x>MN4^o!|K9|Tm-e5 zD+u&e-%|jezSG^teT7yDG(r2+x1;htQO0zq(8RmfBknwX#WDe}%>ekwKv6`k-d`g< zI8M`vwRis_2;I3#Rzd@|x)2nQOm{mODackazfpyPVAHau)(Y=awHQO1p`f9Buom+t z!HtZ5Efc-V(awGCmx~KKFRs{3g@e0eWHVBCw950HUIA6;1xcamoD&6Ra*h_OC5d{m6=P=-pR_qJaHaa$mQ^K+sVx98;S)t-m$ACQ$5l{Od@Qif(4 z?+?@nGisRP>!;wpmqW?S&Ds`Py#JXdn}{B||5{ZQ)r6HUMD3f>RKZ z*Tje{K(s9`1#$7{OJ&vwuwa_0qlAmX&B9=!K-xj5ILs72TU8;C!_8`b|bYB{U*!A&DhWB6S%NA=e$avu4JY<*u7S$ z7T+_HuVHU14>r=v5ik}<6A1bjtipTA>f$XH^-9h$Ejl3Hwj+wc$hJh^ouXn!<%MLA z+7;HDoMb|exgX7d1uU3dW|93V!wg1>4%8zkOwXytcMq_cq zuS7pq7B_Odq8pQU5+~SEj6*Zo)^`SSKXRelfsb}dq{w8!(4!B%J^~+emYraapeKeM zTzx{jdX2l<>VoP=Y$$Y`H8o2?sT0=J9-n$~R`=|m$M0B9Ki&sGiFQ&q4YvJ&TTCsF zoei8!BQrg=W3Wef07Q~)ZwDX0582=ir?Q&_=657C0}ofgLIU22poo$Q55*=U2=q6w z=}6GWrVb-X3FXr_g=&xQ0M^{&XM5hy_}R z_V$~SX~N{smR$b^lr2g|6`pkY8A`eVVGc%uow^!Sa&ZK4BkwY!j+Ur!Yn=r8`DA3} z$6Pt#jnKq+a9rNC<4(H2+SNv^#`_o41Jun2WLmMx^Td3jwy$a!J6byxM6N)IH;_KE zI%Fz6H4>228l``HEDx3NsX1b5wUG}DI>XSTl;-ml#_Tw3Ei;f<)2#R1i!0~aX>P(c zPa&isgkYi!#t~3B$qT)XMiUuI|7_RRG4pJIUx0;YV!yGd*@5V;z=$@=*OKS#pJU)( z(|phC`M1LW=*;juXaJeuwd1+aFF+#kEodM7yiX;*3-|@dAik~n1&AcR1wDxgBffh_ zBTuJ3?s$Le&G+m^OHwEw6=%D;wAWwPz#)#eqcyuQlBOKE;U*NUZ-Y}o|XVoik> zAJ)mh$LJW_W!p~*bWhDEpA0JmOr_?kekisp>T^Q<_)sS`jLb0u;lkqt-PdD6vA@W~ z{65jHO{!)d^qqhecoD-?I~uz9++;IK#ZW>*<`AJvlA>J)hTu|_5aseCr$s_6U;^r1 zs*jOoR+|;K_{GN1quaZBAqeMe1CW3?;}*;o&w72}8&J?g?YpHm$TgELHJkQaMm}H# z$eV2y_~zET4%Dgr0zh9Vm%)vK<3|W{%*yL{QGfUTWT!a6lm)3#GEG_J zjaLO`iZ1o;x6Zd`o=7tapooNrvmvRiDZ)e=xX!_LLlW-2LON-$A}3!UTgoD=kM zP3N~Y3WFm`>AlylMaFWsOT5Z23B)K9ExB0Yj}DPbpxtHM%WScSu~)-z-%bAsdK1SM z=asQ&JTjnwV%7xb%N&QUVzmM%3$t!wGR%2zT2$ma4`+S&`t zCi1x3+KT}lJimbsZ#?e4?FDDEd4Rs{@je^e1Gq^Ym<{V;v;`AQ#NmOuF&Ih2p!*R+ zYDUa_9W*~?Ryv*o&00$WV|-_nkO-=SS;MVLImQi08nr;G0X?n>s2zwjqYvY4H^2=4 z-lqI2coy2iJcujF`dsFm<7eOvW{I@{^VfS1a$v1BmyAWxnykWB4Xl=`Mj+55g;=v1 zNJm8$C(ewhvGeq5QIXQjYSzE9D(q1b!OJVYHq1%Q+6jR&uD%S0HCYc;oQUqWo=1g4D0G*Dk}#{v^_* ztKh*(X0huVg**fIp^WjB023~v7x}VItdL|NEJ+|NxhZNREV(U;5G0ED5a=9_+!%HR zToko-JX~H2?iIzfqvG6t{0`BD>f1QYV4zOol3cYABq{`m^h%6tpf8-<7_@R$4TMEq zR-ljpW3MSVmQ&Bh!_!bHa`NS}KU+8?jnh--DbUYZ3eltRPSru_lXvtRA+CL-duph& zi&w}=SX?C)3e%z_kC0QB{N^sk101nk1HD_&=MQ>6>-j4}fZs1KhpsK95D@lPmp=xE&wY3{Otvov&wLk?0&(;G(qKmeG>?`Ca)d%p5(a#S5E-q z^X3apFO5oY_Uqv16Di%EKlE1?r{Cz>UubYu93Vb#yx`zwkWn$=FSwBQ1jOf|zseuJ zS6%ME;HZi}{_uxjQNM$EDF#TXNkM%6e~SC+u(qCN;RJUJ?(SYBxEG4MyA>}KC|2B^ z7AsnUySuv0!YJOrb#`B$J?7YhTS_9!ls1VxTruZd0gGr2soi zPp8?YXbk;PkgGsV==hjS>mKJw}^6Zq&W7Q9YtzP1i0~no8YTlpG27XcuC!%4|+LF6uS8q41y7oe|`kgm2FkiZ* zWuJd;&ZjSM|G9Y5fig9FyXGT{S`733;GPytuhs`Yy(|UVb#6?SCu|Njx|zCW#!>Dg z5o9nGL}Xl@4Zg?=GlGXKN`dx%T}YQezTYszA0y|zP=31@G?wf#3A^=v2(8hmI*eiL z=SDRlm8ZOJT2eKy{^7}ludzbwuit-Ae8{F} z)^-@0l>w5um(c_co>vG@NG&}n2TL0%cfko&TB?hhb+b>nhH%_P}#H<8WYpfblodi)Zw!@GAK2V*8pWYvxy() zhqLQ)5C>-X!H?gy=)>YLv(`w3Lb!yV#=mENad8-X_fm_yf^q@A5)L;G@jvnsn>h9n zH$J_ROBh%rYRPn?WS42YBlmavBi+jf{zvk=*`L*(*aBn7a2ohEFpzvXM8M@kUbu4@ zL-M`?lxEY2NPSY>ZgUuB5d@f+cHC|k zr2Z1#H+R0%eTcSbQXXtKtY7D*vea{#KW6O`+Zw>}qw}G@oZ|4rlW?GNU6+g%ktv(D z1Dm0Ta3M6%d7npCHAnw!xlMSG^UW8Xp|HiQ56i`@{d%@Bq|2?Zudl%yFsK#&@;6}g z^|I6{)xYrYjyn-LkBYW(zm+@pk|0@xN=wz3%xG8wd!H{|vK$DE+;cT^d?<#mCoO)G zr=9rI%aeO6kuq%mWI=j=%E$w{WFHoNm#(KGBn!cwp~)|j3x^&dxl}XcsUkzrs&VrB z@SSiFbs>UaI0685e5a}|d|NwUsq*Z`B7Ko8clgwc`pVb0EOWjVK1zdCK!tGiDMElf zIsiVk!IsquCa)$fBaX1o49$sXqN*pxf|qZp(k~ybhPy_lXZN#H;*bK+Hi5pF=ZG>o zH<{V~Z84)0R$ZEEv2pwhUIM6LIOPO}jILzOX_n&q|F8hFLZy6#O z5*YlimG_T4#5B6@vnJ=x{fgdsy!P{;E_2!r6O!io4KOAlbfDI()m!-Q9qMd@go_Gm z23FczyJIVFcgIpWtUkOd94M|60==Iwm99@|b0UcwH$SeKF*HaYTSOQZ=+}F0dH&2Y z!68CJqW-nr9}t_%jOs(p^mFUu0txI9-@H)0{CC&}mcIILKz3t+e_vAFs^Zz$B=^IY z&hH%UW9R<|z_h*JO~6mjchAHt^_$g?wEqlnDqQiyC}5M}2~368gvOg8Z1igp>9U`X zJ++}K$EC-28u>c}62`-gR|Ji3U2(j1Fy2}07^(3|H2*zqvgT|rdWNoH z4V{vA;GN@(fIu?AafhtqB=Jx7tN z?;{70U}J76(~$KorSPeD9Cc99`f;bi`AieAVdBwS!`M1;J;y7yO~ z$I*%FYGm$Ak~)vJK*3l*>b3ERGLQNqB)cTiq=kQO3m<{Wj6K9(JeIr7h~JVq!74d=ZKSzh zVdX1*46NiZY!%G=E43-xiHmuJNK_#Hj$K0GM8Aw!_X051n>ZF)cBs1N@q=}$^1BqQ zlD1zp=6%6IFW*k9jw1U@-$_Gcr3@fY!L{%{)~OVU75hzek2iY4_w&)lnr*o-BYH3d z>c@q3Nge4!!1RC0iC6YAUFnG7C6;3elk5ME6~sVw+P6P{0F|D0Aq!L% zHaz>1?SvK-xk)(q^f#^F^Y2R{9>fc;+gth;@gDhG*P2A|fe= ztKqqHzAVe@-m<{^b6m$m>zvl*>+PLKcmem@=*bL%&wL!B4#w{n+ke)$emCb5Z&w+n z{zUi?+n%`TAV#%gzBlqyW`F#@V16T?8uQ=&Ebh<#rPb7e`SwuNm!rfk zTv^Hg&4*Me{#8GG`Ft|CRc`Dg$QLHDn^0Xg|8mda%LN59DVdLpet6IizJmGah=ZOU zTQmz@^&+HD!Hg}R_S4RNN?GuB)#;UQW|mb}c%Zr!TP*cU$Pb2^g6&(j7RB}Uyq4&} zM4sg^mIy}G>cP$-wZjP20`2o3=F!3~6ftnGhCr-R7u!wkG9jjJDwjXcM=nIXL<>r> z?v5YcM=iHqxut`On)?s2DGQa0`;$oI-6s? zcDH1&3d>TbYrQ&#K0>PD(5G+5mX?dAI*B$R4Vl`+g`=y7rmKhLo{FW8miC=xw8IC- zk8HIvDRR{#_-Ryi_f%qN414U!aOZ+4~4)o-aQd1wU_rwQ~#qRX=f7F5iS8EV`U z7h6Y8j4W^2YkmIv8JNv!C@ekgp0u$%(2)}@Ei%@yCD!m}JY3jkxav(fXG7eOoNQD( z3M4A1V~|RNPTH_}gEkEY8z~S^o{)89qS5>1$5Pcq+CFQAh3-ffH=nwt)T*3Q?zb4^-W}|)=CfNI*N#^Csii@OgddMNQKS9SLFkW%*U zAHPxp8%LN<2);#w$y=`hj$yFDz-qrJ=f36v-Z7gMLK=jCZ7jC=df%#j23xQi?&8SP zAMx)dxo<0_9GVAAD&+@6L)(VK9g-bPPjp(?e*?5&J}!HfmYzE=^-_r&jiHC$&993w zNBVx4qcBqe4k3Dl>Uw|M@s69CqGcp5MrcUIRsk#v8eK4}%82Ljb3xotFnfwjU6I7B zjmU3c5>#1!0)0|q)3Q|b#)ACsg?F9!!2BjhbNl5eq3~2Ts;y)iujKXp^?SE9;n2NF zikppF@ETfTO}AON>nBO(wqVS2?4rx%#P5YjB0%N1VI-#6k3&R@KMLgAzLiqktg|Q% ze<@zKIwJg{=BxH<^_Mzr3V-|p*McX8n5@D$#KcnRWqH^SS2QL*)bPdNbWqYsW|8Ht z5ChYafbz)bJ7ta9w^@nM#PEM74!@KCTrA;MZ9pkJnniUn=cOO}CB@-LzRzX5PA?8l zQHzp%;jMh^rE**J4k?F5WfOUy>6+6SiS2~&@u^{4!&s{A+{EIMh?Ofh_3BD*(l>Xo zI0ErzmXg03zC?_HlrnYR4DW~OPm%|6mEToPol8IRg+~9_Px!wu_AhQR{&GuTrz|&w zf-YTjx@u8=LF!l5Pw<4bOmfo9W$&tyc`h*V{r`Eo#3)852=k+Z&lUL%IAmd*znI7p zTO=8tz}TkM4Fl6<%#&;uj zfn*c-17^T+;C}7p@lDA@TRIymCcj6hf`4@m!0~9TkJKO8uw4wohoiF8Jfyb)e#8ix z4E$-qS>LdKCzQhJf=F8uB`<5iQovxFvq67%^j=yK*rl#H39~BgGCmu}+y+byP*OZ* zr_(1T-`enUb$@v%KO~EVLf_c?HktCBnj_XpF90U!z}}=0{|DT>1Ev}JAY+B&N}Fr$ zO|Z07sc@!Cqo-kWg&`Jv$Aw0BsxX#AVvfOyABZ!*W*;G{-2XG;a`PWK5!_k&$oDVK z&obCJP0uCYi&|^9>9=p2V^tWy2q58_X zu&T|d8-8p`>81Nm$HIsdkgh&QE9rK-A>J7+u$z2D>r#TQ$E2+*5_R||EtArCXH?^@ zN>n{NR{OGin4c-7V-rjER)9P!eSY-HpZQX9EqcJhGfGT5ig#4jVW@VoYeCj!A?mV< zD{!o+#LV{<5v-mAUru&0+|7wQtZvDiJG)Vfm)tyTTE&pQR<#82o$%5#&G@pqXT!!% zt#7XM&;w!*WlK46cy*0%wdt^f8+N!|gC-iizcys=cxZzPQk?X7^G&m38R$ye3|D%G zSBCi(on^kcmmAt~yMtjW2N4B7S`i7JZKH(g2UzgZ`zWX{R3@p7gsW%;zi`e- z(?bXOq@<@E?OEo|p6C2B$+g>l0kNs>MRu?1_l^(4Cqwor#c_o`Sk9qx2%`>U2jrG| zLjiPf=#107%r8)2%l#qr@ifR}8Hq7mF4JP_5k9$n60K)653cY4F#wlbAU@y~VuHpbT&JfIOcELBQylQ*v z6uPA%^jBIoHq)%Vg7V@&kZKkr*Uc-8Q;2O2whnk;Qc3|Egkle{rqjnL%cMlX9N0Rf zTr4Dq^b3AyC^tIHkvNvK^Ko_EF83tAm@E@HUE>7oNHuohmGoef+>)>bm>KvGL^!sr z9)jBi`xJI0RUA;7&LZ$!RiMV(4mVGd+sY`bqq6gosJWx>u=>LP;gHTAG(YoP`yAs-0t)q{sImhBtvRyO9Q%iAR2_#@?-I+cD2tF_NV(ThkFx*UhbO4)!(>=`oK;2brd zzEKXz^5B`2g0lmc8SS63`Lva9%$dOI!=2Ug21W_aPcNzWi3wfK(ufP@q)XYf=YLTd zw{ALJZaH*|hLV^6X)gFTS1ys+ra7b~Gvhkw-7w(&t)DJU0}ea1vV3{rshz~xNfz9< z&&j5pNl2h z!^mX(^f`~bqd{o5E8!>7g{^@hH#;EqaX{{6AG~sk-ykP-e@&{4Ih-~={?>Od=Rh?a z&45K>rCR?pRt!xi+&_1>C;H&8UQRY`;dyj-FwD&OSp8uk}3_-?cp6NE20nmz9Dt`X$YaiC3DwZsuB0~1j%cX#{a~UJ0hb8weUrF@6u}>&oX$DYB1-a_!LZXY(`zZIexqA zeE*Mv!cFtxIZz#Ik4jGy2E?ZAXJ%lJIDDzo^YR#)#tgdXoVxiZQc}ITt zn@-Xd+?W=8N$Vhpx$~4MQ5crDZ*Y$Hg*n|2GFQxJhTe}E(Fnh4zf0PM_BGl+cVpZy z@sF($^ozug0h2n;_RVQbu;PUHZ-4~4DiGc!V%78riFYBdWx5Gz*iQ1he7yT9acrrE zrPXjJEYwP1=?*YsovdSQ*GT;(w^y44z}AFVe1b-<_qLNf>TtxDJ$L%#PA1`P6xCKC zoak8f5B^g3&A$K^mO1Z=z8PE9aKfMYn}J9a)dO6x-&bY!o9R1ih>h4a=P_znhaod8FYJfMFSQ<@{5k+CZ^Lp&T&c(B$7u?_#> zPn@9_{>i7%dM0_yhh+Q9ZXx0vx_{U$f>|8?<9VpTmWm@@h2xeSQTL zAO9K;t-3l5RV$J72mgdwM^Z%K@@t&3n1;qpzF`kFjsfp?Fi~&IWo}`8U{7FI4pz?7 zug;I(J{A6bMu(a<1!D^utT1#@*~;oob6&~WcCbB$v20#Awx0T36()q$zo6+RYj}+J zO&9dZ`VFW#Auf25vgcWI;ozFEzfFlh865XjBeP$5z?IlktM+<*132FEBv>^26#Te` zLqo`@-(iPq1lRiYBG&WA13wwf-zpdKLpa>n9fJzFO8$uK=upT@)&+FB6L zwn+0<{G0HJdjn^J&@IrO%J#G|Wf|a{6HDGFk;M^0c8)s4=@@%yiZ-Gy&aYQn{GJ;x zg8h~A_KFv2Um-`RZ6P8|eZ2wLX@v4cE;^?YUg7YOr)9y#ujc#OmsZItPCiu0a zn)%drfLOd|r)lMWKLUw{h?$imYxif(wyGYy?e&G-?W1QCKkQeA??y3*jRP{NBiF*-jJs?e^VKY2y|UBrAtK8Oaw7uMtMxpor3moqz^l&EK5(Ay=_KH3{||{zKQm z_h?N@IG@sVFtEOE?un*-6X;@4Z&MO z^e}DnAu!!b%U>DG<^?vTt8;k-*+yxBCEko(J-k~`b^NFF72m!0FJfoyYddFyhE82> z`gQVU!s?9ZGB~Md3vAL247{|YSF+#6uTi@v<-f~(+Emd5YHMnSCbcZ<%6UY}GG*Kb zhGrB}-P4nUa-AMm3lrE0A`|hAvZyun?^E^F?@9*(aM0M;wV;tTr3!4JC7=s*ucmt^ z3aRJzhJRz_91j=6{hdgU+(S@ZS}$8Mt9lby96tp+s%`cgOB@OrLCmNDtoazm6uXYC zSFstqX-brSSvkCWzdf*LK_Kdqy?%~omdDjj9V6-zs4cf> z#u-z4Rmdv^=RD8eP}*9`p&mcSp=BU=>N1c!b@6ExCyvT=GsA~JWKPSv$hxtsn|EV< zVK4nr@!6s^$#~Q8>QO|$^;j@DI_H}}c@C@tZ<{gcb(H))H%rjb!zeR;ZKcF6r&E(n z6x%u}yy_SZh=9ASxGB~oA1TaN1)lt)J{w4FqLZ9{9RwmL9+eUTdl)0Y94P^0la5K#1JGb z9igUBQn+g1mTJtD05o2Jl`ehEXI|cj#h0mtG)}z zV@WE`g~OF)lzOpdhXf>cp@1*W zRA#>`PKd;ePcn$vqwKy!6?d)c91X45W!jpTfqUYj4QOBOKEp@99Nx3Md*1-{p&$C) zO73b?yn+|vqyd!^*2oE_AKqsK^RwBX(E&s3*=~ErDYId1B$IX)->MkInBG4(sij>O zrZfgU#^VRJS=S+AN55K^4s3aF*F^y;?sq_8XV=KJ=YO+%pbtu95=34js_1{3lA z5xVs|Bab3Qi>ly(z8m*#5GG#K8RijEwO7>@u1=@Q38=0d5=cfz(iQ3aM0U(~0Wzs}7(< z5~eu54xog`?=lf^#9Ugi>6khz{o1BfO4G$DrDbH2yPa=QPI6!K$DP&3U4Q0~NR4>O z^Wk6)Bs1NN`DW-!OAE~LMYMk(p`~T6X_|*Ub$Kt84D-u~-krHqo8@d*uH8^R(ORjM ze%2~cCH6Z&IGOqGES`m{$L%=}<_RvWxC2GFYm&wFv%{=kd*(&PDPpOJ9CMjB=Zn4Q zH|Lo5NElUZxKj*F%do{AIYK@?=bNW6^8?KL=`|~_FDnIyo|VcUYA@tfP5p6}JdL=Q zzrr+3LqD=|s7;%s3_Y6fyu9ezdC&h-xN1w(<5xV?Hety6B@68s6DOV+Vt=WC?y?y! zDL_w9ARfAWk7mbzWWyi(%75qm=bsb|PbwUD@!`;WvVW;yK)>Ss6Z^YHHYj#3hps%g ze+rBI%BF|j>dz3?mDp!8vRAdE+thSdpoB+Rco01hbI^Wy>x4ZcIArp71iFO6+B0r^ zWI>@b%?#l5EW?ji51;wEN|PWDAONU&@w8zUSi=)K@mdkiOB%;}z*bN!=DyIZiy|WL zQ1r%rY@~*71rM4N$zc{&fbL^_O-guu69DyRB(v+Mh9YDZN6#`?9-6<_Q5p**W3o>S zFbHhLVOf_Q}HG9evi#8uU@e_{mO`K`g+O%5xJ9*{OC0isfd}xN=`3 z8mL|9@40=DOkJ45B9;$iM8)oXS&eM;B;g~i$@#!^hBXpS3{!T}F}2EyGQiL7!WD~_ zz)E6&WY%}DpL=v8QH_Kt6lthuMT-Np4F7HmmL|R`Qo-On;Op zLrsTnkX@r6-nwNtuaiCoBSRlpd(noMYe-h|z3Sdww^}7e#5Y})cg!qaoL`-|T|R6_ zT%D)ao0`w7I25-KZZBiC1VzaP7X^}RT=*{7On*$MrC^1biN4VELqvgg0S?r8S_{r2 zRRYMbS(l0-Nzp73@bz29B6M-Dd(U<4OQ9_qU-qLLPCIrR9p%t|>OqHJ z>Q&C)5!D0lq#O}Q+^0S%HH<&4QBYS*7o0oE+Sy(^cHJAn8B|oYtZGOS%Xwb9zC7$O@EUHgz3Uo`iOjeSOV-y~t^!Xc%lDtrLmZ7do+Yjt5>-*_*V zMuR>$bdJ_g_s$?tTW1iT)2HQ^U*gnp}iQ zI49rZdIPO}UyU+ETX_Nv=c2GjqqWwoKiQKN9V}orsXF2>@}Kf%i7q?^QmDn3+=zxx z^``faQ8R0=E149`m}1%JHXS@syvyauE^&!UX4WiGFOZK`ST^3Mq$8wDO}P(L$+5?Y zG!ZYC_k~HZ>c9_E6U||Q>v3ea(*c*LLk?tq14iPY)BZy<1(-uD67a1dmiy&%_8-0; z{wWDsV6lom;0uw`G#APWn=UFdglh+FzXNXs0#uH*SgIuY0CJ^ zs-J7kw3TF~dz~c+X69cHc-0!Q1dW?tN8I{t*R5vw+%sF_{RYToM;@!UOnTZh8Re}U z&wy|`m<#4=b}^@L(^Foe+JwU@7HEW_LCOFJW5MhVAqZ7Px)ztEdK{Dywx+Ms*%wMu Wu#upD_`+F+5SU{MF75ig^uGY>d?}m& literal 0 HcmV?d00001 From e3e730efe83bbe98f13d0e8cedb537f6f2040501 Mon Sep 17 00:00:00 2001 From: EjembiEmmanuel Date: Thu, 19 Sep 2024 13:30:52 +0100 Subject: [PATCH 12/12] feat: ui and minor fixes --- src/abi/nft.abi.json | 845 ++++++++++++++++++++++++++++++++++--- src/app/community/page.tsx | 69 +-- 2 files changed, 816 insertions(+), 98 deletions(-) diff --git a/src/abi/nft.abi.json b/src/abi/nft.abi.json index 8eeba6d9..29b1968f 100644 --- a/src/abi/nft.abi.json +++ b/src/abi/nft.abi.json @@ -1,118 +1,823 @@ [ { - "name": "name", - "type": "function", - "inputs": [], - "outputs": [ + "name": "NFT", + "type": "impl", + "interface_name": "defispring::nft::IDeFiSpringNFT" + }, + { + "name": "core::byte_array::ByteArray", + "type": "struct", + "members": [ { - "type": "core::byte_array::ByteArray" + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" } - ], - "state_mutability": "view" + ] }, { - "name": "symbol", - "type": "function", - "inputs": [], - "outputs": [ + "name": "defispring::nft::Settings", + "type": "struct", + "members": [ { - "type": "core::byte_array::ByteArray" + "name": "maxNFTs", + "type": "core::integer::u8" + }, + { + "name": "minEarnings", + "type": "core::array::Array::" } - ], - "state_mutability": "view" + ] }, { - "name": "mint", - "type": "function", - "inputs": [ + "name": "defispring::nft::IDeFiSpringNFT", + "type": "interface", + "items": [ { - "name": "nftId", - "type": "core::integer::u8" + "name": "name", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "name": "symbol", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "name": "mint", + "type": "function", + "inputs": [ + { + "name": "nftId", + "type": "core::integer::u8" + }, + { + "name": "points", + "type": "core::integer::u128" + }, + { + "name": "hash", + "type": "core::felt252" + }, + { + "name": "signature", + "type": "core::array::Array::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "upgrade", + "type": "function", + "inputs": [ + { + "name": "newClassHash", + "type": "core::starknet::class_hash::ClassHash" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "set_settings", + "type": "function", + "inputs": [ + { + "name": "settings", + "type": "defispring::nft::Settings" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "get_settings", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "defispring::nft::Settings" + } + ], + "state_mutability": "view" + }, + { + "name": "set_pubkey", + "type": "function", + "inputs": [ + { + "name": "pubkey", + "type": "core::felt252" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "get_pubkey", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + } + ] + }, + { + "name": "OwnableImpl", + "type": "impl", + "interface_name": "openzeppelin::access::ownable::interface::IOwnable" + }, + { + "name": "openzeppelin::access::ownable::interface::IOwnable", + "type": "interface", + "items": [ + { + "name": "owner", + "type": "function", + "inputs": [], + "outputs": [ + { + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "state_mutability": "view" }, { - "name": "rewardEarned", + "name": "transfer_ownership", + "type": "function", + "inputs": [ + { + "name": "new_owner", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "renounce_ownership", + "type": "function", + "inputs": [], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "name": "ERC1155MixinImpl", + "type": "impl", + "interface_name": "openzeppelin::token::erc1155::interface::ERC1155ABI" + }, + { + "name": "core::integer::u256", + "type": "struct", + "members": [ + { + "name": "low", "type": "core::integer::u128" }, { - "name": "hash", - "type": "core::felt252" + "name": "high", + "type": "core::integer::u128" + } + ] + }, + { + "name": "core::array::Span::", + "type": "struct", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "name": "core::array::Span::", + "type": "struct", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "name": "core::array::Span::", + "type": "struct", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "name": "core::bool", + "type": "enum", + "variants": [ + { + "name": "False", + "type": "()" }, { - "name": "signature", - "type": "core::array::Array::" + "name": "True", + "type": "()" } - ], - "outputs": [], - "state_mutability": "external" + ] }, { - "name": "upgrade", - "type": "function", - "inputs": [ + "name": "openzeppelin::token::erc1155::interface::ERC1155ABI", + "type": "interface", + "items": [ { - "name": "newClassHash", - "type": "core::starknet::class_hash::ClassHash" + "name": "balance_of", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "token_id", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::integer::u256" + } + ], + "state_mutability": "view" + }, + { + "name": "balance_of_batch", + "type": "function", + "inputs": [ + { + "name": "accounts", + "type": "core::array::Span::" + }, + { + "name": "token_ids", + "type": "core::array::Span::" + } + ], + "outputs": [ + { + "type": "core::array::Span::" + } + ], + "state_mutability": "view" + }, + { + "name": "safe_transfer_from", + "type": "function", + "inputs": [ + { + "name": "from", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "to", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "token_id", + "type": "core::integer::u256" + }, + { + "name": "value", + "type": "core::integer::u256" + }, + { + "name": "data", + "type": "core::array::Span::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "safe_batch_transfer_from", + "type": "function", + "inputs": [ + { + "name": "from", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "to", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "token_ids", + "type": "core::array::Span::" + }, + { + "name": "values", + "type": "core::array::Span::" + }, + { + "name": "data", + "type": "core::array::Span::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "is_approved_for_all", + "type": "function", + "inputs": [ + { + "name": "owner", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "operator", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "name": "set_approval_for_all", + "type": "function", + "inputs": [ + { + "name": "operator", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "approved", + "type": "core::bool" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "supports_interface", + "type": "function", + "inputs": [ + { + "name": "interface_id", + "type": "core::felt252" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "name": "uri", + "type": "function", + "inputs": [ + { + "name": "token_id", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "name": "balanceOf", + "type": "function", + "inputs": [ + { + "name": "account", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "tokenId", + "type": "core::integer::u256" + } + ], + "outputs": [ + { + "type": "core::integer::u256" + } + ], + "state_mutability": "view" + }, + { + "name": "balanceOfBatch", + "type": "function", + "inputs": [ + { + "name": "accounts", + "type": "core::array::Span::" + }, + { + "name": "tokenIds", + "type": "core::array::Span::" + } + ], + "outputs": [ + { + "type": "core::array::Span::" + } + ], + "state_mutability": "view" + }, + { + "name": "safeTransferFrom", + "type": "function", + "inputs": [ + { + "name": "from", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "to", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "tokenId", + "type": "core::integer::u256" + }, + { + "name": "value", + "type": "core::integer::u256" + }, + { + "name": "data", + "type": "core::array::Span::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "safeBatchTransferFrom", + "type": "function", + "inputs": [ + { + "name": "from", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "to", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "tokenIds", + "type": "core::array::Span::" + }, + { + "name": "values", + "type": "core::array::Span::" + }, + { + "name": "data", + "type": "core::array::Span::" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "name": "isApprovedForAll", + "type": "function", + "inputs": [ + { + "name": "owner", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "operator", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, + { + "name": "setApprovalForAll", + "type": "function", + "inputs": [ + { + "name": "operator", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "approved", + "type": "core::bool" + } + ], + "outputs": [], + "state_mutability": "external" } - ], - "outputs": [], - "state_mutability": "external" + ] }, { - "name": "set_settings", - "type": "function", + "name": "constructor", + "type": "constructor", "inputs": [ + { + "name": "name", + "type": "core::byte_array::ByteArray" + }, + { + "name": "symbol", + "type": "core::byte_array::ByteArray" + }, + { + "name": "base_uri", + "type": "core::byte_array::ByteArray" + }, + { + "name": "owner", + "type": "core::starknet::contract_address::ContractAddress" + }, { "name": "settings", "type": "defispring::nft::Settings" + }, + { + "name": "pubkey", + "type": "core::felt252" } - ], - "outputs": [], - "state_mutability": "external" + ] }, { - "name": "get_settings", - "type": "function", - "inputs": [], - "outputs": [ + "kind": "struct", + "name": "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred", + "type": "event", + "members": [ { - "type": "defispring::nft::Settings" + "kind": "key", + "name": "previous_owner", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "key", + "name": "new_owner", + "type": "core::starknet::contract_address::ContractAddress" } - ], - "state_mutability": "view" + ] }, { - "name": "set_pubkey", - "type": "function", - "inputs": [ + "kind": "struct", + "name": "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + "type": "event", + "members": [ { - "name": "pubkey", - "type": "core::felt252" + "kind": "key", + "name": "previous_owner", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "key", + "name": "new_owner", + "type": "core::starknet::contract_address::ContractAddress" } - ], - "outputs": [], - "state_mutability": "external" + ] }, { - "name": "get_pubkey", - "type": "function", - "inputs": [], - "outputs": [ + "kind": "enum", + "name": "openzeppelin::access::ownable::ownable::OwnableComponent::Event", + "type": "event", + "variants": [ { - "type": "core::felt252" + "kind": "nested", + "name": "OwnershipTransferred", + "type": "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferred" + }, + { + "kind": "nested", + "name": "OwnershipTransferStarted", + "type": "openzeppelin::access::ownable::ownable::OwnableComponent::OwnershipTransferStarted" } - ], - "state_mutability": "view" + ] }, { - "name": "set_token_uri", - "type": "function", - "inputs": [ + "kind": "struct", + "name": "openzeppelin::token::erc1155::erc1155::ERC1155Component::TransferSingle", + "type": "event", + "members": [ { - "name": "uri", + "kind": "key", + "name": "operator", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "key", + "name": "from", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "key", + "name": "to", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "id", + "type": "core::integer::u256" + }, + { + "kind": "data", + "name": "value", + "type": "core::integer::u256" + } + ] + }, + { + "kind": "struct", + "name": "openzeppelin::token::erc1155::erc1155::ERC1155Component::TransferBatch", + "type": "event", + "members": [ + { + "kind": "key", + "name": "operator", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "key", + "name": "from", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "key", + "name": "to", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "ids", + "type": "core::array::Span::" + }, + { + "kind": "data", + "name": "values", + "type": "core::array::Span::" + } + ] + }, + { + "kind": "struct", + "name": "openzeppelin::token::erc1155::erc1155::ERC1155Component::ApprovalForAll", + "type": "event", + "members": [ + { + "kind": "key", + "name": "owner", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "key", + "name": "operator", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "kind": "data", + "name": "approved", + "type": "core::bool" + } + ] + }, + { + "kind": "struct", + "name": "openzeppelin::token::erc1155::erc1155::ERC1155Component::URI", + "type": "event", + "members": [ + { + "kind": "data", + "name": "value", "type": "core::byte_array::ByteArray" + }, + { + "kind": "key", + "name": "id", + "type": "core::integer::u256" + } + ] + }, + { + "kind": "enum", + "name": "openzeppelin::token::erc1155::erc1155::ERC1155Component::Event", + "type": "event", + "variants": [ + { + "kind": "nested", + "name": "TransferSingle", + "type": "openzeppelin::token::erc1155::erc1155::ERC1155Component::TransferSingle" + }, + { + "kind": "nested", + "name": "TransferBatch", + "type": "openzeppelin::token::erc1155::erc1155::ERC1155Component::TransferBatch" + }, + { + "kind": "nested", + "name": "ApprovalForAll", + "type": "openzeppelin::token::erc1155::erc1155::ERC1155Component::ApprovalForAll" + }, + { + "kind": "nested", + "name": "URI", + "type": "openzeppelin::token::erc1155::erc1155::ERC1155Component::URI" + } + ] + }, + { + "kind": "enum", + "name": "openzeppelin::introspection::src5::SRC5Component::Event", + "type": "event", + "variants": [] + }, + { + "kind": "struct", + "name": "openzeppelin::upgrades::upgradeable::UpgradeableComponent::Upgraded", + "type": "event", + "members": [ + { + "kind": "data", + "name": "class_hash", + "type": "core::starknet::class_hash::ClassHash" + } + ] + }, + { + "kind": "enum", + "name": "openzeppelin::upgrades::upgradeable::UpgradeableComponent::Event", + "type": "event", + "variants": [ + { + "kind": "nested", + "name": "Upgraded", + "type": "openzeppelin::upgrades::upgradeable::UpgradeableComponent::Upgraded" + } + ] + }, + { + "kind": "enum", + "name": "defispring::nft::DeFiSpringNFT::Event", + "type": "event", + "variants": [ + { + "kind": "flat", + "name": "OwnableEvent", + "type": "openzeppelin::access::ownable::ownable::OwnableComponent::Event" + }, + { + "kind": "flat", + "name": "ERC1155Event", + "type": "openzeppelin::token::erc1155::erc1155::ERC1155Component::Event" + }, + { + "kind": "flat", + "name": "SRC5Event", + "type": "openzeppelin::introspection::src5::SRC5Component::Event" + }, + { + "kind": "flat", + "name": "UpgradeableEvent", + "type": "openzeppelin::upgrades::upgradeable::UpgradeableComponent::Event" } - ], - "outputs": [], - "state_mutability": "external" + ] } -] +] \ No newline at end of file diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx index 9dadc65e..6fa7141a 100644 --- a/src/app/community/page.tsx +++ b/src/app/community/page.tsx @@ -24,6 +24,7 @@ import { Container, Link, Progress, + Spinner, Text, } from '@chakra-ui/react'; import { useEffect, useMemo, useState } from 'react'; @@ -39,8 +40,8 @@ interface OGNFTUserData { const isOGNFTEligibleAtom = atomWithQuery((get) => { return { queryKey: ['isOGNFTEligibleAtom'], - queryFn: async ({ queryKey }: any): Promise => { - const address = get(addressAtom); + queryFn: async ({ _queryKey }: any): Promise => { + const address = get(addressAtom) || '0x0'; if (!address) return null; const data = await fetch(`/api/users/ognft/${address}`); return data.json(); @@ -75,11 +76,12 @@ const CommunityPage = () => { process.env.NEXT_PUBLIC_OG_NFT_CONTRACT || '', provider, ); + const { writeAsync: claimOGNFT } = useContractWrite({ calls: [ ogNFTContract.populate('mint', { nftId: 1, - rewardEarned: 0, + points: 0, hash: isOGNFTEligible.data?.hash || '0', signature: isOGNFTEligible.data?.sig || [], }), @@ -101,11 +103,15 @@ const CommunityPage = () => { if (ogNFTBalance && Number(ogNFTBalance.toLocaleString()) !== 0) { setHasNFT(true); } - - console.log('ogNFTBalance', ogNFTBalance); - console.log('isOGNFTEligible', isOGNFTEligible); }, [ogNFTBalance, isOGNFTEligible]); + useEffect(() => { + if (address) { + isOGNFTEligible.refetch(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [address]); + function copyReferralLink() { if (window.location.origin.includes('app.strkfarm.xyz')) { navigator.clipboard.writeText(`https://strkfarm.xyz/r/${referralCode}`); @@ -148,16 +154,16 @@ const CommunityPage = () => { Community Program Earn points to level up and collect NFTs that signal your loyalty. @@ -264,7 +270,7 @@ const CommunityPage = () => { display="flex" margin="40px 0" gap={{ base: '15px', md: '30px' }} - padding="20px 20px" + padding={{ base: '10px 10px', md: '20px 20px' }} className="theme-gradient" borderRadius="10px" > @@ -272,22 +278,26 @@ const CommunityPage = () => { OG Farmer Limited edition NFT - - {`${progress}/100 Selected`} - + {isOGNFTLoading && } + + {`${progress}/100 Selected`} + + div': { @@ -306,6 +316,7 @@ const CommunityPage = () => { background="purple" borderRadius="5px" marginRight={{ base: 'auto', md: '0' }} + height={{ base: '30px', md: '40px' }} _hover={{ bg: 'bg', borderColor: 'purple', @@ -316,11 +327,13 @@ const CommunityPage = () => { isDisabled={hasNFT || isOGNFTLoading || !isOGNFTEligible.data} > - {hasNFT - ? 'Claimed' - : !isEligible - ? 'Check eligibility' - : 'Claim'} + {!address + ? 'Connect wallet to check eligibility' + : hasNFT + ? 'Claimed' + : !isEligible + ? 'Check eligibility' + : 'Claim'} @@ -350,7 +363,7 @@ const CommunityPage = () => { @@ -360,11 +373,11 @@ const CommunityPage = () => { display="flex" flexDirection="column" gap="10px" - padding="10px 20px" + padding={{ base: '10px 10px', md: '20px 20px' }} className="theme-gradient" borderRadius="10px" > - + Your Stats { zIndex: -1, }} > - + Coming soon You will be able to check your points and claim your NFTs here soon.