diff --git a/.gitignore b/.gitignore index 4ff78fd..cda4568 100644 --- a/.gitignore +++ b/.gitignore @@ -72,4 +72,8 @@ yarn-error.log graphql-types.ts # Auto-generated by gatsby-plugin-extract-schema -schema.graphql \ No newline at end of file +schema.graphql + + +.bin/* + diff --git a/config/settings.json b/config/settings.json index ece8b06..db8c3df 100644 --- a/config/settings.json +++ b/config/settings.json @@ -1,7 +1,7 @@ { "author": "@TenKBay", "siteUrl": "https://tenkbay.com", - "contractName": "v2.tenk.testnet", + "contractName": "nft.cheddar.testnet", "social": [ { "href": "https://discord.com/invite/UY9Xf2k", diff --git a/lib/locales/Locale.ts b/lib/locales/Locale.ts index 018541a..38142ae 100644 --- a/lib/locales/Locale.ts +++ b/lib/locales/Locale.ts @@ -7,76 +7,76 @@ /** * Commonmark-formatted markdown. @see https://remarkjs.github.io/react-markdown/ */ -type Markdown = string + type Markdown = string -// TODO: should be able to import Action from ./runtimeUtils, but currently breaks with typescript-json-validator -// import type { Action } from './runtimeUtils' -type Action = "ADD_TO_CALENDAR(SALE_START)" | "ADD_TO_CALENDAR(PRESALE_START)" | "SIGN_IN" | "MINT" | "GO_TO_PARAS" - -export const requiredHeroFields = [ - 'title', - 'body', - 'cta', - 'action', - 'remaining', -] as const - -export const optionalHeroFields = [ - 'backgroundImage', - 'backgroundColor', - 'image', - 'video', - 'ps', - 'setNumber', -] as const - -export type Hero = { - [K in typeof requiredHeroFields[number]]: K extends 'action' ? Action : Markdown -} & { - [K in typeof optionalHeroFields[number]]?: string -} - -export const userStatuses = ['signedOut', 'signedIn', 'vip'] as const - -type HeroSaleStatus = Partial & { - [K in typeof userStatuses[number]]?: Partial -} - -export const saleStatuses = ['saleClosed', 'presale', 'saleOpen', 'allSold'] as const - -export type RawHeroTree = Partial & { - [K in typeof saleStatuses[number]]?: HeroSaleStatus -} - -export interface SectionI18n { - text: Markdown - image?: string - video?: string - backgroundImage?: string - backgroundColor?: string - className?: string - blocks?: { - image?: string - text?: string - linkTo?: string - }[] -} - -export interface Locale { - viewIn: string - langPicker: string - title: string - description: string - calendarEvent: string - connectWallet: string - signOut: string - new: string - myNFTs: string - nextNFT: string - prevNFT: string - close: string - zoomIn: string - zoomOut: string - hero: RawHeroTree - extraSections?: SectionI18n[] -} \ No newline at end of file + // TODO: should be able to import Action from ./runtimeUtils, but currently breaks with typescript-json-validator + // import type { Action } from './runtimeUtils' + type Action = "ADD_TO_CALENDAR(SALE_START)" | "ADD_TO_CALENDAR(PRESALE_START)" | "SIGN_IN" | "MINT" | "GO_TO_PARAS" + + export const requiredHeroFields = [ + 'title', + 'body', + 'cta', + 'action', + 'remaining', + ] as const + + export const optionalHeroFields = [ + 'backgroundImage', + 'backgroundColor', + 'image', + 'video', + 'ps', + 'setNumber', + ] as const + + export type Hero = { + [K in typeof requiredHeroFields[number]]: K extends 'action' ? Action : Markdown + } & { + [K in typeof optionalHeroFields[number]]?: string + } + + export const userStatuses = ['signedOut', 'signedIn', 'vip'] as const + + type HeroSaleStatus = Partial & { + [K in typeof userStatuses[number]]?: Partial + } + + export const saleStatuses = ['saleClosed', 'presale', 'saleOpen', 'allSold'] as const + + export type RawHeroTree = Partial & { + [K in typeof saleStatuses[number]]?: HeroSaleStatus + } + + export interface SectionI18n { + text: Markdown + image?: string + video?: string + backgroundImage?: string + backgroundColor?: string + className?: string + blocks?: { + image?: string + text?: string + linkTo?: string + }[] + } + + export interface Locale { + viewIn: string + langPicker: string + title: string + description: string + calendarEvent: string + connectWallet: string + signOut: string + new: string + myNFTs: string + nextNFT: string + prevNFT: string + close: string + zoomIn: string + zoomOut: string + hero: RawHeroTree + extraSections?: SectionI18n[] + } \ No newline at end of file diff --git a/lib/locales/runtimeUtils.ts b/lib/locales/runtimeUtils.ts index 896d6b9..2d25f47 100644 --- a/lib/locales/runtimeUtils.ts +++ b/lib/locales/runtimeUtils.ts @@ -1,11 +1,13 @@ -import { Gas, NEAR } from 'near-units' -import { atcb_action as addToCalendar } from 'add-to-calendar-button' -import settings from '../../config/settings.json' -import { signIn } from '../../src/near' -import { TenK } from '../../src/near/contracts' +import { Gas, NEAR } from "near-units" +import { atcb_action as addToCalendar } from "add-to-calendar-button" +import settings from "../../config/settings.json" +import { signIn } from "../../src/near" +import { TenK } from "../../src/near/contracts" import { TenkData } from "../../src/hooks/useTenk" -import { saleStatuses, userStatuses } from './Locale' -import { Locale } from '../../src/hooks/useLocales' +import { saleStatuses, userStatuses } from "./Locale" +import { Locale } from "../../src/hooks/useLocales" +import { transactions } from "near-api-js" +import { BN } from "bn.js" type Timestamp = number @@ -13,8 +15,11 @@ type Data = TenkData & { currentUser: string locale: Locale numberToMint?: number + nearMint?: number + chedMint?: number saleStatus: typeof saleStatuses[number] userStatus: typeof userStatuses[number] + price_cheddar_near: string } function formatNumber( @@ -23,9 +28,8 @@ function formatNumber( /** * `undefined` will default to browser's locale (may not work correctly in Node during build) */ - locale?: string, + locale?: string ) { - return new Intl.NumberFormat(locale, { maximumSignificantDigits: 3, }).format(Number(num)) @@ -33,12 +37,24 @@ function formatNumber( function formatCurrency( num: number | string, - currency: string = 'NEAR', + currency: string = "NEAR", /** * `undefined` will default to browser's locale (may not work correctly in Node during build) */ - locale?: string, + locale?: string +) { + return `${formatNumber(num, locale)} ${currency}` +} + +function formatCurrencyCheddar( + num: number | string, + currency: string = "Cheddar", + + /** + * `undefined` will default to browser's locale (may not work correctly in Node during build) + */ + locale?: string ) { return `${formatNumber(num, locale)} ${currency}` } @@ -54,9 +70,9 @@ function formatDate( ): string { const date = typeof d === "number" ? new Date(d) : d - return new Intl.DateTimeFormat(locale, { - dateStyle: 'short', - timeStyle: 'short', + return new Intl.DateTimeFormat(locale, { + dateStyle: "short", + timeStyle: "short", ...options, }).format(date) } @@ -66,9 +82,17 @@ const replacers = { PRESALE_START: (d: Data) => formatDate(d.saleInfo.presale_start), SALE_START: (d: Data) => formatDate(d.saleInfo.sale_start), MINT_LIMIT: (d: Data) => d.remainingAllowance ?? 0, - MINT_PRICE: (d: Data) => formatCurrency( - NEAR.from(d.saleInfo.price).mul(NEAR.from('' + (d.numberToMint ?? 1))).toHuman().split(' ')[0] - ), + MINT_PRICE: (d: Data) => + d.nearMint == 1 + ? formatCurrency( + NEAR.from(d.saleInfo.price) + .mul(NEAR.from("" + (d.numberToMint ?? 1))) + .toHuman() + .split(" ")[0] + ) + : formatCurrencyCheddar( + (BigInt(d.price_cheddar_near) / BigInt(Math.pow(10, 24))).toString() + ), MINT_RATE_LIMIT: (d: Data) => d.mintRateLimit, INITIAL_COUNT: (d: Data) => formatNumber(d.saleInfo.token_final_supply), REMAINING_COUNT: (d: Data) => formatNumber(d.tokensLeft), @@ -78,20 +102,20 @@ export const placeholderStrings = Object.keys(replacers) export type PlaceholderString = keyof typeof replacers -const placeholderRegex = new RegExp(`(${placeholderStrings.join('|')})`, 'gm') +const placeholderRegex = new RegExp(`(${placeholderStrings.join("|")})`, "gm") export function fill(text: string, data: Data): string { - return text.replace(placeholderRegex, (match) => { + return text.replace(placeholderRegex, match => { return String(replacers[match as PlaceholderString](data)) }) } // add-to-calendar-button has strange strict requirements on time format function formatDatesForAtcb(d: Timestamp) { - let [start, end] = new Date(d).toISOString().split('T') + let [start, end] = new Date(d).toISOString().split("T") return [ start, - end.replace(/:\d\d\..*$/, '') // strip seconds, ms, & TZ + end.replace(/:\d\d\..*$/, ""), // strip seconds, ms, & TZ ] } @@ -103,26 +127,106 @@ function getStartAndEnd(d: Timestamp) { } const actions = { - 'ADD_TO_CALENDAR(SALE_START)': (d: Data) => addToCalendar({ - name: d.locale.calendarEvent!, - ...getStartAndEnd(d.saleInfo.sale_start), - options: ['Google', 'iCal', 'Apple', 'Microsoft365', 'MicrosoftTeams', 'Outlook.com', 'Yahoo'], - timeZone: "UTC", - trigger: 'click', - }), - 'ADD_TO_CALENDAR(PRESALE_START)': (d: Data) => addToCalendar({ - name: d.locale.calendarEvent!, - ...getStartAndEnd(d.saleInfo.presale_start), - options: ['Google', 'iCal', 'Apple', 'Microsoft365', 'MicrosoftTeams', 'Outlook.com', 'Yahoo'], - timeZone: "UTC", - trigger: 'click', - }), - 'SIGN_IN': signIn, - 'MINT': (d: Data) => TenK.nft_mint_many({ num: d.numberToMint ?? 1 }, { - gas: Gas.parse('40 Tgas').mul(Gas.from('' + d.numberToMint)), - attachedDeposit: NEAR.from(d.saleInfo.price).mul(NEAR.from('' + d.numberToMint)), - }), - 'GO_TO_PARAS': () => window.open(`https://paras.id/search?q=${settings.contractName}&sort=priceasc&pmin=.01&is_verified=true`), + "ADD_TO_CALENDAR(SALE_START)": (d: Data) => + addToCalendar({ + name: d.locale.calendarEvent!, + ...getStartAndEnd(d.saleInfo.sale_start), + options: [ + "Google", + "iCal", + "Apple", + "Microsoft365", + "MicrosoftTeams", + "Outlook.com", + "Yahoo", + ], + timeZone: "UTC", + trigger: "click", + }), + "ADD_TO_CALENDAR(PRESALE_START)": (d: Data) => + addToCalendar({ + name: d.locale.calendarEvent!, + ...getStartAndEnd(d.saleInfo.presale_start), + options: [ + "Google", + "iCal", + "Apple", + "Microsoft365", + "MicrosoftTeams", + "Outlook.com", + "Yahoo", + ], + timeZone: "UTC", + trigger: "click", + }), + SIGN_IN: signIn, + MINT: (d: Data) => + TenK.nft_mint_many( + { num: d.numberToMint ?? 1 }, + { + gas: Gas.parse("40 Tgas").mul(Gas.from("" + d.numberToMint)), + attachedDeposit: NEAR.from(d.saleInfo.price).mul( + NEAR.from("" + d.numberToMint) + ), + } + ), + MintForNear: (d: Data) => + TenK.nft_mint_many( + { num: d.numberToMint ?? 1, with_cheddar: false }, + { + gas: Gas.parse("40 Tgas").mul(Gas.from("" + d.numberToMint)), + attachedDeposit: NEAR.from(d.saleInfo.price).mul( + NEAR.from("" + d.numberToMint) + ), + } + ), + MintForChed: (d: Data) => { + TenK.get_cheddar_storage_balance("oreos.testnet").then(resp => { + const cheddarActions = [] + const tenkActions = [] + const finalActions = [] + if (!resp) { + cheddarActions.push( + transactions.functionCall( + "storage_deposit", + {}, + Gas.parse("100 Tgas"), + new BN("1120000000000000000000") + ) + ) + } + cheddarActions.push( + transactions.functionCall( + "ft_transfer_call", + { + receiver_id: TenK.contractId, + amount: d.price_cheddar_near, + msg: "", + }, + Gas.parse("200 Tgas"), + new BN("1") + ) + ) + tenkActions.push( + transactions.functionCall( + "nft_mint_many", + { num: d.numberToMint ?? 1, with_cheddar: true }, + + Gas.parse("40 Tgas").mul(Gas.from("" + d.numberToMint)), + new BN("1") + ) + ) + finalActions.push( + TenK.makeTransaction("token-v3.cheddar.testnet", cheddarActions) + ) + finalActions.push(TenK.makeTransaction(TenK.contractId, tenkActions)) + TenK.passToWallet(finalActions) + }) + }, + GO_TO_PARAS: () => + window.open( + `https://paras.id/search?q=${settings.contractName}&sort=priceasc&pmin=.01&is_verified=true` + ), } export type Action = keyof typeof actions @@ -132,19 +236,18 @@ export function act(action: Action, data: Data): void { } export function can(action: Action, data: Data): boolean { - if (action === 'MINT') { - return Boolean(data.currentUser) && ( - (data.saleStatus === 'presale' && + if (action === "MINT") { + return ( + Boolean(data.currentUser) && + ((data.saleStatus === "presale" && data.remainingAllowance !== undefined && - data.remainingAllowance > 0 - ) || - (data.saleStatus === 'saleOpen' && ( - // users are added to the whitelist as they mint during saleOpen; - // undefined means they haven't minted yet - data.remainingAllowance === undefined || - data.remainingAllowance > 0 - )) + data.remainingAllowance > 0) || + (data.saleStatus === "saleOpen" && + // users are added to the whitelist as they mint during saleOpen; + // undefined means they haven't minted yet + (data.remainingAllowance === undefined || + data.remainingAllowance > 0))) ) } return true -} \ No newline at end of file +} diff --git a/package.json b/package.json index b716043..cbae3b7 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,12 @@ "version": "0.1.0", "author": "Chad Ostrowski ", "dependencies": { + "@radix-ui/colors": "^0.1.8", + "@radix-ui/react-dialog": "^0.1.7", "@radix-ui/react-dropdown-menu": "0.1.6", + "@radix-ui/react-icons": "^1.1.1", "@radix-ui/react-slider": "^0.1.4", + "@stitches/react": "^1.2.8", "add-to-calendar-button": "1.7.8", "bn.js": "^5.2.0", "gatsby-background-image": "^1.6.0", diff --git a/src/components/connectModal/index.tsx b/src/components/connectModal/index.tsx new file mode 100644 index 0000000..559a65a --- /dev/null +++ b/src/components/connectModal/index.tsx @@ -0,0 +1,141 @@ +import React, { useState } from "react" +import { styled, keyframes } from "@stitches/react" +import { violet, blackA, mauve, green } from "@radix-ui/colors" +import { Cross2Icon } from "@radix-ui/react-icons" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { signIn } from "../../near" +import Spinner from "../spinner" + +const overlayShow = keyframes({ + "0%": { opacity: 0 }, + "100%": { opacity: 1 }, +}) + +const contentShow = keyframes({ + "0%": { opacity: 0, transform: "translate(-50%, -48%) scale(.96)" }, + "100%": { opacity: 1, transform: "translate(-50%, -50%) scale(1)" }, +}) + +const StyledOverlay = styled(DialogPrimitive.Overlay, { + backgroundColor: blackA.blackA9, + position: "fixed", + inset: 0, + "@media (prefers-reduced-motion: no-preference)": { + animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1) forwards`, + }, +}) + +const StyledContent = styled(DialogPrimitive.Content, { + backgroundColor: "white", + borderRadius: 6, + boxShadow: + "hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px", + position: "fixed", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: "90vw", + maxWidth: "450px", + maxHeight: "85vh", + padding: 25, + "@media (prefers-reduced-motion: no-preference)": { + animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1) forwards`, + }, + "&:focus": { outline: "none" }, +}) + +const StyledTitle = styled(DialogPrimitive.Title, { + margin: 0, + fontWeight: 500, + color: mauve.mauve12, + fontSize: 17, +}) + +const StyledDescription = styled(DialogPrimitive.Description, { + margin: "10px 0 20px", + color: mauve.mauve11, + fontSize: 15, + lineHeight: 1.5, +}) + +// Exports +export const Dialog = DialogPrimitive.Root +export const DialogContent = DialogPrimitive.Portal +export const DialogTitle = StyledTitle +export const DialogDescription = StyledDescription +export const DialogClose = DialogPrimitive.Close + +// Your app... +const Flex = styled("div", { display: "flex" }) +const Box = styled("div", {}) + +const IconButton = styled("button", { + all: "unset", + fontFamily: "inherit", + borderRadius: "100%", + height: 25, + width: 25, + display: "inline-flex", + alignItems: "center", + justifyContent: "center", + color: violet.violet11, + position: "absolute", + top: 10, + right: 10, + + "&:hover": { backgroundColor: violet.violet4 }, + "&:focus": { boxShadow: `0 0 0 2px ${violet.violet7}` }, +}) + +type Props = { + showConnectModal: boolean + setShowConnectModal: React.Dispatch> +} + +const ConnectModal = ({ showConnectModal, setShowConnectModal }: Props) => { + const [showSpinner, setShowSpinner] = useState(false) + + const handleClose = () => { + setShowConnectModal(false) + } + + const handleSignIn = () => { + setShowSpinner(true) + signIn() + } + + return ( + <> + {showSpinner && } + + + + {!showSpinner && ( + + Error + + To mint a NFT you must first connect your wallet. + + + + + + + + + )} + + + + ) +} + +export default ConnectModal diff --git a/src/components/errorModal/index.tsx b/src/components/errorModal/index.tsx new file mode 100644 index 0000000..22a8f8f --- /dev/null +++ b/src/components/errorModal/index.tsx @@ -0,0 +1,132 @@ +import React from "react" +import { styled, keyframes } from "@stitches/react" +import { violet, blackA, mauve, green } from "@radix-ui/colors" +import { Cross2Icon } from "@radix-ui/react-icons" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { signIn } from "../../near" + +const overlayShow = keyframes({ + "0%": { opacity: 0 }, + "100%": { opacity: 1 }, +}) + +const contentShow = keyframes({ + "0%": { opacity: 0, transform: "translate(-50%, -48%) scale(.96)" }, + "100%": { opacity: 1, transform: "translate(-50%, -50%) scale(1)" }, +}) + +const StyledOverlay = styled(DialogPrimitive.Overlay, { + backgroundColor: blackA.blackA9, + position: "fixed", + inset: 0, + "@media (prefers-reduced-motion: no-preference)": { + animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1) forwards`, + }, +}) + +const StyledContent = styled(DialogPrimitive.Content, { + backgroundColor: "white", + borderRadius: 6, + boxShadow: + "hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px", + position: "fixed", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: "90vw", + maxWidth: "450px", + maxHeight: "85vh", + padding: 25, + "@media (prefers-reduced-motion: no-preference)": { + animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1) forwards`, + }, + "&:focus": { outline: "none" }, +}) + +const StyledTitle = styled(DialogPrimitive.Title, { + margin: 0, + fontWeight: 500, + color: mauve.mauve12, + fontSize: 17, +}) + +const StyledDescription = styled(DialogPrimitive.Description, { + margin: "10px 0 20px", + color: mauve.mauve11, + fontSize: 15, + lineHeight: 1.5, +}) + +// Exports +export const Dialog = DialogPrimitive.Root +export const DialogContent = DialogPrimitive.Portal +export const DialogTitle = StyledTitle +export const DialogDescription = StyledDescription +export const DialogClose = DialogPrimitive.Close + +// Your app... +const Flex = styled("div", { display: "flex" }) +const Box = styled("div", {}) + +const IconButton = styled("button", { + all: "unset", + fontFamily: "inherit", + borderRadius: "100%", + height: 25, + width: 25, + display: "inline-flex", + alignItems: "center", + justifyContent: "center", + color: violet.violet11, + position: "absolute", + top: 10, + right: 10, + + "&:hover": { backgroundColor: violet.violet4 }, + "&:focus": { boxShadow: `0 0 0 2px ${violet.violet7}` }, +}) + +type Props = { + error: string + setError: React.Dispatch> +} + +const ErrorModal = ({ error, setError }: Props) => { + const handleClose = () => { + window.history.replaceState({}, "", `${window.location.pathname}`) + setError("") + } + + const fillError = (error: string) => { + switch (error) { + case "userRejected": + return "Transaction rejected by user" + break + + default: + return "Please try again" + break + } + } + + return ( + + + + + Error + {fillError(error)} + + + + + + + + + ) +} + +export default ErrorModal diff --git a/src/components/hero/index.tsx b/src/components/hero/index.tsx index dd04aa5..71136ab 100644 --- a/src/components/hero/index.tsx +++ b/src/components/hero/index.tsx @@ -1,22 +1,31 @@ -import React from 'react' -import type { ExpandedHeroTree } from '../../../lib/locales' -import { act, can, fill } from '../../../lib/locales/runtimeUtils' +import React, { useState } from "react" +import type { ExpandedHeroTree } from "../../../lib/locales" +import { act, can, fill } from "../../../lib/locales/runtimeUtils" import { wallet } from "../../near" -import Slider from '../slider' -import Section from '../section' +import Section from "../section" import Markdown from "../markdown" -import useHeroStatuses from '../../hooks/useHeroStatuses' -import useTenk from '../../hooks/useTenk' -import useLocales from '../../hooks/useLocales' -import * as css from './hero.module.css' +import useHeroStatuses from "../../hooks/useHeroStatuses" +import useTenk from "../../hooks/useTenk" +import useLocales from "../../hooks/useLocales" +import * as css from "./hero.module.css" +import ConnectModal from "../connectModal" +import Spinner from "../spinner" +import { NEAR } from "near-units" const currentUser = wallet.getAccountId() -const Hero: React.FC<{ heroTree: ExpandedHeroTree }> = ({ heroTree }) => { +const Hero: React.FC<{ + heroTree: ExpandedHeroTree + showConnectModal: boolean + setShowConnectModal: React.Dispatch> +}> = ({ heroTree, showConnectModal, setShowConnectModal }) => { const { locale } = useLocales() const tenkData = useTenk() const { saleStatus, userStatus } = useHeroStatuses() const [numberToMint, setNumberToMint] = React.useState(1) + const [nearMint, setNearMint] = React.useState(1) + const [chedMint, setChedMint] = React.useState(3) + const [showSpinner, setShowSpinner] = useState(false) const hero = heroTree[saleStatus][userStatus] if (!locale) return null @@ -29,27 +38,57 @@ const Hero: React.FC<{ heroTree: ExpandedHeroTree }> = ({ heroTree }) => { userStatus, } + const mintForNear = function () { + if (userStatus === "signedOut") { + setShowConnectModal(true) + } else { + console.log("NEAR") + setShowSpinner(true) + act("MintForNear", { ...data, numberToMint }) + } + } + + const mintForCheddar = function () { + if (userStatus === "signedOut") { + setShowConnectModal(true) + } else { + console.log("Cheddar") + setShowSpinner(true) + act("MintForChed", { ...data, numberToMint }) + } + } + return (
{can(hero.action, data) && ( -
{ - e.preventDefault() - act(hero.action, { ...data, numberToMint }) - }}> + { + e.preventDefault() + //act(hero.action, { ...data, numberToMint }) + }} + > {hero.setNumber && ( <>
@@ -60,34 +99,35 @@ const Hero: React.FC<{ heroTree: ExpandedHeroTree }> = ({ heroTree }) => { {fill(hero.remaining, data)}
- setNumberToMint(v)} - value={[numberToMint]} - /> )} - + )}

{locale.title}

- +
{hero.ps && }
+ + {showSpinner && }
) } -export default Hero \ No newline at end of file +export default Hero diff --git a/src/components/layout/index.tsx b/src/components/layout/index.tsx index f031f0e..2e1aaca 100644 --- a/src/components/layout/index.tsx +++ b/src/components/layout/index.tsx @@ -5,12 +5,15 @@ import Nav from "../nav" import Footer from "../footer" import * as css from "./layout.module.css" -const Layout: React.FC<{ style?: React.CSSProperties }> = ({ style, children }) => { +const Layout: React.FC<{ + style?: React.CSSProperties + showConnectModal: boolean +}> = ({ style, showConnectModal, children }) => { return (
-
diff --git a/src/components/my-nfts/index.tsx b/src/components/my-nfts/index.tsx index e384b13..d6b5e01 100644 --- a/src/components/my-nfts/index.tsx +++ b/src/components/my-nfts/index.tsx @@ -1,19 +1,20 @@ import React, { useEffect, useMemo, useRef, useState } from "react" -import { createPortal } from 'react-dom' -import Lightbox from 'react-image-lightbox'; +import { createPortal } from "react-dom" +import Lightbox from "react-image-lightbox" import { wallet } from "../../near" -import useLocales from '../../hooks/useLocales' -import useTenk from '../../hooks/useTenk' -import * as css from './my-nfts.module.css' -import 'react-image-lightbox/style.css'; +import useLocales from "../../hooks/useLocales" +import useTenk from "../../hooks/useTenk" +import * as css from "./my-nfts.module.css" +import "react-image-lightbox/style.css" -const portalRoot: undefined | HTMLElement = typeof document !== 'undefined' - ? document.getElementById("portal")! - : undefined +const portalRoot: undefined | HTMLElement = + typeof document !== "undefined" + ? document.getElementById("portal")! + : undefined const MyNFTs: React.FC<{ - highlight?: string[], - onClose: () => void, + highlight?: string[] + onClose: () => void }> = ({ highlight, onClose }) => { const currentUser = wallet.getAccountId() const { locale } = useLocales() @@ -23,23 +24,31 @@ const MyNFTs: React.FC<{ const portalElement = useRef(document.createElement("div")) const containerElement = useRef(null) - const onClick = useMemo(() => function onCloseRaw(this: Document, event: MouseEvent) { - if (!lightboxOpen && event.target && !containerElement.current?.contains(event.target as Node)) { - onClose() - } - }, [lightboxOpen, onClose]) + const onClick = useMemo( + () => + function onCloseRaw(this: Document, event: MouseEvent) { + if ( + !lightboxOpen && + event.target && + !containerElement.current?.contains(event.target as Node) + ) { + onClose() + } + }, + [lightboxOpen, onClose] + ) useEffect(() => { portalRoot?.appendChild(portalElement.current) - const bgContent: HTMLDivElement = document.querySelector('#___gatsby')! - bgContent.style.filter = 'blur(4px)' - bgContent.style.overflow = 'hidden' - document.addEventListener('click', onClick) + const bgContent: HTMLDivElement = document.querySelector("#___gatsby")! + bgContent.style.filter = "blur(4px)" + bgContent.style.overflow = "hidden" + document.addEventListener("click", onClick) return function onUnmount() { - bgContent.style.filter = '' - bgContent.style.overflow = '' + bgContent.style.filter = "" + bgContent.style.overflow = "" portalRoot?.removeChild(portalElement.current) - document.removeEventListener('click', onClick) + document.removeEventListener("click", onClick) } }, [onClick, portalRoot]) @@ -52,9 +61,13 @@ const MyNFTs: React.FC<{ ) { return null } - - const nextSrc = nfts.length > 1 ? nfts[(photoIndex + 1) % nfts.length].media : undefined - const prevSrc = nfts.length > 1 ? nfts[(photoIndex + nfts.length - 1) % nfts.length].media : undefined + console.log(nfts) + const nextSrc = + nfts.length > 1 ? nfts[(photoIndex + 1) % nfts.length].media : undefined + const prevSrc = + nfts.length > 1 + ? nfts[(photoIndex + nfts.length - 1) % nfts.length].media + : undefined return createPortal( <> @@ -68,23 +81,32 @@ const MyNFTs: React.FC<{
{nfts.map((nft, index) => ( - +
+ +
))}
@@ -115,4 +137,4 @@ const MyNFTs: React.FC<{ ) } -export default MyNFTs \ No newline at end of file +export default MyNFTs diff --git a/src/components/my-nfts/my-nfts.module.css b/src/components/my-nfts/my-nfts.module.css index fbd3b00..e6ff94e 100644 --- a/src/components/my-nfts/my-nfts.module.css +++ b/src/components/my-nfts/my-nfts.module.css @@ -50,6 +50,20 @@ display: flex; flex-direction: column; justify-content: space-between; + align-items: center; + text-align: left; +} + +.nft-wrapper { + background-color: #777; + color: inherit; + padding: 10px; + margin: 0; + border: none; + border-radius: 5px; + display: flex; + flex-direction: column; + justify-content: space-between; text-align: left; transition: transform 75ms; transform: scale(100%); @@ -58,7 +72,7 @@ .nft:hover, .nft:focus { box-shadow: none; - transform: scale(105%); + transform: scale(1.05); background-color: transparent; } @@ -72,7 +86,7 @@ font-weight: var(--fw-bold); } -.nft>div:first-child { +.nft > div:first-child { --aspect-ratio: 1 / 1; background-color: var(--gray-1); border-radius: var(--br-small); @@ -80,4 +94,4 @@ background-repeat: no-repeat; background-position: center; background-size: cover; -} \ No newline at end of file +} diff --git a/src/components/my-nfts/my-nfts.module.css.d.ts b/src/components/my-nfts/my-nfts.module.css.d.ts index e2644cd..e71289c 100644 --- a/src/components/my-nfts/my-nfts.module.css.d.ts +++ b/src/components/my-nfts/my-nfts.module.css.d.ts @@ -3,4 +3,5 @@ export const myNfts: string; export const close: string; export const grid: string; export const nft: string; +export const nftWrapper: string; export const highlight: string; diff --git a/src/components/nav/index.tsx b/src/components/nav/index.tsx index cb57ef7..effdbb8 100644 --- a/src/components/nav/index.tsx +++ b/src/components/nav/index.tsx @@ -1,5 +1,5 @@ import settings from "../../../config/settings.json" -import React, { useState } from "react" +import React, { useEffect, useState } from "react" import { signIn, wallet } from "../../near" import * as css from "./nav.module.css" import useLocales from "../../hooks/useLocales" @@ -7,27 +7,59 @@ import useTenk from "../../hooks/useTenk" import Dropdown from "../../components/dropdown" import MyNFTs from "../../components/my-nfts" import Image from "../image" +import Spinner from "../spinner" function signOut() { wallet.signOut() window.location.replace(window.location.origin + window.location.pathname) } -export default function Nav() { +type Props = { + showConnectModal: boolean +} + +export default function Nav({ showConnectModal }: Props) { const currentUser = wallet.getAccountId() const { locale } = useLocales() const { nfts } = useTenk() const [showNFTs, setShowNFTs] = useState(false) + const [firstRender, setFirstRender] = useState(true) + const [buttonClass, setButtonClass] = useState("secondary") + const [showSpinner, setShowSpinner] = useState(false) if (!locale) return null + const handleOnAnimationEnd = () => { + setButtonClass("secondary") + } + + useEffect(() => { + if (showConnectModal) { + setFirstRender(false) + } + if (!showConnectModal && !firstRender) { + setButtonClass(`secondary ${css.buttonAnimation}`) + } + }, [showConnectModal]) + + const handleSignIn = () => { + setShowSpinner(true) + signIn() + } + return ( <> {showNFTs && setShowNFTs(false)} />} + {showSpinner && } ) } diff --git a/src/components/nav/nav.module.css b/src/components/nav/nav.module.css index 3738a2c..73f8a05 100644 --- a/src/components/nav/nav.module.css +++ b/src/components/nav/nav.module.css @@ -41,4 +41,32 @@ .button { font-size: var(--spacing-l); font-weight: var(--fw-black); -} \ No newline at end of file +} + +.button-animation { + animation: animationKeyFrames 3s ease-in-out; +} + +@keyframes animationKeyFrames { + 0% { + transform: scale(1) rotate(0deg); + } + 16% { + transform: scale(0.9) rotate(0deg); + } + 33% { + transform: scale(1) rotate(0deg); + } + 50% { + transform: scale(0.9) rotate(0deg); + } + 66% { + transform: scale(1) rotate(0deg); + } + 83% { + transform: scale(0.9) rotate(0deg); + } + 100% { + transform: scale(1) rotate(0deg); + } +} diff --git a/src/components/nav/nav.module.css.d.ts b/src/components/nav/nav.module.css.d.ts index 833280f..c9aafd0 100644 --- a/src/components/nav/nav.module.css.d.ts +++ b/src/components/nav/nav.module.css.d.ts @@ -4,3 +4,5 @@ export const content: string; export const social: string; export const actions: string; export const button: string; +export const buttonAnimation: string; +export const animationKeyFrames: string; diff --git a/src/components/spinner/cheddar.svg b/src/components/spinner/cheddar.svg new file mode 100644 index 0000000..7034080 --- /dev/null +++ b/src/components/spinner/cheddar.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/spinner/index.tsx b/src/components/spinner/index.tsx new file mode 100644 index 0000000..3736571 --- /dev/null +++ b/src/components/spinner/index.tsx @@ -0,0 +1,54 @@ +import React from "react" +import { styled, keyframes } from "@stitches/react" +import { blackA } from "@radix-ui/colors" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import * as css from "./spinner.module.css" +import { StaticImage } from "gatsby-plugin-image" + +const overlayShow = keyframes({ + "0%": { opacity: 0 }, + "100%": { opacity: 1 }, +}) + +const StyledOverlay = styled(DialogPrimitive.Overlay, { + backgroundColor: blackA.blackA11, + position: "fixed", + inset: 0, + "@media (prefers-reduced-motion: no-preference)": { + animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1) forwards`, + }, +}) + +// Exports +export const Dialog = DialogPrimitive.Root +export const DialogContent = DialogPrimitive.Portal + +const Spinner = () => { + return ( + + + +
+ +
+
+
+ ) +} + +export default Spinner diff --git a/src/components/spinner/spinner.module.css b/src/components/spinner/spinner.module.css new file mode 100644 index 0000000..11bb28b --- /dev/null +++ b/src/components/spinner/spinner.module.css @@ -0,0 +1,18 @@ +.cheddarSpinner { + width: 70px !important; + margin: auto; + animation-iteration-count: infinite; + animation-timing-function: linear; + animation-duration: 0.8s; + animation-name: spinner-loading; + z-index: -1; +} + +@keyframes spinner-loading { + 0% { + transform: rotate(0deg); + } + to { + transform: rotate(1turn); + } +} diff --git a/src/components/spinner/spinner.module.css.d.ts b/src/components/spinner/spinner.module.css.d.ts new file mode 100644 index 0000000..d7b2a33 --- /dev/null +++ b/src/components/spinner/spinner.module.css.d.ts @@ -0,0 +1,3 @@ +// This file is automatically generated. Do not modify this file manually -- YOUR CHANGES WILL BE ERASED! +export const cheddarSpinner: string; +export const spinnerLoading: string; diff --git a/src/hooks/useTenk.ts b/src/hooks/useTenk.ts index 1731171..086d9e1 100644 --- a/src/hooks/useTenk.ts +++ b/src/hooks/useTenk.ts @@ -1,5 +1,9 @@ import React from "react" -import { NftContractMetadata, SaleInfo, Token as RawToken } from "../near/contracts/tenk" +import { + NftContractMetadata, + SaleInfo, + Token as RawToken, +} from "../near/contracts/tenk" import { TenK } from "../near/contracts" import { wallet } from "../near" import staleData from "../../stale-data-from-build-time.json" @@ -22,6 +26,7 @@ export interface TenkData { interface ReturnedData extends TenkData { stale: boolean + price_cheddar_near: string } // initialize calls at root of file so that first evaluation of this file causes @@ -46,7 +51,7 @@ export async function rpcData(): Promise { vip, remainingAllowance, nfts, - mintRateLimit + mintRateLimit, ] = await rpcCalls return { saleInfo, @@ -54,22 +59,65 @@ export async function rpcData(): Promise { tokensLeft, vip: vip ?? false, remainingAllowance: remainingAllowance ?? undefined, - nfts: nfts?.map(nft => ({ ...nft, - media: new URL(nft.metadata?.media ?? '', contractMetadata.base_uri ?? '').href - })) ?? [], + nfts: + nfts?.map(nft => ({ + ...nft, + media: new URL( + nft.metadata?.media ?? "", + contractMetadata.base_uri ?? "" + ).href, + })) ?? [], mintRateLimit: mintRateLimit ?? 10, } } +const axios = require("axios") + +const cheddarNearPrice = async (nftNearPrice: string) => { + const url = "https://api.stats.ref.finance/api/top-tokens" + const returns = await axios.get(url) + const prices = returns.data + + let price_near = 0 + let price_cheddar = 0 + let price_cheddar_near = 0 + if (prices) { + for (let i = 0; i < prices.length; i++) { + const price = prices[i] + if (price.symbol == "wNEAR") { + price_near = price.price + } else if (price.symbol == "Cheddar") { + price_cheddar = price.price + } + } + + if (price_near > 0 && price_cheddar > 0) { + price_cheddar_near = + Math.round(price_near / price_cheddar) * Math.pow(10, 3) + } + } + const nftCheddarPrice = ( + (BigInt(nftNearPrice) * BigInt(price_cheddar_near)) / + BigInt(Math.pow(10, 3)) + ).toString() + + return nftCheddarPrice +} + export default function useTenk(): ReturnedData { const [data, setData] = React.useState({ - ...staleData as unknown as TenkData, - stale: true + ...(staleData as unknown as TenkData), + stale: true, + price_cheddar_near: "0", }) React.useEffect(() => { - rpcData().then(d => setData({ ...d, stale: false })) + rpcData().then(d => + cheddarNearPrice(d.saleInfo.price).then(resp => + setData({ ...d, stale: false, price_cheddar_near: resp }) + ) + ) }, []) return data -} \ No newline at end of file +} diff --git a/src/near/contracts/tenk.ts b/src/near/contracts/tenk.ts index db52f83..064ea21 100644 --- a/src/near/contracts/tenk.ts +++ b/src/near/contracts/tenk.ts @@ -1,712 +1,1380 @@ -import { Account, transactions, providers, DEFAULT_FUNCTION_CALL_GAS } from 'near-api-js'; +import { + Account, + transactions, + providers, + DEFAULT_FUNCTION_CALL_GAS, + Near, + connect, + keyStores, + WalletAccount, + utils, + WalletConnection, +} from "near-api-js" - -import BN from 'bn.js'; +import BN from "bn.js" +import { nearConfig } from ".." +import { baseDecode } from "borsh" export interface ChangeMethodOptions { - gas?: BN; - attachedDeposit?: BN; - walletMeta?: string; - walletCallbackUrl?: string; + gas?: BN + attachedDeposit?: BN + walletMeta?: string + walletCallbackUrl?: string } export interface ViewFunctionOptions { - parse?: (response: Uint8Array) => any; - stringify?: (input: any) => any; + parse?: (response: Uint8Array) => any + stringify?: (input: any) => any } /** 64 bit unsigned integer less than 2^53 -1 */ -type u64 = number; +type u64 = number /** 64 bit signed integer less than 2^53 -1 */ -type i64 = number; +type i64 = number /** -* StorageUsage is used to count the amount of storage used by a contract. -*/ -export type StorageUsage = u64; + * StorageUsage is used to count the amount of storage used by a contract. + */ +export type StorageUsage = u64 /** -* Balance is a type for storing amounts of tokens, specified in yoctoNEAR. -*/ -export type Balance = U128; + * Balance is a type for storing amounts of tokens, specified in yoctoNEAR. + */ +export type Balance = U128 /** -* Represents the amount of NEAR tokens in "gas units" which are used to fund transactions. -*/ -export type Gas = u64; + * Represents the amount of NEAR tokens in "gas units" which are used to fund transactions. + */ +export type Gas = u64 /** -* base64 string. -*/ -export type Base64VecU8 = string; + * base64 string. + */ +export type Base64VecU8 = string /** -* Raw type for duration in nanoseconds -*/ -export type Duration = u64; -export type U128 = string; + * Raw type for duration in nanoseconds + */ +export type Duration = u64 +export type U128 = string /** -* Public key in a binary format with base58 string serialization with human-readable curve. -* The key types currently supported are `secp256k1` and `ed25519`. -* -* Ed25519 public keys accepted are 32 bytes and secp256k1 keys are the uncompressed 64 format. -*/ -export type PublicKey = string; -export type AccountId = string; + * Public key in a binary format with base58 string serialization with human-readable curve. + * The key types currently supported are `secp256k1` and `ed25519`. + * + * Ed25519 public keys accepted are 32 bytes and secp256k1 keys are the uncompressed 64 format. + */ +export type PublicKey = string +export type AccountId = string /** -* Raw type for timestamp in nanoseconds -*/ -export type Timestamp = u64; + * Raw type for timestamp in nanoseconds + */ +export type Timestamp = u64 export interface StorageBalanceBounds { - min: U128, - max?: U128, + min: U128 + max?: U128 } export interface FungibleTokenMetadata { - spec: string, - name: string, - symbol: string, - icon?: string, - reference?: string, - reference_hash?: Base64VecU8, - decimals: number, + spec: string + name: string + symbol: string + icon?: string + reference?: string + reference_hash?: Base64VecU8 + decimals: number } /** -* In this implementation, the Token struct takes two extensions standards (metadata and approval) as optional fields, as they are frequently used in modern NFTs. -*/ + * In this implementation, the Token struct takes two extensions standards (metadata and approval) as optional fields, as they are frequently used in modern NFTs. + */ export interface Token { - token_id: TokenId, - owner_id: AccountId, - metadata?: TokenMetadata, - approved_account_ids?: Record, + token_id: TokenId + owner_id: AccountId + metadata?: TokenMetadata + approved_account_ids?: Record } /** -* Note that token IDs for NFTs are strings on NEAR. It's still fine to use autoincrementing numbers as unique IDs if desired, but they should be stringified. This is to make IDs more future-proof as chain-agnostic conventions and standards arise, and allows for more flexibility with considerations like bridging NFTs across chains, etc. -*/ -export type TokenId = string; + * Note that token IDs for NFTs are strings on NEAR. It's still fine to use autoincrementing numbers as unique IDs if desired, but they should be stringified. This is to make IDs more future-proof as chain-agnostic conventions and standards arise, and allows for more flexibility with considerations like bridging NFTs across chains, etc. + */ +export type TokenId = string export interface StorageBalance { - total: U128, - available: U128, + total: U128 + available: U128 } -export type WrappedDuration = string; +export type WrappedDuration = string /** -* Metadata on the individual token level. -*/ + * Metadata on the individual token level. + */ export interface TokenMetadata { - title?: string, - description?: string, - media?: string, - media_hash?: Base64VecU8, - copies?: u64, - issued_at?: string, - expires_at?: string, - starts_at?: string, - updated_at?: string, - extra?: string, - reference?: string, - reference_hash?: Base64VecU8, + title?: string + description?: string + media?: string + media_hash?: Base64VecU8 + copies?: u64 + issued_at?: string + expires_at?: string + starts_at?: string + updated_at?: string + extra?: string + reference?: string + reference_hash?: Base64VecU8 } /** -* Metadata for the NFT contract itself. -*/ + * Metadata for the NFT contract itself. + */ export interface NftContractMetadata { - spec: string, - name: string, - symbol: string, - icon?: string, - base_uri?: string, - reference?: string, - reference_hash?: Base64VecU8, + spec: string + name: string + symbol: string + icon?: string + base_uri?: string + reference?: string + reference_hash?: Base64VecU8 } /** -* Current state of contract -*/ + * Current state of contract + */ export enum Status { /** - * Not open for any sales - */ + * Not open for any sales + */ Closed = "Closed", /** - * VIP accounts can mint - */ + * VIP accounts can mint + */ Presale = "Presale", /** - * Any account can mint - */ + * Any account can mint + */ Open = "Open", /** - * No more tokens to be minted - */ + * No more tokens to be minted + */ SoldOut = "SoldOut", } export interface InitialMetadata { - name: string, - symbol: string, - uri: string, - icon?: string, - spec?: string, - reference?: string, - reference_hash?: Base64VecU8, + name: string + symbol: string + uri: string + icon?: string + spec?: string + reference?: string + reference_hash?: Base64VecU8 } /** -* Information about the current sale -*/ + * Information about the current sale + */ export interface SaleInfo { /** - * Current state of contract - */ - status: Status, + * Current state of contract + */ + status: Status /** - * Start of the VIP sale - */ - presale_start: Duration, + * Start of the VIP sale + */ + presale_start: Duration /** - * Start of public sale - */ - sale_start: Duration, + * Start of public sale + */ + sale_start: Duration /** - * Total tokens that could be minted - */ - token_final_supply: u64, + * Total tokens that could be minted + */ + token_final_supply: u64 /** - * Current price for one token - */ - price: U128, + * Current price for one token + */ + price: U128 } export interface Sale { - royalties?: Royalties, - initial_royalties?: Royalties, - presale_start?: Duration, - public_sale_start?: Duration, - allowance?: number, - presale_price?: U128, - price: U128, - mint_rate_limit?: number, + royalties?: Royalties + initial_royalties?: Royalties + presale_start?: Duration + public_sale_start?: Duration + allowance?: number + presale_price?: U128 + price: U128 + mint_rate_limit?: number } -export type BasisPoint = number; +export type BasisPoint = number /** -* Information about the current sale from user perspective -*/ + * Information about the current sale from user perspective + */ export interface UserSaleInfo { - sale_info: SaleInfo, - is_vip: boolean, - remaining_allowance?: number, + sale_info: SaleInfo + is_vip: boolean + remaining_allowance?: number } /** -* Copied from https://github.com/near/NEPs/blob/6170aba1c6f4cd4804e9ad442caeae9dc47e7d44/specs/Standards/NonFungibleToken/Payout.md#reference-level-explanation -* A mapping of NEAR accounts to the amount each should be paid out, in -* the event of a token-sale. The payout mapping MUST be shorter than the -* maximum length specified by the financial contract obtaining this -* payout data. Any mapping of length 10 or less MUST be accepted by -* financial contracts, so 10 is a safe upper limit. -* This currently deviates from the standard but is in the process of updating to use this type -*/ + * Copied from https://github.com/near/NEPs/blob/6170aba1c6f4cd4804e9ad442caeae9dc47e7d44/specs/Standards/NonFungibleToken/Payout.md#reference-level-explanation + * A mapping of NEAR accounts to the amount each should be paid out, in + * the event of a token-sale. The payout mapping MUST be shorter than the + * maximum length specified by the financial contract obtaining this + * payout data. Any mapping of length 10 or less MUST be accepted by + * financial contracts, so 10 is a safe upper limit. + * This currently deviates from the standard but is in the process of updating to use this type + */ export interface Payout { - payout: Record, + payout: Record } export interface Royalties { - accounts: Record, - percent: BasisPoint, + accounts: Record + percent: BasisPoint } export class Contract { + near: Near | undefined + walletAccount: WalletConnection | undefined + + constructor(public account: Account, public readonly contractId: string) {} + + async passToWallet( + preTXs: Promise[] + ): Promise { + const TXs = await Promise.all(preTXs) + if (!this.walletAccount) { + throw new Error("You have to make a transaction first") + } + + this.walletAccount.requestSignTransactions({ + transactions: TXs, + callbackUrl: window.location.href, + }) + } - constructor(public account: Account, public readonly contractId: string){} + async makeTransaction( + receiverId: string, + actions: transactions.Action[], + nonceOffset = 1 + ): Promise { + this.near = await connect( + Object.assign( + { + deps: { + keyStore: new keyStores.BrowserLocalStorageKeyStore(), + }, + }, + nearConfig + ) + ) + this.walletAccount = new WalletAccount(this.near) + const accountAux = this.walletAccount.account() + const [accessKey, block] = await Promise.all([ + accountAux.accessKeyForTransaction(receiverId, actions), + this.near.connection.provider.block({ finality: "final" }), + ]) + + if (!accessKey) { + throw new Error( + `Cannot find matching key for transaction sent to ${receiverId}` + ) + } + + const blockHash = baseDecode(block.header.hash) + + const publicKey = utils.PublicKey.from(accessKey.public_key) + const nonce = accessKey.access_key.nonce + nonceOffset + + return transactions.createTransaction( + accountAux.accountId, + publicKey, + receiverId, + nonce, + actions, + blockHash + ) + } - check_key(args: { - public_key: PublicKey, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "check_key", args, options); - } - async update_allowance(args: { - allowance: number, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.update_allowanceRaw(args, options)); - } - update_allowanceRaw(args: { - allowance: number, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "update_allowance", args, ...options}); - } - update_allowanceTx(args: { - allowance: number, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("update_allowance", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - whitelisted(args: { - account_id: AccountId, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "whitelisted", args, options); - } - get_sale_info(args: { - } = {}, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "get_sale_info", args, options); - } - cost_per_token(args: { - minter: AccountId, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "cost_per_token", args, options); - } - async transfer_ownership(args: { - new_owner: AccountId, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.transfer_ownershipRaw(args, options)); - } - transfer_ownershipRaw(args: { - new_owner: AccountId, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "transfer_ownership", args, ...options}); - } - transfer_ownershipTx(args: { - new_owner: AccountId, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("transfer_ownership", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - nft_total_supply(args: { - } = {}, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "nft_total_supply", args, options); - } - nft_tokens(args: { - from_index?: U128, - limit?: u64, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "nft_tokens", args, options); - } - nft_token(args: { - token_id: TokenId, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "nft_token", args, options); - } - async close_contract(args: { - } = {}, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.close_contractRaw(args, options)); - } - close_contractRaw(args: { - } = {}, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "close_contract", args, ...options}); - } - close_contractTx(args: { - } = {}, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("close_contract", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async nft_approve(args: { - token_id: TokenId, - account_id: AccountId, - msg?: string, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.nft_approveRaw(args, options)); - } - nft_approveRaw(args: { - token_id: TokenId, - account_id: AccountId, - msg?: string, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "nft_approve", args, ...options}); - } - nft_approveTx(args: { - token_id: TokenId, - account_id: AccountId, - msg?: string, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("nft_approve", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async start_sale(args: { - price?: U128, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.start_saleRaw(args, options)); - } - start_saleRaw(args: { - price?: U128, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "start_sale", args, ...options}); - } - start_saleTx(args: { - price?: U128, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("start_sale", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async nft_mint_many(args: { - num: number, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.nft_mint_manyRaw(args, options)); - } - nft_mint_manyRaw(args: { - num: number, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "nft_mint_many", args, ...options}); - } - nft_mint_manyTx(args: { - num: number, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("nft_mint_many", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async update_uri(args: { - uri: string, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.update_uriRaw(args, options)); - } - update_uriRaw(args: { - uri: string, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "update_uri", args, ...options}); - } - update_uriTx(args: { - uri: string, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("update_uri", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async nft_transfer_call(args: { - receiver_id: AccountId, - token_id: TokenId, - approval_id?: u64, - memo?: string, - msg: string, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.nft_transfer_callRaw(args, options)); - } - nft_transfer_callRaw(args: { - receiver_id: AccountId, - token_id: TokenId, - approval_id?: u64, - memo?: string, - msg: string, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "nft_transfer_call", args, ...options}); - } - nft_transfer_callTx(args: { - receiver_id: AccountId, - token_id: TokenId, - approval_id?: u64, - memo?: string, - msg: string, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("nft_transfer_call", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - nft_payout(args: { - token_id: string, - balance: U128, - max_len_payout?: number, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "nft_payout", args, options); - } - async nft_transfer_payout(args: { - receiver_id: AccountId, - token_id: string, - approval_id?: u64, - memo?: string, - balance: U128, - max_len_payout?: number, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.nft_transfer_payoutRaw(args, options)); - } - nft_transfer_payoutRaw(args: { - receiver_id: AccountId, - token_id: string, - approval_id?: u64, - memo?: string, - balance: U128, - max_len_payout?: number, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "nft_transfer_payout", args, ...options}); - } - nft_transfer_payoutTx(args: { - receiver_id: AccountId, - token_id: string, - approval_id?: u64, - memo?: string, - balance: U128, - max_len_payout?: number, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("nft_transfer_payout", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) + check_key( + args: { + public_key: PublicKey + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "check_key", + args, + options + ) + } + async update_allowance( + args: { + allowance: number + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.update_allowanceRaw(args, options) + ) + } + update_allowanceRaw( + args: { + allowance: number + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "update_allowance", + args, + ...options, + }) + } + update_allowanceTx( + args: { + allowance: number + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "update_allowance", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + whitelisted( + args: { + account_id: AccountId + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "whitelisted", + args, + options + ) + } + get_sale_info( + args: {} = {}, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "get_sale_info", + args, + options + ) + } + get_cheddar_storage_balance(account_id: string): Promise { + return this.account.viewFunction( + "token-v3.cheddar.testnet", + "storage_balance_of", + { + account_id, + } + ) + } + cost_per_token( + args: { + minter: AccountId + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "cost_per_token", + args, + options + ) + } + async transfer_ownership( + args: { + new_owner: AccountId + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.transfer_ownershipRaw(args, options) + ) + } + transfer_ownershipRaw( + args: { + new_owner: AccountId + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "transfer_ownership", + args, + ...options, + }) + } + transfer_ownershipTx( + args: { + new_owner: AccountId + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "transfer_ownership", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + nft_total_supply( + args: {} = {}, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "nft_total_supply", + args, + options + ) + } + nft_tokens( + args: { + from_index?: U128 + limit?: u64 + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "nft_tokens", + args, + options + ) + } + nft_token( + args: { + token_id: TokenId + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "nft_token", + args, + options + ) + } + async close_contract( + args: {} = {}, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.close_contractRaw(args, options) + ) + } + close_contractRaw( + args: {} = {}, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "close_contract", + args, + ...options, + }) + } + close_contractTx( + args: {} = {}, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "close_contract", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async nft_approve( + args: { + token_id: TokenId + account_id: AccountId + msg?: string + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.nft_approveRaw(args, options) + ) + } + nft_approveRaw( + args: { + token_id: TokenId + account_id: AccountId + msg?: string + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "nft_approve", + args, + ...options, + }) + } + nft_approveTx( + args: { + token_id: TokenId + account_id: AccountId + msg?: string + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "nft_approve", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async start_sale( + args: { + price?: U128 + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.start_saleRaw(args, options) + ) + } + start_saleRaw( + args: { + price?: U128 + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "start_sale", + args, + ...options, + }) + } + start_saleTx( + args: { + price?: U128 + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "start_sale", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async nft_mint_many( + args: { + num: number + with_cheddar?: boolean + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.nft_mint_manyRaw(args, options) + ) + } + nft_mint_manyRaw( + args: { + num: number + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "nft_mint_many", + args, + ...options, + }) + } + nft_mint_manyTx( + args: { + num: number + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "nft_mint_many", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async update_uri( + args: { + uri: string + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.update_uriRaw(args, options) + ) + } + update_uriRaw( + args: { + uri: string + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "update_uri", + args, + ...options, + }) + } + update_uriTx( + args: { + uri: string + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "update_uri", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async nft_transfer_call( + args: { + receiver_id: AccountId + token_id: TokenId + approval_id?: u64 + memo?: string + msg: string + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.nft_transfer_callRaw(args, options) + ) + } + nft_transfer_callRaw( + args: { + receiver_id: AccountId + token_id: TokenId + approval_id?: u64 + memo?: string + msg: string + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "nft_transfer_call", + args, + ...options, + }) + } + nft_transfer_callTx( + args: { + receiver_id: AccountId + token_id: TokenId + approval_id?: u64 + memo?: string + msg: string + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "nft_transfer_call", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + nft_payout( + args: { + token_id: string + balance: U128 + max_len_payout?: number + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "nft_payout", + args, + options + ) + } + async nft_transfer_payout( + args: { + receiver_id: AccountId + token_id: string + approval_id?: u64 + memo?: string + balance: U128 + max_len_payout?: number + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.nft_transfer_payoutRaw(args, options) + ) + } + nft_transfer_payoutRaw( + args: { + receiver_id: AccountId + token_id: string + approval_id?: u64 + memo?: string + balance: U128 + max_len_payout?: number + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "nft_transfer_payout", + args, + ...options, + }) + } + nft_transfer_payoutTx( + args: { + receiver_id: AccountId + token_id: string + approval_id?: u64 + memo?: string + balance: U128 + max_len_payout?: number + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "nft_transfer_payout", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) } /** - * Returns the balance associated with given key. - */ - get_key_balance(args: { - } = {}, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "get_key_balance", args, options); + * Returns the balance associated with given key. + */ + get_key_balance(args: {} = {}, options?: ViewFunctionOptions): Promise { + return this.account.viewFunction( + this.contractId, + "get_key_balance", + args, + options + ) } /** - * Create a pending token that can be claimed with corresponding private key - */ - async create_linkdrop(args: { - public_key: PublicKey, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.create_linkdropRaw(args, options)); + * Create a pending token that can be claimed with corresponding private key + */ + async create_linkdrop( + args: { + public_key: PublicKey + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.create_linkdropRaw(args, options) + ) } /** - * Create a pending token that can be claimed with corresponding private key - */ - create_linkdropRaw(args: { - public_key: PublicKey, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "create_linkdrop", args, ...options}); + * Create a pending token that can be claimed with corresponding private key + */ + create_linkdropRaw( + args: { + public_key: PublicKey + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "create_linkdrop", + args, + ...options, + }) } /** - * Create a pending token that can be claimed with corresponding private key - */ - create_linkdropTx(args: { - public_key: PublicKey, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("create_linkdrop", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async add_whitelist_accounts(args: { - accounts: AccountId[], - allowance?: number, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.add_whitelist_accountsRaw(args, options)); - } - add_whitelist_accountsRaw(args: { - accounts: AccountId[], - allowance?: number, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "add_whitelist_accounts", args, ...options}); - } - add_whitelist_accountsTx(args: { - accounts: AccountId[], - allowance?: number, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("add_whitelist_accounts", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async new(args: { - owner_id: AccountId, - metadata: NftContractMetadata, - size: number, - sale: Sale, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.newRaw(args, options)); - } - newRaw(args: { - owner_id: AccountId, - metadata: NftContractMetadata, - size: number, - sale: Sale, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "new", args, ...options}); - } - newTx(args: { - owner_id: AccountId, - metadata: NftContractMetadata, - size: number, - sale: Sale, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("new", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async start_presale(args: { - public_sale_start?: Duration, - presale_price?: U128, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.start_presaleRaw(args, options)); - } - start_presaleRaw(args: { - public_sale_start?: Duration, - presale_price?: U128, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "start_presale", args, ...options}); - } - start_presaleTx(args: { - public_sale_start?: Duration, - presale_price?: U128, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("start_presale", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - token_storage_cost(args: { - } = {}, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "token_storage_cost", args, options); - } - async nft_transfer(args: { - receiver_id: AccountId, - token_id: TokenId, - approval_id?: u64, - memo?: string, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.nft_transferRaw(args, options)); - } - nft_transferRaw(args: { - receiver_id: AccountId, - token_id: TokenId, - approval_id?: u64, - memo?: string, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "nft_transfer", args, ...options}); - } - nft_transferTx(args: { - receiver_id: AccountId, - token_id: TokenId, - approval_id?: u64, - memo?: string, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("nft_transfer", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async nft_revoke_all(args: { - token_id: TokenId, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.nft_revoke_allRaw(args, options)); - } - nft_revoke_allRaw(args: { - token_id: TokenId, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "nft_revoke_all", args, ...options}); - } - nft_revoke_allTx(args: { - token_id: TokenId, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("nft_revoke_all", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - cost_of_linkdrop(args: { - minter: AccountId, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "cost_of_linkdrop", args, options); - } - total_cost(args: { - num: number, - minter: AccountId, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "total_cost", args, options); - } - get_linkdrop_contract(args: { - } = {}, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "get_linkdrop_contract", args, options); - } - async new_default_meta(args: { - owner_id: AccountId, - metadata: InitialMetadata, - size: number, - sale?: Sale, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.new_default_metaRaw(args, options)); - } - new_default_metaRaw(args: { - owner_id: AccountId, - metadata: InitialMetadata, - size: number, - sale?: Sale, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "new_default_meta", args, ...options}); - } - new_default_metaTx(args: { - owner_id: AccountId, - metadata: InitialMetadata, - size: number, - sale?: Sale, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("new_default_meta", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async nft_revoke(args: { - token_id: TokenId, - account_id: AccountId, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.nft_revokeRaw(args, options)); - } - nft_revokeRaw(args: { - token_id: TokenId, - account_id: AccountId, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "nft_revoke", args, ...options}); - } - nft_revokeTx(args: { - token_id: TokenId, - account_id: AccountId, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("nft_revoke", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - nft_metadata(args: { - } = {}, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "nft_metadata", args, options); - } - mint_rate_limit(args: { - } = {}, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "mint_rate_limit", args, options); - } - nft_is_approved(args: { - token_id: TokenId, - approved_account_id: AccountId, - approval_id?: u64, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "nft_is_approved", args, options); - } - remaining_allowance(args: { - account_id: AccountId, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "remaining_allowance", args, options); - } - async nft_mint(args: { - token_id: TokenId, - token_owner_id: AccountId, - token_metadata: TokenMetadata, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.nft_mintRaw(args, options)); - } - nft_mintRaw(args: { - token_id: TokenId, - token_owner_id: AccountId, - token_metadata: TokenMetadata, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "nft_mint", args, ...options}); - } - nft_mintTx(args: { - token_id: TokenId, - token_owner_id: AccountId, - token_metadata: TokenMetadata, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("nft_mint", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - get_user_sale_info(args: { - account_id: AccountId, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "get_user_sale_info", args, options); - } - nft_tokens_for_owner(args: { - account_id: AccountId, - from_index?: U128, - limit?: u64, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "nft_tokens_for_owner", args, options); - } - async add_whitelist_account_ungaurded(args: { - account_id: AccountId, - allowance: number, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.add_whitelist_account_ungaurdedRaw(args, options)); - } - add_whitelist_account_ungaurdedRaw(args: { - account_id: AccountId, - allowance: number, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "add_whitelist_account_ungaurded", args, ...options}); - } - add_whitelist_account_ungaurdedTx(args: { - account_id: AccountId, - allowance: number, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("add_whitelist_account_ungaurded", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - tokens_left(args: { - } = {}, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "tokens_left", args, options); - } - nft_supply_for_owner(args: { - account_id: AccountId, - }, options?: ViewFunctionOptions): Promise { - return this.account.viewFunction(this.contractId, "nft_supply_for_owner", args, options); - } - async update_royalties(args: { - royalties: Royalties, - }, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.update_royaltiesRaw(args, options)); - } - update_royaltiesRaw(args: { - royalties: Royalties, - }, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "update_royalties", args, ...options}); - } - update_royaltiesTx(args: { - royalties: Royalties, - }, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("update_royalties", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) - } - async nft_mint_one(args: { - } = {}, options?: ChangeMethodOptions): Promise { - return providers.getTransactionLastResult(await this.nft_mint_oneRaw(args, options)); - } - nft_mint_oneRaw(args: { - } = {}, options?: ChangeMethodOptions): Promise { - return this.account.functionCall({contractId: this.contractId, methodName: "nft_mint_one", args, ...options}); - } - nft_mint_oneTx(args: { - } = {}, options?: ChangeMethodOptions): transactions.Action { - return transactions.functionCall("nft_mint_one", args, options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, options?.attachedDeposit ?? new BN(0)) + * Create a pending token that can be claimed with corresponding private key + */ + create_linkdropTx( + args: { + public_key: PublicKey + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "create_linkdrop", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async add_whitelist_accounts( + args: { + accounts: AccountId[] + allowance?: number + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.add_whitelist_accountsRaw(args, options) + ) + } + add_whitelist_accountsRaw( + args: { + accounts: AccountId[] + allowance?: number + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "add_whitelist_accounts", + args, + ...options, + }) + } + add_whitelist_accountsTx( + args: { + accounts: AccountId[] + allowance?: number + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "add_whitelist_accounts", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async new( + args: { + owner_id: AccountId + metadata: NftContractMetadata + size: number + sale: Sale + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult(await this.newRaw(args, options)) + } + newRaw( + args: { + owner_id: AccountId + metadata: NftContractMetadata + size: number + sale: Sale + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "new", + args, + ...options, + }) + } + newTx( + args: { + owner_id: AccountId + metadata: NftContractMetadata + size: number + sale: Sale + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "new", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async start_presale( + args: { + public_sale_start?: Duration + presale_price?: U128 + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.start_presaleRaw(args, options) + ) + } + start_presaleRaw( + args: { + public_sale_start?: Duration + presale_price?: U128 + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "start_presale", + args, + ...options, + }) + } + start_presaleTx( + args: { + public_sale_start?: Duration + presale_price?: U128 + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "start_presale", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + token_storage_cost( + args: {} = {}, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "token_storage_cost", + args, + options + ) + } + async nft_transfer( + args: { + receiver_id: AccountId + token_id: TokenId + approval_id?: u64 + memo?: string + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.nft_transferRaw(args, options) + ) + } + nft_transferRaw( + args: { + receiver_id: AccountId + token_id: TokenId + approval_id?: u64 + memo?: string + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "nft_transfer", + args, + ...options, + }) + } + nft_transferTx( + args: { + receiver_id: AccountId + token_id: TokenId + approval_id?: u64 + memo?: string + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "nft_transfer", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async nft_revoke_all( + args: { + token_id: TokenId + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.nft_revoke_allRaw(args, options) + ) + } + nft_revoke_allRaw( + args: { + token_id: TokenId + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "nft_revoke_all", + args, + ...options, + }) + } + nft_revoke_allTx( + args: { + token_id: TokenId + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "nft_revoke_all", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + cost_of_linkdrop( + args: { + minter: AccountId + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "cost_of_linkdrop", + args, + options + ) + } + total_cost( + args: { + num: number + minter: AccountId + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "total_cost", + args, + options + ) + } + get_linkdrop_contract( + args: {} = {}, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "get_linkdrop_contract", + args, + options + ) + } + async new_default_meta( + args: { + owner_id: AccountId + metadata: InitialMetadata + size: number + sale?: Sale + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.new_default_metaRaw(args, options) + ) + } + new_default_metaRaw( + args: { + owner_id: AccountId + metadata: InitialMetadata + size: number + sale?: Sale + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "new_default_meta", + args, + ...options, + }) + } + new_default_metaTx( + args: { + owner_id: AccountId + metadata: InitialMetadata + size: number + sale?: Sale + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "new_default_meta", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async nft_revoke( + args: { + token_id: TokenId + account_id: AccountId + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.nft_revokeRaw(args, options) + ) + } + nft_revokeRaw( + args: { + token_id: TokenId + account_id: AccountId + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "nft_revoke", + args, + ...options, + }) + } + nft_revokeTx( + args: { + token_id: TokenId + account_id: AccountId + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "nft_revoke", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + nft_metadata( + args: {} = {}, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "nft_metadata", + args, + options + ) + } + mint_rate_limit( + args: {} = {}, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "mint_rate_limit", + args, + options + ) + } + nft_is_approved( + args: { + token_id: TokenId + approved_account_id: AccountId + approval_id?: u64 + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "nft_is_approved", + args, + options + ) + } + remaining_allowance( + args: { + account_id: AccountId + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "remaining_allowance", + args, + options + ) + } + async nft_mint( + args: { + token_id: TokenId + token_owner_id: AccountId + token_metadata: TokenMetadata + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.nft_mintRaw(args, options) + ) + } + nft_mintRaw( + args: { + token_id: TokenId + token_owner_id: AccountId + token_metadata: TokenMetadata + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "nft_mint", + args, + ...options, + }) + } + nft_mintTx( + args: { + token_id: TokenId + token_owner_id: AccountId + token_metadata: TokenMetadata + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "nft_mint", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + get_user_sale_info( + args: { + account_id: AccountId + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "get_user_sale_info", + args, + options + ) + } + nft_tokens_for_owner( + args: { + account_id: AccountId + from_index?: U128 + limit?: u64 + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "nft_tokens_for_owner", + args, + options + ) + } + async add_whitelist_account_ungaurded( + args: { + account_id: AccountId + allowance: number + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.add_whitelist_account_ungaurdedRaw(args, options) + ) + } + add_whitelist_account_ungaurdedRaw( + args: { + account_id: AccountId + allowance: number + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "add_whitelist_account_ungaurded", + args, + ...options, + }) + } + add_whitelist_account_ungaurdedTx( + args: { + account_id: AccountId + allowance: number + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "add_whitelist_account_ungaurded", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + tokens_left(args: {} = {}, options?: ViewFunctionOptions): Promise { + return this.account.viewFunction( + this.contractId, + "tokens_left", + args, + options + ) + } + nft_supply_for_owner( + args: { + account_id: AccountId + }, + options?: ViewFunctionOptions + ): Promise { + return this.account.viewFunction( + this.contractId, + "nft_supply_for_owner", + args, + options + ) + } + async update_royalties( + args: { + royalties: Royalties + }, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.update_royaltiesRaw(args, options) + ) + } + update_royaltiesRaw( + args: { + royalties: Royalties + }, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "update_royalties", + args, + ...options, + }) + } + update_royaltiesTx( + args: { + royalties: Royalties + }, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "update_royalties", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) + } + async nft_mint_one( + args: {} = {}, + options?: ChangeMethodOptions + ): Promise { + return providers.getTransactionLastResult( + await this.nft_mint_oneRaw(args, options) + ) + } + nft_mint_oneRaw( + args: {} = {}, + options?: ChangeMethodOptions + ): Promise { + return this.account.functionCall({ + contractId: this.contractId, + methodName: "nft_mint_one", + args, + ...options, + }) + } + nft_mint_oneTx( + args: {} = {}, + options?: ChangeMethodOptions + ): transactions.Action { + return transactions.functionCall( + "nft_mint_one", + args, + options?.gas ?? DEFAULT_FUNCTION_CALL_GAS, + options?.attachedDeposit ?? new BN(0) + ) } } diff --git a/src/near/index.ts b/src/near/index.ts index 680cbd1..7a3ba74 100644 --- a/src/near/index.ts +++ b/src/near/index.ts @@ -8,7 +8,7 @@ import { Buffer } from "buffer" if (typeof window !== "undefined") window.Buffer = Buffer if (typeof global !== "undefined") global.Buffer = Buffer -const nearConfig = /near$/.test(contractName) +export const nearConfig = /near$/.test(contractName) ? { networkId: "mainnet", nodeUrl: "https://rpc.mainnet.near.org", @@ -35,9 +35,10 @@ if (!nearConfig) { */ export const near = new naj.Near({ ...nearConfig, - keyStore: typeof window === "undefined" - ? new naj.keyStores.InMemoryKeyStore() - : new naj.keyStores.BrowserLocalStorageKeyStore() + keyStore: + typeof window === "undefined" + ? new naj.keyStores.InMemoryKeyStore() + : new naj.keyStores.BrowserLocalStorageKeyStore(), }) /** @@ -47,4 +48,4 @@ export const wallet = new naj.WalletConnection(near) export function signIn() { wallet.requestSignIn({ contractId: settings.contractName }) -} \ No newline at end of file +} diff --git a/src/templates/[locale].tsx b/src/templates/[locale].tsx index 857f900..69c5507 100644 --- a/src/templates/[locale].tsx +++ b/src/templates/[locale].tsx @@ -4,7 +4,7 @@ import { PageProps, navigate } from "gatsby" import * as naj from "near-api-js" import { near, wallet } from "../near" -import { fill } from '../../lib/locales/runtimeUtils' +import { fill } from "../../lib/locales/runtimeUtils" import Hero from "../components/hero" import MyNFTs from "../components/my-nfts" import Section from "../components/section" @@ -15,18 +15,21 @@ import Image from "../components/image" import type { DecoratedLocale } from "../../lib/locales" import useTenk from "../hooks/useTenk" import useImageData from "../hooks/useImageData" -import useHeroStatuses from '../hooks/useHeroStatuses' +import useHeroStatuses from "../hooks/useHeroStatuses" import { Token } from "../near/contracts/tenk" +import ErrorModal from "../components/errorModal" type PageContext = { locale: DecoratedLocale } function hasSuccessValue(obj: {}): obj is { SuccessValue: string } { - return 'SuccessValue' in obj + return "SuccessValue" in obj } -async function getTokenIDsForTxHash(txHash: string): Promise { +async function getTokenIDsForTxHash( + txHash: string +): Promise { const rpc = new naj.providers.JsonRpcProvider(near.config.nodeUrl) const tx = await rpc.txStatus(txHash, wallet.getAccountId()) if (!hasSuccessValue(tx.status)) return undefined @@ -38,13 +41,18 @@ async function getTokenIDsForTxHash(txHash: string): Promise> = ({ location, pageContext: { locale } }) => { - +const Landing: React.FC> = ({ + location, + pageContext: { locale }, +}) => { + const [showConnectModal, setShowConnectModal] = useState(false) + const [error, setError] = useState("") const tenkData = useTenk() - const { image } = useImageData(settings.image) + const image = { publicURL: undefined } //const { image } = useImageData(settings.image) const params = new URLSearchParams(location.search) - const transactionHashes = params.get('transactionHashes') ?? undefined + + const transactionHashes = params.get("transactionHashes") ?? undefined const [tokensMinted, setTokensMinted] = useState() const { saleStatus, userStatus } = useHeroStatuses() @@ -55,15 +63,28 @@ const Landing: React.FC> = ({ location, pageContext: saleStatus, userStatus, } - + useEffect(() => { if (!transactionHashes) return getTokenIDsForTxHash(transactionHashes).then(setTokensMinted) }, [transactionHashes]) + useEffect(() => { + if (params.get("errorCode")) { + for (var pair of params.entries()) { + console.log(pair[0] + ", " + pair[1]) + } + + setError(params.get("errorCode") ?? "") + } + }, []) + return ( <> - + > = ({ location, pageContext: favicon={tenkData.contractMetadata?.icon} image={image?.publicURL ?? undefined} /> - + {locale.extraSections?.map((section, i) => (
{section.blocks && (
{section.blocks.map(({ linkTo, text, image }, j) => { - const El = linkTo ? 'a' : 'div' - const props = linkTo && { href: linkTo, target: '_blank' } + const El = linkTo ? "a" : "div" + const props = linkTo && { href: linkTo, target: "_blank" } return ( {image && ( @@ -106,6 +131,7 @@ const Landing: React.FC> = ({ location, pageContext: highlight={tokensMinted} /> )} + ) } diff --git a/stale-data-from-build-time.json b/stale-data-from-build-time.json index e3ca8d6..37472f5 100644 --- a/stale-data-from-build-time.json +++ b/stale-data-from-build-time.json @@ -1 +1 @@ -{"saleInfo":{"status":"Open","presale_start":8640000000000000,"sale_start":1649267295545,"token_final_supply":10000,"price":"2000000000000000000000000"},"contractMetadata":{"spec":"nft-1.0.0","name":"TENK NFT","symbol":"TENK","icon":"data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4gKgSUNDX1BST0ZJTEUAAQEAAAKQbGNtcwQwAABtbnRyUkdCIFhZWiAAAAAAAAAAAAAAAABhY3NwQVBQTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAAADhjcHJ0AAABQAAAAE53dHB0AAABkAAAABRjaGFkAAABpAAAACxyWFlaAAAB0AAAABRiWFlaAAAB5AAAABRnWFlaAAAB+AAAABRyVFJDAAACDAAAACBnVFJDAAACLAAAACBiVFJDAAACTAAAACBjaHJtAAACbAAAACRtbHVjAAAAAAAAAAEAAAAMZW5VUwAAABwAAAAcAHMAUgBHAEIAIABiAHUAaQBsAHQALQBpAG4AAG1sdWMAAAAAAAAAAQAAAAxlblVTAAAAMgAAABwATgBvACAAYwBvAHAAeQByAGkAZwBoAHQALAAgAHUAcwBlACAAZgByAGUAZQBsAHkAAAAAWFlaIAAAAAAAAPbWAAEAAAAA0y1zZjMyAAAAAAABDEoAAAXj///zKgAAB5sAAP2H///7ov///aMAAAPYAADAlFhZWiAAAAAAAABvlAAAOO4AAAOQWFlaIAAAAAAAACSdAAAPgwAAtr5YWVogAAAAAAAAYqUAALeQAAAY3nBhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbcGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltwYXJhAAAAAAADAAAAAmZmAADypwAADVkAABPQAAAKW2Nocm0AAAAAAAMAAAAAo9cAAFR7AABMzQAAmZoAACZmAAAPXP/bAEMABQMEBAQDBQQEBAUFBQYHDAgHBwcHDwsLCQwRDxISEQ8RERMWHBcTFBoVEREYIRgaHR0fHx8TFyIkIh4kHB4fHv/bAEMBBQUFBwYHDggIDh4UERQeHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHv/CABEIAZABkAMBIgACEQEDEQH/xAAcAAEBAAIDAQEAAAAAAAAAAAAAAQIHBAUGAwj/xAAbAQEAAwEBAQEAAAAAAAAAAAAAAwQFAgEGB//aAAwDAQACEAMQAAAB+KPr/wA8qCoKgqCoKgqCoKgqCoKgqCoKgqCoKgqCoKgqCoKgjF53kxGTEZMRkxGTEZMRkxGTEZMRkxGTEZMRkxGTEZMRkxGTEZMRkxGTEZMRkxGTEZMREedVBUFQVBUFQVBUFTLz2O37iC15BsDmwWdZNpuO9WNm8LvjX71/UT1unXGerUe+VBUFQVBUFQVBUER53UFQVBUFQVBU9BHJ0He+97fL2vLei5Ez9aiOcAAADj+d9TZINVdFvHqNDJ1K9B57UxaiSOoKgqCoKgqCMTrJiMmIyYjJiMmIy5fY7MoaXS+lsxfoqOJQAAAB5juLm+K8173UxO19CZW5RzJPNelvcWluJujWe1870bFfzcmIyYjJiMmIyYiI86qCoKgqC+ox2dma8+hj74PQAE4nk5YPbtW4Wae1Xg/YVrf20vsfXGjk93tDrefS0ftfN8TiT17znfxyfQcyvn9DzWHl96ax2MDzKNPIqCoKgqCI87qCoKgvd9XuOjocv6GH9GD0AADpes9bJa+vOFsDprdLXePvvN3M+Z+bzlh974Z62KfxTYfoeetP87a3YQWel7ozdajzt8/oeai6PdWnNz5z4IvZ9QVBUER51UFQVO6479n6+X5z6oOJQAAAPlrXkdLp4/S5+19zNX1U9H4TuPCcvOzV5P37bratvnd9rB15vf66S2jma3eCreAAeQ9c7i0K7npfo/lajvioKgxR51UFQXbWuN1ZevRl7IAAAAHQd9XvE1r6fUmhmX33B5Nit4vaep9+xya42RpvcFa30Pk9nSOXQ303V5i/neh7Hxns8zWojmAA83qXfuldTG6xGpkVBUER53UFQbE951XbfO/TBFOAAAAABrHy/den2ML0mrNx6Ur2pu3T+4OfdX+u6/o+udnDP1AAAAAJ4P3vVS19KI+i+aqCoIxOsmIy5fC9PFLt0fO/TAAAAAAHFyeeZ9b1fKki5eqNr+bkj6n3XmPT89dTqXd+hLdPfjp+3z9GjzsAAAADRHE9L5j6L5nJiliyYgxed5MRl7nwmxKtnYQw98AAAfJ59OFfr75wuVy+E85M+N89+304b1zB50A03uTWVyl3fstR7c46CtbAAAAA1p4bYWu9zAyYrVbJiIjzqoLtHVuz6dv3IxtsAQrhc15wvh2j3zjY4c44v1zy89cHnD49R3vTdcdyOZB0/vPb+D6PhXqHW730Dvb3zkjP0gAAAAPE6u2drDaxKi3UqCIdVBdia69tVsbUGJuAAY9J3s954fNPPQegATxPt/n1H0XodU7Okjuv9j+MNU9v1PuNjJ8NuvSe169j2YydYAAAADXWu/a+J28OotV6gxRz3UF9B577cd/od8/p8/ug9AAAAAAHRe88XX3x6fVy/adb5ZNFny+Cmi+209Te0rWNsjG2AAAAB83ml/P/AF+P0GFUd8VBEedVBUG6fTap2th7AQzgAAAAATwfvNOWK3lUbWTUFQfTYutva1rG2hja4AAADzPptUzQeHRuY9QVBEO6gqDlb/8Azts+hb2EMzRAAAA4fL1T6qaD1s1l52TnZOlo0aNRPFUFQXu+jce7h7rQipa/SL87d1DLu9qzvYJfa3ruxgmB7x9AbF1hp51RfqVBUEYvOsmIyYjLl8J57+ieXqfbGJqBFKPmfSeK8tPDt/iaM6meH1PmMWhTyYu+cmIyYjJiMmIyYjJiMmIyYjL0HnXHW4vT/nbn07OPExXquTF75kxGTERHnVQVBUF3RpZDLvXzGsUXfrfOcVPFUScVBUFQVBUFQVBUFQVBUFQVBUFQVBUER53UFQVBUHZe68lvGjZ122JnWm1w2V8TXbYh5rtsQa7bEGu2xBp/yG2fF3a+XpNl/GnY102I8a7bE5D3QfoflsKxDrTyO3dQzx3LDcPXPRzZ+madjyuMaVSoKgqCoMUedVBUFQVBUF2brHZteX3X55/Q354hkqL1bm/fZnu6Fn88P0O49/PD9Dj847B7Pq5Odnfmf9MfmTn2o0K3Z/oj87fonOt6e2HrzYXLpNP7f6OXjsdi3XVWfpPCxq0qjvmoKgqCoIh3UFQVBUFQXZ2sNn15Pdfnf9EfnaHuovQbi9D4/YOPc1M9Ynj8m9YNe+q4HO752f8AmP8ATn5i56qNCv2f6K/On6LzbOndh672J45XZ5eLrS+r4+hN8S8aN6n9B6KvQcFFmOoKgqCoIxedZMRkxGTEZMRkxGXuvBuPd0aZxc+5MUvPJ2tqBF1vhodB3vhoc92XwPBpeN76KxesmKbjsNz6IQd+t9np8bn1Bx3vmXcdKk53p0OqVeXJitRZMRkxGTEZMRB50AAAAAAAAAAAAAAAAAAAAAAABijzuoKgqCoKgqCoKgqCoKgqCoKgqCoKgqCoKgqCoKgqD//EAC0QAAEDAwIGAwABBQEBAAAAAAQCAwUAAQYTMBIUFiA1QBARFSEiJDRQgDFg/9oACAEBAAEFAv8Ag1NlKUzGGu01AvXpEExwtwwSbfkgV+SBTkMEqy4JjhdgXrU9GGtUqykq/wBIJFlv0NCDIppptq206027YmEGXRcWWx/oQYogmwUeML6JseMVR0UQNb3BR3SXI6JZG3j5BgOiJkx5UUI60jskYlkmih3RnPZi410xQw7QzW7NSPKJVe6r44NfS7iR2iGpSNdDV68PGKKulNkp3Xl2aaIeW+/DhMmLTayU96k2UmYjFC39WFjblLta1rbBBDI6CZ9Fqdmzl03MnpUNP0MQySjJXLojRmVPuxglgxXFobQ/Mgt1++JTEyC7SFJWnsva17TUbcVfpxACjX0JshOyVGiEKRBCWWRAvcb0M+0Ny5HGw86O7KyHOj461ZZ0lNpRT77z6/hCbrWwQUA9FHc6z2LTZaZcBQT/AKIzKyHwhmxR9wkwYenZwFF1TwSrGNxbtKtdKrOLs2ww8QsKDWpLWPs2rkBVVyQewaM2UOSysd/0McC0R9txaW0Scy68qlpUi4sWW+y9GPMN3+vhmSNZb/YkabyAqyhZwV2m1pWnYyMLWH34YTnDNzJjLqeGjSCAxcfX92ZZs7KTdk3Upx50luzF0jKuAHFDnAuQ5bXyGYQIuMkWTk7EyJyZm9j4vLgbjUUIl34npTVVQVrBQ9v63UgXRBYqRwv0dEilKk4YlLvAvUHeWw9HlIMG78gF5gDdihuaO358u4oVQAbRjOWPX1Ph77j5hN7KT8PDsPKLgWHLwgpgBGxKjcqduYmx/Rv5Y5e5o7Dj6gRkCDTy7Llo7hUTWVM8B2PkXIjt3LWP6NyKZ5eO38lTdMtjIOkzUum6JPHUcctWVtcQOKPcBe7LM68dthN6xnoSsbcyU+MpZ05LFb6jdSzWtGxpNhC941vRM2sZb45bebS4lyzranJAtIbIruuPU+HzYONNaUX8Et6JEO9rxu7kzfBLbWIN/ZG2Y2p4WOaeZGSzaxH3bi4U8arJV35C3wS+LfaRN3L2/ojaw238bLi0tpdQ2UK2lDSKHdW5Wg1zNlO67zDbrjza1OduXt/RWLvacnu5lb+NrEP8DYdU/Z6REQaww3Zlpxltb5LyWGkK400Xciza+O6Ia73KduXWbUPH6nO7uX/4G1h6v7Ttv/4G446N8ikIJT3WvoyvzJHsAtP5Aau9zW3Ylp99mwzlnh9zMFf2m1hnff7+ot4x2iGdW+xlRD7CIExZoNOoutOUjsMjVGjWftWPru5EbmZ7eHrVY/0VpStKXVwUsy4h5qsqHdfFVa6VY3ouC1iKr3jNzMFquftY85ZuY9LIALmiRMu9H2dyUm9yZ095Cr3VcJ+4xbakpXjJtiE7mQuWcmNplxTTrarLR6EwemPFemJF2nXnXb9uH+S23FWQh5xTru3jT+vE+hlDabNdyE3UrFxNEnbyV/QidzECeAv0MnM5mQ78O8nt5eTxl7gzyhyGHEvM7SX2VEfMrKjDibEKdaPLZyKOXdmRBe2X3EsskvKII3cPM42dpb3KZdIToY1Lyhf0fMGmI3GX3maZm5JqmcnftQ+SBLoUwUrszAzhZ3gyFilCvNkD7DxDDNZK8K9IeoJMyA14yeGKuU82OOYQsonfxSR0nuxa0oQdkQjFyMkOXT8ke/f2FmFLF9HHpOxwxMzHMURlCKeyGScu++8/f/6CNG5w3pWula6VrpWula6VrpWula6VrpWula6VrpWpmF/OFqNhDDUdK10rXStdK10rUyB+cVH47zYc1C/nDfCEqWpnF3FNEY22wyr64vTx29rTOszWszSVpVV/4trM1rM1rM1rM1rM1rM1rM1rM1rM1mLiFRmNhJNkau63a+szWszVnWvus08pj/hs18X8Y3E2EbWqyE5DLc+76+DVLeL+WRiXk8gfXIH1yB9cgfXIH08MSynBvi973v8AEX5Os18rj/hs18XWNQ1m7Vk8tzK/YwWpfxXzhXi+3NfF4L2xfk6zXyuPeGzXxeNQur8ZgaQyj2cFqX8V84T4uaKcCjeqJCuqJCuqD66oPqUmSZBjBe2K8nWbeVx7wsiE2cm38U280tw4Rk0eVAdjyvYwSpfxXzhBCdEhlshnpmOrpmOrpmOrpmOrI4gQAHBO2L8nWbeVx7wtZFMWAQGaQKXGGNHCSwDUgKcK8ET6+JyAgNSM3GOgfI7zjDwGTDOJ/fia/fia/fia/fiayiTBNj8TPEBr9+J7AFpaO/fiaygwc2QhpmNHjD8jCbGedW87UTIPRxKcgirpnDIaSF/4E//EAC0RAAEEAQMDAwMEAwEAAAAAAAEAAgMRBBIgIQUTMRAwMiIjQTNAUWEUFUKB/9oACAEDAQE/Af3NKlSpUqVKlSpUqVKlSpUqVKlSpUqVe0AT4TMGeTw1N6ROV/ppv5Cd0icJ+DOzy1EEef2MUD5TTAoOjjzKVHjxxfEbpMeKX5BT9HHmIqWB8Rp497D6c6b6ncBRRMiGlo3Sytibqcm5M2ZJoaaCjjEbaHpLEyUaXBZnTnQ/U3ke5gYGv65PG2XJji+RX+0htRZMUvxK6o8veIgsTHEEfPn8o5cI/wCkyVj/AIn1z8DR9yPx7WBid51nwEONrsaJxshSYuI27NKTHYzmJ6E57vcfyicjLP8ASZ0uV3nhM6W5vOqimAhtE365+J2Xah4PsRxl7g0KGIRMDRuy8iSRxjiUXTnvFv4UzYGfS3krtm6XbymtBB4TM+Zn9rGzWTceDsmjErC0qSMxuLTv6ZD5ed7WNb4C6hklv22qGLtRmY/+LpzA/VawHfSWH8KXDjk/Cf057OWFQOc5n1DnZ1OHw8bqWNH24wPYMJnySF1AVEGjwumj7doHtZf9HfkR9yMhVthZqeB7LGsa815Ky4u5GQFjCohazwWua8JrtQvfMzS8jbhj7w3HlAAcbc5txWsN+qIb8wfeO3C/V2Wi0HyhRHoU7gg+jnBosqbJZI0tCwCKI35v6u3F4lGwjm1WyRuptBQPsaT5CyG3GVCzU+isLiTflcynaw6XA+y5waLKlmBdqZ5RnkP5Q4NrFcRIN7zqcTugfqYPYyXAM2Y9axunfpYd+I+jp3NmHNp2SweFLKZDsaS02EMt35Qy2/kITsP5QN+mW+zp3jhRP1tv1dOwI5X8BOOo37QcW+E3JNco87KVKlSjeYyjkOKLnO8qlSpUqVKlSpUqVKlSpUqVboWNcOV2WLssXajXajXajXajT4/rpqEDB5XajXZZ/CZG0vIUzQ00FHGXlSMYwePch+YU/o2EEWv8cL/HCa3TJSyPx6QeEz9QqRpc9cRtTiXGz7kXyU/n0Z8eFpk/laZE0HXyp/x6QeE35lGm8prw9SR6fcaaNpztXo1xb4XdK7pWs3ac4u9GvLUHkG05xchwjIT+/pUqVKlSpUqVKlSpUqVKlSpUqVKlSpf/xAAvEQABAwIEBAYBBAMAAAAAAAABAAIDBBESICExEBMwUQUUIjIzQSMVQEJQYXHw/9oACAECAQE/Af7smydVRM3cj4jEF+px9ih4jEU2qifs5A32/YySsjF3FS+JE6RhPlfJ7jmZK+P2lReJHZ4UcrJBdp61TWtj9Ld0+Rzzd2ZjC91mp0EVMzE7UpzsRvwZI5hu1U1a2T0u36lZWYfQzLHBJJ7Qv0+VSQSR+4Lw9oa0yFVMxlf/AIQp5T/FOjcz3DjR1mL0P6VZU8puEb5mzyNFgUyoqTtqmTudpIxGH8WBiAgpR/107xGMbap/iAdph0TiCbjjR1PNbhO/Qe8MbiKkeZHYjmpoGMbzJFJXsafTqonTP1doFzG2usdO42I1T6KF2yqKR0Wu4yRvMbsQTHh7cQz+IS7MGcuLt1Q04d63KWTmyCJV7y3DZVjfUHj7UdTJHsU2vY7R4UrQHenbJ4fLuzPO/HIT0BKIacFUGspJXiB/IiOZTf6zwPwSA5pnYYyei4uc0X2VLJy5LlVBvIbKhs4OYU4YTbPC7EwHLWH8JzbK5OuWhdaWyq2YZc9GfwjLW/FlBtsje/EbW4NBJsFFTSRuDiq8G4Oei+LLVi8RyX0tljcGuuVPHY4hsVTutIFO/Cy4VcLxjPSC0QyvbiaR0WtLjYKGnIbhfqEKaMfSIBFlVsBjvnjbhaBmqGYJCOhSsLpNMlVfl5qdmOQDPWx3GLM6B2lvtNo5DuoIBEMjmh4sU6hZ9J1C/wCinU0g+kQRvwoo7DFnIvopouW63FlNI76TaE/ZTG4G26TmNdun0QJ9KAtp0JohKLJtGwbpsbG7D9xUyvY6zV5iXuvMy915ibuvMTd15ibuvMTd1DKeViejVSOOi8xN3XmZe6kmeImkKmeXsuVNMIxdRSTSu36lV8RVFseElWWuIsvOu7LzruyfJzICVRffCt9wUvwtUEgjiuV6p3qOMRiw6lV8RVFseE2kpusdP2WODsnlpgOFUX3wrPcFL8LU0F/pClhMWqp58Ysd+pIwPbhKiiEe3CSFsm68mxeTYuQ3BgUUIj24SwNk1KdA1zQ1RQtj2TmgixTaVrTcH+4//8QARhAAAQICAwcRBwMDBQEAAAAAAQIDABEEEiETIjAxQVFhECAjMjM0QEJScZGSo7HB0eEUYnJzgaHwgqLCJJPxBUNQY4Bg/9oACAEBAAY/Av8AwbVSCScggSZKQeVZGyvoT8InF+84Tosi1Kl86o3D95jcP3mLEqRzKi8ecB02xsT6FfEJQZslQHJtiqoEEZD/AMLOpc051xspU6egRJptKB7owcnW0rHvCNiKmj0iJ1LonOj/AIELVsTec+UTQia+UrHwGa0SXyk44K07K3nHlw25somc+QQFubI7nOIc2Gks1l5EjHFVqTQORImYutKcW46cilTq60rb2N3OMR54ubyJHPkPCgo3jOVWfmi5spqpw1ya3VQ6sFSiSTjJi7utoq/7d6J8+vubyayYKhNbORWbn4RdngQyP3QEpAAGQYZbisSRMwt5e2UZwQpTt7ab2zpgJSJAZMAUqAIOSLsyCWT+3g12eEmR+6JCwYGu84EDTBFHZKveVZF6pDfwp84mXEr0FPlEqSz9UeUV2XAoRVHHUB4wG0FIJ5RlAb42NRzmCtaglIykxYtTh90RuT/QPOACstn3xAUlQUDlGtkbRF2ZE2T+3gltjSdufCAlIkBYMEVuoUVnjVjAJU6oZiYm060RpFWXfBcUStfIbTOKlwdrSnKqYrtLKFCGOKtM66cnPF1XtGk1p5JwW6JfHlnF9IrvOKWdOqEplM6YvazZypUMf0isUFCxjGT6a0pUJg2GLLWlbQ+HAkst7ZRhLLYxYznOfC7M8hJzZeiJJujnwp84kWXiD7o84r0WkXJR4i0mUVTL6GcKbCpJVKsM8oqstqWdEf1YqT5K74eEbLSHFfCJQkuNBxSRKsrGeeN6sdQYBTLgx4jmOeFMubZJ4D7QsX7os0JwhWsySMZgooxLbWfjHUkoWyB6YW4lsiUqoNlaK9IdZZnkUq37RYZ6gbadCEjIEDyjfH7BGyNNKGiyJOhTJ02iApCgoHKDgfaEC/aFulPAAkjY02r8sL7GgkJTavSYuzSKxryAnkymJ0l4AZkecF25or8qVsFqhyJyuZPpE1FTjivqTFyMi6NvLi6IXSsiXAn8+0IeacLS8SuMJw4V1aqEFdYYjLJq1mVyzjIYvb1wY0HAlIGxqtR5YdKiL92+PhhVPOIurijMlWLo1TRqOrYxtjyvTUFNeSkuidwnmP4TGyLlWNqjCqGZKXVOLKcYhdGJsXfJty/ndqV5XNc7SjLDjzIDiCSZJxj6Rc6przlVlbOEutmSkmEvIsnjGY4BSgL9q+Hjhm2pTTOa+bgBCDJxy9To06irsgybcBChlzj7CGaOMQFc/nTqkpErm5MAcnN0QFAzBxaqVONpUpJmDlEFTCy0c2MQpp0AsrG2SZieBcalJM5o5sK7STjJqDx4A23O9SjFpMVGhWVMCXjCGEZMZznPD5BygfaEsOAqbdNUgfY6iHQBJxP3H4ITW2yDVNksM1SRjBqHwwrDRmDVmZ5zwBZPGAI6I9qcF+4L3QnUpAPLJ6YasmEzJ6NRDoFra8eYH8EOMmWyJmOcYZ9sAk1ZiWcYRloiYUsA83AaOs7nV2TmHnPVutsnUz+os8oWVNXzV6lzODk+2o+iRN4SJZxCHqlaRt5tGHeaAkErIHNg0K5CSrw8cOsqdrJJvRLawpsLBUnbAZIuikLVaLEp0wh4WVxPUNQbI3fJ06ISbZuEqt1XGpzqKKYYcM51ZGeizDLVy0hXh4YN93kpCen/ABhHGkKCStMpkTgIfcujmVVacKeClXwtTOyKs7c0V6orSlOJKAOXXu3tUKkRps84W2Vtqvp3qpy/JYZh3lJKej/ODpJyXvjgqyzITAhSK023EyrJMJZRYEpkkaBqKrMqbkbJ5YNIq7IU1Z6IUgtbHKYXP7Q2tczczWFtkNrS6UhONORWuZdntkVej/MBvI6kjxw1GOS+8MG6f+3wGBQG0JKDtjO0RcXCQKwNkJaTtU2CeaGnlC+bnV+sXRYVVBGITgKGUT1P6ZCFLrDbGyUGoQlWkTipSFFTzaihc/zNrmlVhXQuUucekNFptTikrBqjDNH/ALfA4N5GZyf216HHWi0tWNE8WsUpCViSim+TLXlNkqQiePjJ9COjWVnDNXFQMZg3MNtjJZMw5RXGwlwKC0qHHM7Z6Y2J5xufJVKG3QJBaQrCsozuT+2DpX6fHX2Qv2tm5yMkWY/rDRui0VF1r3LowNGLM035VXzH8Ji6O7dKqpOfUkHFN6Uy8YbWCoulfGcKjKWn6alJUqcmmFL+upRyrNV6DLC0X9Xhg3UTvS3OX1HAilYCgcYMLbkVsLtllq+cJcbUFJUJg6jdxYU4pKsYyCClQIIsIMUuiV0peeSQOaWooE7V0gdAwrSJ3obnL6nBsEmQJq9I4HNA2Zu1OnOILdS6tm2qTKRjYmGkj3pmKtZLXwC2CpRmTjMNvjiGfOIBWiuM04caDLTIblIIy4V8gzANXoGDQ6nbIUFCErSZpUJg8BryrOKsQmCDSSke6JRN1xaz7xnrnPknvGEUtRklImTC3VbZaio4RuZmpu8Ph9pcBdfcGNCENHTWJPhrwkSmc5lCnVUlhSi3K5oWFEc+EckZKcvB4/aeFXRlGx0TTzj8+3AbknaM3o58v5owDnyT3jCIoyTY0Jq5z+ffCoeRtkKnCHUbVYmMGqjhxJdSJlOUaxwtUhtb2JKUqnbgS8W7oCmrji+LjXxJ8oTc6U0a2IVpHowK3V7VAmYW8vbLVPDKoS8aL5HN+d+DUt1Zq15EzyFNnhFVB9oXKd4RL6mLyhgHSucVFrCEZUosnhTcXnG58lUoA9orgZFJBnBu1GbXmqmr5wA6lxo5TKYjYH0LsnIG3o1iaEg2rvl82T80YdukN7ZBhD7ZmlYmMCLs823PlKlF0oyq8036gbCeC2PlxPJcvoDb+wOaTen6wt90yQgTMOUhzbLPAPYnVXjhvNCvXWla1BKRjJiowk0hWgyT0wQ0lpoZDKZEG6Ut20SICpDoHCRRVPrU1OdU8CqOqF3b23vDPFtKQs5kX3dBFHoqjZjWZfaL1xDWhKPOJvOrcIxVlT/+hbo1epXyynG/uy9Y392XrG/uy9Y392XrG/uy9Y392XrG/uy9Y392XrG/uy9Y392XrG/uy9Y392XrG/uy9YS/7TdJrqyqS8dQOWNNnjLy80b+7L1jf3Zesb+7L1jf3Zesb+7L1hLF1uk0Vp1ZQ3SPbKlcTlc5+MJe9puk11ZVJZ9OqEJBUomQAywlTtKDayLU1Jy+8Ked/wBRCUJEyS16wapJTkmJcEo5JkJnujdUdaN1R1ovVBXMYmY3VHWjdUdaN1R1o3VHWjdUdaN1R1o3VHWjdUdaN1R1obCVpOzDEdBgBy1tsV1DPo1JFxIPPG6o60bqjrRIOI6dRv5I7zFG+CG/nDuOqKS+n+oULJ8QecFSiAkWknJFyZmKOjF7xz8Ipn6P5RS/kr7tZWZo7ricU0oJjeVJ/tGN5Un+0Y3lSf7RjeVJ/tGN5Un+0YrPUd5tOKakERTP0fy1Jm06tF+cjv1G/kjvMUb4Ib+cO46iabS03+NtB4unn1PZKMvYU7ZQ458uE0z9H8opfyV92sc+ce4a5v5w7jFM/R/LW0X5yO/Ub+SO8xRvghv5w7jCabS07HjbQeNpOjUTRWwUNui+Xn93hVM/R/KKX8hfdrHPnHuEO0loJK0ylWxY43KjdU+cblRuqfONyo3VPnG5UbqnzgMvIaACq14D5xTP0fy1tF+cjv1G/kjvMUb4YaQ7tEOVyOVYbNRbaFpUtvbAHFCmH0zSekHOILLloxoXyhwmmfo/lFL+Svu1j1E44VdBpGL854Uy8gLQoSIMY3+tGN/rRjf60Y3+tCXmC5WLgTfHQYpn6P5a2i/OR36jfyB3mKN8GpcGCDSVDqaY9paWbpxp8bnhL7X1GY5oLLlhxpVyTCmH0yUOgjOOEUn2p2pXq1b0nPmikNN0ma1tKSBUVjlzaxLzKyhaTMEQBTEllecCafON99mryjffZq8o332avKN99mryhDVGfrqDoVKqRZIxSfanalerVvSc+aN99mry1lHcWZJQ6kk6Jxvvs1eUIdozldAaCZyItmYYZdpFVaU2ioryhRoi7s9iSKpA5zCnXVFS1GZJ1Lq1ak7dGRQgE0kp0FCrIq+1hLqbW1XNXRi/8C//xAAtEAEAAQIEBQMFAQEAAwAAAAABEQAhMUFRYTBxgZGhECDwQLHB0fHhUGBwgP/aAAgBAQABPyH/ANmTwJ9J/wCuas2Alaki5jGHRv4oh2GSfeKMDM7Edoagm/N+Ir5v21837agm/N+ZpyMjsR2gqI3GSPaaki5jGXQv4p1ZshCf8VcUva7GNAzz14i/mlrKygBPDGkrIAk0HPPXib+aXR1d3Mf+DfgLh3eX8VDQrezdscuNPrLSrayd8M6twF07nP8Ar60+jO8hcqi8HGo7PztlxjrlP22lXEhBIZ3fxFEQlZAOhOe/x9kXh51HZ+d86foyvIHP6oA0Xd2ft/KKFxHVdXjRMDOFwGvPT5KkGlEq0tIpuWnnKJ97YuJuOpQFpd3Z+38+oMqaxg8u2/wLC0AQBxpY58WgTWcim21T9whAY8Ls+RnRoBQAgDgFhaFEiU5U1zF59t/j9KaQuxr0NtXpyJGAQBlwdIKyu8jOmsUQWE5MZnaoKxbk+VQpujj7GsPnX/L91r1aMTmYlKix10P4rAa1CPOLsUR4XZPwMqx0eQg61KhhhPzsHoEQRzAQeSQ70NIpEkfayMQhHOnSF3Nejto9Of0eZnHV2b0FQ0BgHCBwEYo5Xij7vEkPYKbJjJBsP2qXKINHzZPA15+mjWKw9uk+yZ0ZYURs1oHmlhQpLBgJ8vSlDhyOE/L7c65yBYcjL1v5KCQeWmkiouBzUDRA126zPt7QqGgcErOzro7t/ogkGMTgbtWEAwL5y4smCib57V6MILnAHhSC2hG4U+C9OYYz16U6RJoDuU6paDcie9aw4hY5uVWoYg9ISFdGhGQyg++acFfDP5GGdfOvxUUe4mEDAvlCgkCMxg7n0PytId8e3ECSWUwCtLvturkfNqWWWjMRdABPCVA18PKblMWi80Fy0arkBnHKomOYj0wUBUY3Hx0olqMku8v2pYcfhT8hWB0KUPB+VpHtj34M1NTU1NTU1K89WP2/fFOYEznB0/O1TLJZxgcTeDvTBzQl6sOzQJJl47MMahTbCE/s54c6n0gLyjAKNPdEpFp3M3W2Vw7LYOVpV799RaLHkMbWRbOOdWyilzyA1NTU6YcW/OPjTHDZZc3NTgyvHRj9P1U1NTU1NTU8ACYve2Tt93ikZbJmTjGCOc86CKaZ7wjzaHxPoY89QYJln0FpROoY44rnQyzLKS/cisE4uZGIG56L5WkKMd5Ncb0HP7aBvGrS3avgYCI1q53QfxWgcTy5xwAma97ZO32ONJj1FhxwwnDrRx5wakGEZ/Gp6DuHzLy7qdS+9OmjYitjtHdUMTFqyqyznjNe7dRQ4gShsnqB0Sy4Ml+dPDzMzt5nepTaiEH7Wm8ZHAagx6qx4Y4xh04pgxzCBf8ADt9BcblmhGfAUTlMCM38AtfcoqC20RrUmpB1AB8lR7jxwWw7i9pM6KiEXLrieGi5icRFrkBaIQ6cYUY5hRv+XfixACQxFxO6/QYMnJln3GpthGHwxieUemP32Nx4aRdo210PdPS7iCeQPmk6gXDFyDovbjSSFjibgdw4hsgQN1/FH0EgzMaBJCdbHSggj0WwhJOGIDod1Gac6XTviHfL0JJEBibh5ClmVfMyKzAS8TjbjNGyBB3W8cNiGBre3HIRXMcdROdYxahLpnSsIBJDaA3iDG0xNCEAMZmNvRkSTWZdXV9wqdwJTsPAPo4VqOJrDFNmgSlW49YnjMw2K7cMOVvCc08S8YlpXNJKf1iMpW3wjDpOK1NhxJLnNG2VDsCILK4OH2e1duJvGk0YAJIJmMj3j3w0kckSEvlQXgW5cyIYsZsc+McreE5p4dNeSfHPhRw3w6rB5amXQzMdGohLob2Dxb0QM7Es4m9rRbX8KEUxFzp+ciraUIiy5oxPNSoULO7VMGlZ5gSFsc8v6Se549DQ5pqsozEmCSx8J14yacl+OXDJqFHBMZy1mDAw1z7xDi0ArRv4k60tihssh0LUrJz+xCHxSpgBmV0MDnQYIIXH7egtZhEQld7fHBISAsxDpJ96clhClkw+x3mfcRUmS3Bm5Xf4BWGeMTUAOGG57yB+vdKVl2oU6SknR49hkG8TYYmHL0Qcb+0pARc7Vtv7BuLuI+JvWKC7InVt4pcRlLZClexWftFOAq5Gl2pEgYOUk8UzPeAv3w8fv8TRLFigozWDAFFZXehqTkhkTcotG7a/BRqCIMAgNLlhxij4E41hgM+fQIumW4drGobUhaSTZOf2ehPRwmESw+e3pi1D6EHg4uPh8Aq7akg+79EJUoBIlJmBajImy5vfnUYYizPS4YlGWC5GN7dqWC8BCOlNKRBGWQxwzbY49KkWGwCT8vFQKs2hZPscNVaS3UA7x9Heal235PuVEYrEGw1yC5L4io9M4sXcqnSloulGVan1soOodSSiqDnQPOL0D8nbXTK64F+KqsIbICd54cDNwdRkoe5AsxwfoZ5CJDi68j9a0ehGYKO0l/NFgrBWHf3O1xDHuwrIMWoGbA6rLxM2htRh+z6EMqUS+FTSxd096MFIIx3bFHb3hlIrDkYTjxMmhtTj+jij38Jyu0/QnVMfez+5H0hGe/hOV2jisKdHeMnalqks+iTwxPjO2H55NT2QHpGGakJwxvpFKrKy8BYSbCKSjPimQUZzz5URJrEK9V+CtUNj0Caxp09pyNuNKbH51udFn/HDMWLAgCJbE9lJ3hFOO0lvNN5jP+BU8nLFHNdXHDDippHFNPtRxP8ALQYnzXWxOHe6pk8XIfUv4oYn3Is3xHsybPlWzqk/648UFwDmYJ1JK6KpTZ3MODtZ0Uu9XOgTECxFtAuW+lIXlom2uJ0aOAex4nJ1811VAGxu4VFRcIZGAdCD6C9LGVI0vjHn7SxbK4A501FmF/0nQjepmE2O7NntTAnGFnQPqRBEBJiCAnGNsPoozpZvh135lBYQkxW02HWiaEjweSZOtFEKIRX3xUPz+KHKf/IcxmmUsuHTjP8A/wD/AP8A/wD/APlw2/izMtPSMMdEknUZ+Pc//wD+XDZbKkRLpXYM260yNbKZFMy09QNiAlTkVMJAV/pOKslG5tYzmDKQm5LH0hvgmV31/JV/DVN27G5FIZAC6tfw1fy1fy1fy1fy1fy1fy1fy1fy1NyZIBrAi7AkikLr4Gi1NhTJI1/LV/LUsJLYA+qv4W77Dcte2DGy/LtrIX1UIAZtMKJYrOsNNPgfT46XzWv2Oy5kJnS1fIfxXyn8V8p/FfKfxXyn8UfamAidL1j9AiRRlVx9fntPqb+du+vuc47N0P7NOeFC0hrywfcee31RfBa/oPasvntPqb+Nu+ltnaHwhyGfLGms1T7xpvrPOfqcdL4DX7XdgiMFuJlz9tu379j/APlEyCZrX3V8dp9Xfy92pq0O1B0XoAgICkUgF8mSTWNUonJJk1cz7Atqc9T6pvjtfsWq3MAC7QU1IBmr+N/Vfxv6r+N/Vfxv6pVFZCkKabe5vntPqz+Fu+jjHIxDq30OruSvSZZC4mqfl6VazZ2+cqsY9mX1NzUrHj8TkkzPqO4mhF+B1Ks+YqlIPY0PBmo4uLubtZna/P25ZZZSgMMmGYald1NCL8Dqe3K3WxpgBfTKaRBmgzDUqyTaahnakkUuTHcCxof7WtBUz6DEwzP+J0f9KfAJLNbGCKz5ubbq0P8A8C//2gAMAwEAAgADAAAAEAQQQQQQQQQQQQQQQQQQQQQQQQTjDDjjjjjjjjjjjjjjjjjjjjjvfffffffff/OP9OP/AH33333333LLLLLLLedXzzzzyFvPLLLLLLI000000HTzzzzwHvTzkE000003333322XzzxzX78WwPz1333333DDDDqHzzzwXw7vLtjbxFbDDDAEEEerzzzzzSk+6dKfzzx4sEEH/AP8A5vPPPPPAp7Yx1QePPPGv/wD/ACyzY8888888G8Dq888888hzyyNNfc888888+NUB6d88888svNNCCX88884oizJ88Q5888888rCC99788440gaww8EF3888888199BBH888csc88rDXQG888888JBB88q18888888Byes988888xW88zzzL8888888rzzzy88888HzzzCCCW88888/YxCCCTgVD68qCCCNNNMJ84YWsNNNNNNNNNP/MNNNxxxxgRCxxxxxxxxxxxxxxxxxxAAAAAY00//wD/AMjz/iqhPmAAAAD/AP8A/wD/ANuU9oDDlH9onbU8/wD/AP8A/wDvvvvvqFPusMDVvqm2k+PvvvvjDDDDDHTTPTzvLDHrbz/DDDDDMcccccccccccccccccccccccccMMMMMMMMMMMMMMMMMMMMMMMMP/xAApEQADAAAEBAcAAwEAAAAAAAAAAREhMUFhIFFxkRCBobHB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ddddddddddddd2/T+AfdddNa1LzIpOK57zHHG8RwiDvhOREESIgiJllQPRx/0a9evyrCJUOAc07tg9a6657Mz/gfo1KDqBXoOl+Xj0Q0P6qIwt7SuXm2IgEgn7cDD9goj5EfOWBTy6XgHk68h5EE74gJVXgOSPPYiIIhddddddddddddddddddddddc1/wDk74J5p3uqoBaxUBVCqGuuurl8HK6eHhEURoiiIpubpzHA5CkXhAAKv6ffffeuFxQOabvgt5y3/wCTvgnmne48t113PXTlWICsB4C40vQnsiHEZ1ySc75Jv8xlQe/Dn68ggh5LPkKsOBQn1iHLf6/RwHBruHEWwB6fgqxor2KDcGhMlaNHTFPhdObtPE7jfEDORjGRprrrrrrrrr/9nfevvX3r7196+9fevvX3r7196+9fevvX3r7196+9feuvvX3r7196+9fevvX3r7196+9fevvX3r7196+9fevvX3r7196+9fevvX3r7196+9feuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuurq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq7/2Q==","base_uri":"https://bafybeihmtke7glg2aec5oav5btzlv6ec4fxkbbh4xjre4x5ipaqdxroahe.ipfs.dweb.link","reference":null,"reference_hash":null},"tokensLeft":9922,"vip":false,"nfts":[],"mintRateLimit":10} \ No newline at end of file +{"saleInfo":{"status":"Open","presale_start":1654533037000,"sale_start":1654533937000,"token_final_supply":200,"price":"15000000000000000000000000"},"contractMetadata":{"spec":"nft-1.0.0","name":"Cheddar","symbol":"Cheddar","icon":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA2NCA2NCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjQgNjQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDp1cmwoI1hNTElEXzE2Xyk7fQoJLnN0MXtmaWxsOiNGRkU5NTk7fQoJLnN0MntvcGFjaXR5OjAuMjt9Cgkuc3Qze2ZpbGw6IzQyNDI0Mjt9Cgkuc3Q0e29wYWNpdHk6MC45O2ZpbGw6dXJsKCNYTUxJRF8xN18pO2VuYWJsZS1iYWNrZ3JvdW5kOm5ldyAgICA7fQoJLnN0NXtmaWxsOnVybCgjU1ZHSURfMV8pO30KCS5zdDZ7ZmlsbDp1cmwoI1hNTElEXzE4Xyk7fQoJLnN0N3tvcGFjaXR5OjAuOTtmaWxsOnVybCgjWE1MSURfMTlfKTtlbmFibGUtYmFja2dyb3VuZDpuZXcgICAgO30KCS5zdDh7ZmlsbDp1cmwoI1hNTElEXzIwXyk7fQoJLnN0OXtvcGFjaXR5OjAuOTtmaWxsOnVybCgjWE1MSURfMjFfKTtlbmFibGUtYmFja2dyb3VuZDpuZXcgICAgO30KCS5zdDEwe2ZpbGw6dXJsKCNTVkdJRF8yXyk7fQoJLnN0MTF7b3BhY2l0eTowLjk7ZmlsbDp1cmwoI1hNTElEXzIyXyk7ZW5hYmxlLWJhY2tncm91bmQ6bmV3ICAgIDt9Cgkuc3QxMntmaWxsOnVybCgjWE1MSURfMjNfKTt9Cgkuc3QxM3tvcGFjaXR5OjAuOTtmaWxsOnVybCgjWE1MSURfMjRfKTtlbmFibGUtYmFja2dyb3VuZDpuZXcgICAgO30KCS5zdDE0e2ZpbGw6dXJsKCNTVkdJRF8zXyk7fQoJLnN0MTV7ZmlsbDp1cmwoI1hNTElEXzI2Xyk7fQoJLnN0MTZ7b3BhY2l0eTowLjk7ZmlsbDp1cmwoI1hNTElEXzI3Xyk7ZW5hYmxlLWJhY2tncm91bmQ6bmV3ICAgIDt9Cgkuc3QxN3tmaWxsOnVybCgjWE1MSURfMzFfKTt9Cgkuc3QxOHtvcGFjaXR5OjAuOTtmaWxsOnVybCgjWE1MSURfMzNfKTtlbmFibGUtYmFja2dyb3VuZDpuZXcgICAgO30KCS5zdDE5e2ZpbGw6dXJsKCNTVkdJRF80Xyk7fQoJLnN0MjB7b3BhY2l0eTowLjk7ZmlsbDp1cmwoI1hNTElEXzM0Xyk7ZW5hYmxlLWJhY2tncm91bmQ6bmV3ICAgIDt9Cgkuc3QyMXtmaWxsOnVybCgjWE1MSURfMzZfKTt9Cgkuc3QyMntvcGFjaXR5OjAuOTtmaWxsOnVybCgjWE1MSURfMzdfKTtlbmFibGUtYmFja2dyb3VuZDpuZXcgICAgO30KCS5zdDIze2ZpbGw6dXJsKCNTVkdJRF81Xyk7fQoJLnN0MjR7ZmlsbDp1cmwoI1hNTElEXzM4Xyk7fQoJLnN0MjV7b3BhY2l0eTowLjk7ZmlsbDp1cmwoI1hNTElEXzM5Xyk7ZW5hYmxlLWJhY2tncm91bmQ6bmV3ICAgIDt9Cgkuc3QyNntmaWxsOnVybCgjWE1MSURfNDBfKTt9Cgkuc3QyN3tvcGFjaXR5OjAuOTtmaWxsOnVybCgjWE1MSURfNDFfKTtlbmFibGUtYmFja2dyb3VuZDpuZXcgICAgO30KCS5zdDI4e2ZpbGw6dXJsKCNTVkdJRF82Xyk7fQoJLnN0Mjl7b3BhY2l0eTowLjk7ZmlsbDp1cmwoI1hNTElEXzQyXyk7ZW5hYmxlLWJhY2tncm91bmQ6bmV3ICAgIDt9Cjwvc3R5bGU+CjxnIGlkPSJMYXllcl8xXzFfIj4KCQoJCTxsaW5lYXJHcmFkaWVudCBpZD0iWE1MSURfMTZfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0xODIxLjEzNTMiIHkxPSI5ODMuMTc0MyIgeDI9Ii0xODIxLjEzNTMiIHkyPSI5OTguMDU0MyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSAtMTc5MC4yNzUzIC05NDYpIj4KCQk8c3RvcCAgb2Zmc2V0PSIxLjk3MDAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNGN0JDMDAiLz4KCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojRkNENzNFIi8+Cgk8L2xpbmVhckdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzY4XyIgY2xhc3M9InN0MCIgZD0iTTUyLjMsNDAuMnYxNC4xbC00MC4yLTYuMWMtMS41LTAuMy0yLjYtMS4zLTIuNi0yLjNjMC4xLTMuOC0wLjEtNy41LDAtMTEuMQoJCWMwLTAuNywwLjEtMS41LDEuMi0xLjdjMC43LTAuMSwxLjYsMC4yLDIuMywwLjNsNC45LDAuOGMwLDAuMSwwLDAuMywwLDAuNGMwLDEuNiwxLjcsMyw0LDMuMWMyLDAuMSwzLjYtMC44LDMuOS0yLjJMNDAuNiwzOAoJCWMtMC4xLDAuMi0wLjIsMC40LTAuMiwwLjVjMCwwLjcsMC45LDEuMywxLjksMS40YzEuMSwwLjEsMS45LTAuNCwxLjktMS4yYzAtMC4xLDAtMC4xLDAtMC4yQzQ0LjEsMzguNiw1Mi4zLDQwLjIsNTIuMyw0MC4yeiIvPgoJPHBhdGggY2xhc3M9InN0MSIgZD0iTTkuNSwzNC44YzAuMy0xLjItMC40LTIuNiwzLjUtMS41bDQuOSwwLjhjMC40LTEsMi0xLjYsMy44LTEuNGMyLjIsMC4xLDQsMS4zLDQsMi41YzAsMC4xLDAsMC4yLDAsMC4zCgkJTDQwLjUsMzhsMCwwYzAuMy0wLjMsMS0wLjQsMS43LTAuNGMxLDAuMSwxLjksMC41LDEuOSwxLjFsOC4yLDEuNkwzMC44LDI3LjVjLTItMS4yLTQuNS0xLjctNi43LTEuM2MtMi4yLDAuMy01LDAuOC03LjEsMS42CgkJYy0yLjUsMC45LTQuOCwyLjQtNi4yLDMuNmMtMC4xLDAuMS0wLjYsMC41LTAuOSwxLjFDOS41LDMzLDkuNSwzMy44LDkuNSwzNC44eiIvPgoJPGcgY2xhc3M9InN0MiI+CgkJPHBhdGggY2xhc3M9InN0MyIgZD0iTTI1LjQsMjdjMS42LDAsMy4zLDAuNCw0LjcsMS4zbDIxLDEyLjV2MTIuNmwtMzguOC01LjljLTAuOS0wLjItMS43LTAuOC0xLjYtMS40YzAtMi4yLDAtNC4zLDAtNi41CgkJCWMwLTEuNSwwLTMuMSwwLTQuN2MwLTAuMSwwLTAuMiwwLTAuNGwwLDBjMC0wLjUsMC4xLTEuMiwwLjMtMS42YzAuMi0wLjQsMC41LTAuNywwLjYtMC44YzEuNC0xLjEsMy42LTIuNSw1LjgtMy40CgkJCWMxLjYtMC42LDQtMS4xLDYuOC0xLjVDMjQuNiwyNywyNC45LDI3LDI1LjQsMjcgTTI1LjQsMjYuMWMtMC40LDAtMC45LDAtMS4zLDAuMWMtMi4yLDAuMy01LDAuOC03LjEsMS42CgkJCWMtMi41LDAuOS00LjgsMi40LTYuMiwzLjZjLTAuMSwwLjEtMC42LDAuNS0wLjksMS4xYy0wLjMsMC41LTAuMywxLjItMC40LDEuOWMwLDAuMiwwLDAuMywwLDAuNGMwLDMuNywwLjEsNy41LDAuMSwxMS4xCgkJCWMwLDEuMSwxLjEsMi4xLDIuNiwyLjNsNDAuMiw2LjFWNDAuMkwzMC44LDI3LjVDMjkuMiwyNi42LDI3LjMsMjYuMSwyNS40LDI2LjFMMjUuNCwyNi4xeiIvPgoJPC9nPgoJCgkJPHJhZGlhbEdyYWRpZW50IGlkPSJYTUxJRF8xN18iIGN4PSItMzA0NS4xOTg3IiBjeT0iMjA0Ny40NDY5IiByPSI2Ljg2ODciIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuNDI1OCAwIDAgMC4zMjI1IC0xMjc5Ljk4MTMgLTYxNy4xODk5KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguMTMwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0RFN0YxNCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuMzM4OSIgc3R5bGU9InN0b3AtY29sb3I6I0RBN0QxMiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNTk5MyIgc3R5bGU9InN0b3AtY29sb3I6I0NFNzYwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuODYwMiIgc3R5bGU9InN0b3AtY29sb3I6I0JBNkMwNSIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQUQ2NTAwIi8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzM1XyIgY2xhc3M9InN0NCIgZD0iTTE4LjQsNDMuOGMwLDEuMi0xLjMsMi4xLTMsMmMtMS42LTAuMS0zLTEuMi0zLTIuM3MxLjMtMi4xLDMtMkMxNyw0MS41LDE4LjQsNDIuNiwxOC40LDQzLjh6IgoJCS8+CgkKCQk8cmFkaWFsR3JhZGllbnQgaWQ9IlNWR0lEXzFfIiBjeD0iLTMwNjQuNDI3NSIgY3k9IjIwMTUuOTU3IiByPSIxNC4xMzIxIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjQyNTggMCAwIDAuMzIyNSAtMTI3OS45ODEzIC02MTcuMTg5OSkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KCQk8c3RvcCAgb2Zmc2V0PSI4LjEzMDAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNERTdGMTQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjMzODkiIHN0eWxlPSJzdG9wLWNvbG9yOiNEQTdEMTIiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjU5OTMiIHN0eWxlPSJzdG9wLWNvbG9yOiNDRTc2MEQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjg2MDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNCQTZDMDUiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjk4NyIgc3R5bGU9InN0b3AtY29sb3I6I0FENjUwMCIvPgoJPC9yYWRpYWxHcmFkaWVudD4KCTxwYXRoIGNsYXNzPSJzdDUiIGQ9Ik0xNy44LDM0LjRjMCwxLjYsMS43LDMsNCwzLjFjMiwwLjEsMy42LTAuOCwzLjktMi4yaC0wLjFsLTAuMy0wLjFsMC40LDAuMWMwLTAuMSwwLTAuMiwwLTAuMwoJCWMwLTEuMy0xLjgtMi40LTQtMi41Yy0xLjgtMC4xLTMuNCwwLjUtMy44LDEuNEMxNy44LDM0LjIsMTcuOCwzNC40LDE3LjgsMzQuNHoiLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMThfIiBjeD0iLTEzMjguMjIwMiIgY3k9IjM0ODEuNzQ3MSIgcj0iNy4xMDIiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMzA3OCAwLjIyMjggMC4yNDA5IDAuMTkwOSAtMTIyMS41MjUxIC0zNDAuNzk4NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KCQk8c3RvcCAgb2Zmc2V0PSI4Ljc1MDAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNEOTk5MTQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjQwOTgiIHN0eWxlPSJzdG9wLWNvbG9yOiNENTk2MTIiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjczNSIgc3R5bGU9InN0b3AtY29sb3I6I0M5OEQwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQkE4MjA3Ii8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzMyXyIgY2xhc3M9InN0NiIgZD0iTTI2LjgsMjkuNGMwLDAuNS0wLjksMS0yLDAuOXMtMi0wLjYtMi0xLjJzMC45LTEsMi0wLjlDMjYsMjguMiwyNi44LDI4LjgsMjYuOCwyOS40eiIvPgoJCgkJPHJhZGlhbEdyYWRpZW50IGlkPSJYTUxJRF8xOV8iIGN4PSItMzA3OS41NTMiIGN5PSIyMDQ0LjQxMSIgcj0iMTUuNjcyNSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC40MjU4IDAgMCAwLjMyMjUgLTEyNzkuOTgxMyAtNjE3LjE4OTkpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CgkJPHN0b3AgIG9mZnNldD0iOC4xMzAwMDBlLTAyIiBzdHlsZT0ic3RvcC1jb2xvcjojREU3RjE0Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC4zMzg5IiBzdHlsZT0ic3RvcC1jb2xvcjojREE3RDEyIi8+CgkJPHN0b3AgIG9mZnNldD0iMC41OTkzIiBzdHlsZT0ic3RvcC1jb2xvcjojQ0U3NjBEIi8+CgkJPHN0b3AgIG9mZnNldD0iMC44NjAyIiBzdHlsZT0ic3RvcC1jb2xvcjojQkE2QzA1Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC45ODciIHN0eWxlPSJzdG9wLWNvbG9yOiNBRDY1MDAiLz4KCTwvcmFkaWFsR3JhZGllbnQ+Cgk8cGF0aCBpZD0iWE1MSURfMzBfIiBjbGFzcz0ic3Q3IiBkPSJNMzUuMSw0My40YzAsMi4xLTIuMiwzLjUtNSwzLjNzLTUtMi01LTRjMC0yLjEsMi4yLTMuNSw1LTMuM0MzMi44LDM5LjYsMzUuMSw0MS40LDM1LjEsNDMuNHoiCgkJLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMjBfIiBjeD0iLTMwNzYuMzYxNiIgY3k9IjIwMTEuMTY0OCIgcj0iMTAuOTExOCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC40MjU4IDAgMCAwLjMyMjUgLTEyNzkuOTgxMyAtNjE3LjE4OTkpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CgkJPHN0b3AgIG9mZnNldD0iOC43NTAwMDBlLTAyIiBzdHlsZT0ic3RvcC1jb2xvcjojRDk5OTE0Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC40MDk4IiBzdHlsZT0ic3RvcC1jb2xvcjojRDU5NjEyIi8+CgkJPHN0b3AgIG9mZnNldD0iMC43MzUiIHN0eWxlPSJzdG9wLWNvbG9yOiNDOThEMEQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjk4NyIgc3R5bGU9InN0b3AtY29sb3I6I0JBODIwNyIvPgoJPC9yYWRpYWxHcmFkaWVudD4KCTxwYXRoIGlkPSJYTUxJRF8yOV8iIGNsYXNzPSJzdDgiIGQ9Ik0zNS44LDMzLjFjMCwwLjgtMS4zLDEuMy0yLjksMS4yYy0xLjYtMC4yLTIuOS0wLjgtMi45LTEuNmMwLTAuOCwxLjMtMS4zLDIuOS0xLjIKCQlDMzQuNiwzMS43LDM1LjgsMzIuMywzNS44LDMzLjF6Ii8+CgkKCQk8cmFkaWFsR3JhZGllbnQgaWQ9IlhNTElEXzIxXyIgY3g9Ii0zMTAyLjMwMzUiIGN5PSIyMDU5Ljc2NjgiIHI9IjUuOTI4NSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC40MjU4IDAgMCAwLjMyMjUgLTEyNzkuOTgxMyAtNjE3LjE4OTkpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CgkJPHN0b3AgIG9mZnNldD0iOC4xMzAwMDBlLTAyIiBzdHlsZT0ic3RvcC1jb2xvcjojREU3RjE0Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC4zMzg5IiBzdHlsZT0ic3RvcC1jb2xvcjojREE3RDEyIi8+CgkJPHN0b3AgIG9mZnNldD0iMC41OTkzIiBzdHlsZT0ic3RvcC1jb2xvcjojQ0U3NjBEIi8+CgkJPHN0b3AgIG9mZnNldD0iMC44NjAyIiBzdHlsZT0ic3RvcC1jb2xvcjojQkE2QzA1Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC45ODciIHN0eWxlPSJzdG9wLWNvbG9yOiNBRDY1MDAiLz4KCTwvcmFkaWFsR3JhZGllbnQ+Cgk8cGF0aCBpZD0iWE1MSURfMjhfIiBjbGFzcz0ic3Q5IiBkPSJNNDIuMyw0Ny43YzAsMS0xLjIsMS44LTIuNiwxLjdjLTEuNC0wLjEtMi42LTEtMi42LTIuMWMwLTEuMSwxLjItMS44LDIuNi0xLjcKCQlDNDEuMSw0NS45LDQyLjMsNDYuOCw0Mi4zLDQ3Ljd6Ii8+CgkKCQk8cmFkaWFsR3JhZGllbnQgaWQ9IlNWR0lEXzJfIiBjeD0iLTMxMDYuODA4OCIgY3k9IjIwMzAuMDM0MyIgcj0iOC44NDA4IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjQyNTggMCAwIDAuMzIyNSAtMTI3OS45ODEzIC02MTcuMTg5OSkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KCQk8c3RvcCAgb2Zmc2V0PSI4LjYwMDAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNDNzczMTUiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjM3NzQiIHN0eWxlPSJzdG9wLWNvbG9yOiNDMzcxMTMiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjY3MTgiIHN0eWxlPSJzdG9wLWNvbG9yOiNCNzZDMEYiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjk2NjkiIHN0eWxlPSJzdG9wLWNvbG9yOiNBMzY0MDciLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjk4NyIgc3R5bGU9InN0b3AtY29sb3I6I0ExNjMwNiIvPgoJPC9yYWRpYWxHcmFkaWVudD4KCTxwYXRoIGNsYXNzPSJzdDEwIiBkPSJNNDAuMiwzOC41YzAsMC43LDAuOSwxLjMsMS45LDEuNGMxLjEsMC4xLDEuOS0wLjQsMS45LTEuMmMwLTAuMSwwLTAuMSwwLTAuMmwwLDBjMC0wLjUtMC45LTEtMS45LTEuMQoJCWMtMC43LTAuMS0xLjQsMC4xLTEuNywwLjRsMCwwQzQwLjMsMzguMSw0MC4yLDM4LjMsNDAuMiwzOC41eiIvPgoJCgkJPHJhZGlhbEdyYWRpZW50IGlkPSJYTUxJRF8yMl8iIGN4PSItMzExOS4yMjA1IiBjeT0iMjA0OC44OTk5IiByPSI0LjM5ODUiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuNDI1OCAwIDAgMC4zMjI1IC0xMjc5Ljk4MTMgLTYxNy4xODk5KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguMTMwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0RFN0YxNCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuMzM4OSIgc3R5bGU9InN0b3AtY29sb3I6I0RBN0QxMiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNTk5MyIgc3R5bGU9InN0b3AtY29sb3I6I0NFNzYwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuODYwMiIgc3R5bGU9InN0b3AtY29sb3I6I0JBNkMwNSIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQUQ2NTAwIi8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzI1XyIgY2xhc3M9InN0MTEiIGQ9Ik00OC43LDQ0YzAsMC41LTAuNiwxLTEuMywwLjljLTAuNy0wLjEtMS4zLTAuNS0xLjMtMS4xYzAtMC41LDAuNi0xLDEuMy0wLjkKCQlDNDgsNDMsNDguNyw0My40LDQ4LjcsNDR6Ii8+CjwvZz4KPGcgaWQ9IkxheWVyXzFfMl8iPgoJCgkJPGxpbmVhckdyYWRpZW50IGlkPSJYTUxJRF8yM18iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTIxMjYuNTcyIiB5MT0iMTAyNi43NzYxIiB4Mj0iLTIxMjYuNTcyIiB5Mj0iMTAzOS45Mzc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjc2NTIgLTEuNjc0MTIzZS0wMiAtMS44Nzk5NDdlLTAyIDAuODU5MyAtMTU3Ni4yMTU3IC04OTMuNDA4NSkiPgoJCTxzdG9wICBvZmZzZXQ9IjEuOTcwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0Y3QkMwMCIvPgoJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNGQ0Q3M0UiLz4KCTwvbGluZWFyR3JhZGllbnQ+Cgk8cGF0aCBpZD0iWE1MSURfN18iIGNsYXNzPSJzdDEyIiBkPSJNNDcsMjdsLTAuNCwxMC45bC0yOC41LTUuMWMtMS4xLTAuMi0xLjgtMS0xLjgtMS44YzAuMi0yLjksMC4xLTUuOCwwLjMtOC42CgkJYzAtMC41LDAuMS0xLjIsMC45LTEuM2MwLjUtMC4xLDEuMiwwLjIsMS43LDAuM2wzLjQsMC43YzAsMC4xLDAsMC4yLDAsMC4zYzAsMS4yLDEuMiwyLjMsMi43LDIuNGMxLjQsMC4xLDIuNi0wLjYsMi44LTEuNgoJCWwxMC41LDIuMmMtMC4xLDAuMS0wLjIsMC4zLTAuMiwwLjRjMCwwLjUsMC41LDEuMSwxLjMsMS4yYzAuNywwLjEsMS4zLTAuNCwxLjMtMC45YzAtMC4xLDAtMC4xLDAtMC4yQzQxLjEsMjUuNiw0NywyNyw0NywyN3oiLz4KCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0xNi41LDIyLjRjMC4yLTAuOS0wLjItMiwyLjUtMS4ybDMuNCwwLjdjMC4zLTAuNywxLjQtMS4yLDIuOC0xLjFjMS41LDAuMSwyLjgsMS4xLDIuOCwyYzAsMC4xLDAsMC4yLDAsMC4yCgkJbDEwLjUsMi4ybDAsMGMwLjItMC4zLDAuNy0wLjQsMS4zLTAuNGMwLjcsMC4xLDEuMywwLjQsMS4zLDAuOGw1LjgsMS4zTDMyLDE3Yy0xLjMtMC45LTMuMS0xLjMtNC44LTEuMWMtMS42LDAuMi0zLjYsMC41LTUuMSwxLjIKCQljLTEuOCwwLjYtMy40LDEuOC00LjUsMi43Yy0wLjEsMC4xLTAuNCwwLjQtMC42LDAuOEMxNi42LDIxLjEsMTYuNiwyMS43LDE2LjUsMjIuNHoiLz4KCTxnIGNsYXNzPSJzdDIiPgoJCTxwYXRoIGNsYXNzPSJzdDMiIGQ9Ik0yOC4xLDE2LjVjMS4yLDAsMi4zLDAuNCwzLjMsMWwxNC43LDkuOEw0NS45LDM3bC0yNy42LTQuOWMtMC42LTAuMS0xLjItMC42LTEuMi0xLjIKCQkJYzAuMS0xLjYsMC4xLTMuMywwLjItNC45YzAtMS4yLDAuMS0yLjQsMC4xLTMuNmMwLTAuMSwwLTAuMiwwLTAuM2wwLDBjMC0wLjQsMC4xLTAuOSwwLjItMS4zYzAuMS0wLjMsMC40LTAuNSwwLjQtMC42CgkJCWMxLTAuOCwyLjYtMS45LDQuMy0yLjVjMS4yLTAuNCwyLjktMC44LDQuOS0xLjFDMjcuNSwxNi41LDI3LjgsMTYuNSwyOC4xLDE2LjUgTTI4LjEsMTUuOGMtMC40LDAtMC42LDAtMSwwLjEKCQkJYy0xLjYsMC4yLTMuNiwwLjUtNS4xLDEuMmMtMS44LDAuNi0zLjQsMS44LTQuNSwyLjdjLTAuMSwwLjEtMC40LDAuNC0wLjYsMC44Yy0wLjIsMC40LTAuMywwLjktMC4zLDEuNGMwLDAuMSwwLDAuMiwwLDAuMwoJCQljLTAuMSwyLjktMC4xLDUuOC0wLjMsOC42YzAsMC44LDAuNywxLjYsMS44LDEuOGwyOC41LDUuMUw0NywyN0wzMS45LDE3QzMwLjgsMTYuMywyOS40LDE1LjgsMjguMSwxNS44TDI4LjEsMTUuOHoiLz4KCTwvZz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMjRfIiBjeD0iLTM5OTEuOTUyOSIgY3k9IjI0MDEuOTY1MSIgcj0iNi44Njg3IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjMwMzcgLTQuMzQ2MjA4ZS0wMyAtNy41ODc3MjFlLTAzIDAuMjQ4NCAtMTE3Mi42NTQ4IC01ODUuMTI5KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguMTMwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0RFN0YxNCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuMzM4OSIgc3R5bGU9InN0b3AtY29sb3I6I0RBN0QxMiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNTk5MyIgc3R5bGU9InN0b3AtY29sb3I6I0NFNzYwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuODYwMiIgc3R5bGU9InN0b3AtY29sb3I6I0JBNkMwNSIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQUQ2NTAwIi8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzZfIiBjbGFzcz0ic3QxMyIgZD0iTTIyLjcsMjkuNGMwLDAuOS0xLDEuNi0yLjIsMS40Yy0xLjItMC4yLTIuMS0wLjktMi4xLTEuOWMwLTEsMS0xLjYsMi4yLTEuNAoJCVMyMi43LDI4LjUsMjIuNywyOS40eiIvPgoJCgkJPHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8zXyIgY3g9Ii00MDExLjE4MTYiIGN5PSIyMzcwLjQ3NTMiIHI9IjE0LjEzMjEiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMzAzNyAtNC4zNDYyMDhlLTAzIC03LjU4NzcyMWUtMDMgMC4yNDg0IC0xMTcyLjY1NDggLTU4NS4xMjkpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CgkJPHN0b3AgIG9mZnNldD0iOC4xMzAwMDBlLTAyIiBzdHlsZT0ic3RvcC1jb2xvcjojREU3RjE0Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC4zMzg5IiBzdHlsZT0ic3RvcC1jb2xvcjojREE3RDEyIi8+CgkJPHN0b3AgIG9mZnNldD0iMC41OTkzIiBzdHlsZT0ic3RvcC1jb2xvcjojQ0U3NjBEIi8+CgkJPHN0b3AgIG9mZnNldD0iMC44NjAyIiBzdHlsZT0ic3RvcC1jb2xvcjojQkE2QzA1Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC45ODciIHN0eWxlPSJzdG9wLWNvbG9yOiNBRDY1MDAiLz4KCTwvcmFkaWFsR3JhZGllbnQ+Cgk8cGF0aCBjbGFzcz0ic3QxNCIgZD0iTTIyLjQsMjIuMmMwLDEuMiwxLjIsMi4zLDIuNywyLjRjMS40LDAuMSwyLjYtMC42LDIuOC0xLjZoLTAuMWgtMC4ybDAuMywwLjFjMC0wLjEsMC0wLjIsMC0wLjIKCQljMC0xLTEuMy0xLjktMi44LTJjLTEuMy0wLjEtMi40LDAuNC0yLjgsMS4xQzIyLjUsMjIsMjIuNCwyMi4xLDIyLjQsMjIuMnoiLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMjZfIiBjeD0iLTE3NjcuOTQ2NSIgY3k9IjQ1OTMuOTUyMSIgcj0iNy4xMDIiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMjI0OCAwLjE2ODUgMC4xNjczIDAuMTQ5NSAtMTEzNy40NjU2IC0zNzEuNjE3OCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KCQk8c3RvcCAgb2Zmc2V0PSI4Ljc1MDAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNEOTk5MTQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjQwOTgiIHN0eWxlPSJzdG9wLWNvbG9yOiNENTk2MTIiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjczNSIgc3R5bGU9InN0b3AtY29sb3I6I0M5OEQwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQkE4MjA3Ii8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzVfIiBjbGFzcz0ic3QxNSIgZD0iTTI5LjEsMTguNGMwLDAuNC0wLjYsMC43LTEuNCwwLjdjLTAuNy0wLjEtMS4zLTAuNC0xLjMtMC45YzAtMC40LDAuNi0wLjcsMS40LTAuNwoJCUMyOC40LDE3LjYsMjkuMSwxNy45LDI5LjEsMTguNHoiLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMjdfIiBjeD0iLTQwMjYuMzA3MSIgY3k9IjIzOTguOTI5MiIgcj0iMTUuNjcyNSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC4zMDM3IC00LjM0NjIwOGUtMDMgLTcuNTg3NzIxZS0wMyAwLjI0ODQgLTExNzIuNjU0OCAtNTg1LjEyOSkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KCQk8c3RvcCAgb2Zmc2V0PSI4LjEzMDAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNERTdGMTQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjMzODkiIHN0eWxlPSJzdG9wLWNvbG9yOiNEQTdEMTIiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjU5OTMiIHN0eWxlPSJzdG9wLWNvbG9yOiNDRTc2MEQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjg2MDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNCQTZDMDUiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjk4NyIgc3R5bGU9InN0b3AtY29sb3I6I0FENjUwMCIvPgoJPC9yYWRpYWxHcmFkaWVudD4KCTxwYXRoIGlkPSJYTUxJRF80XyIgY2xhc3M9InN0MTYiIGQ9Ik0zNC42LDI5LjNjLTAuMSwxLjUtMS43LDIuNy0zLjcsMi41Yy0yLTAuMi0zLjYtMS42LTMuNS0zLjFjMC4xLTEuNSwxLjctMi43LDMuNy0yLjUKCQlDMzMsMjYuMywzNC42LDI3LjcsMzQuNiwyOS4zeiIvPgoJCgkJPHJhZGlhbEdyYWRpZW50IGlkPSJYTUxJRF8zMV8iIGN4PSItNDAyMy4xMTU3IiBjeT0iMjM2NS42ODMxIiByPSIxMC45MTE4IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjMwMzcgLTQuMzQ2MjA4ZS0wMyAtNy41ODc3MjFlLTAzIDAuMjQ4NCAtMTE3Mi42NTQ4IC01ODUuMTI5KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguNzUwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0Q5OTkxNCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNDA5OCIgc3R5bGU9InN0b3AtY29sb3I6I0Q1OTYxMiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNzM1IiBzdHlsZT0ic3RvcC1jb2xvcjojQzk4RDBEIi8+CgkJPHN0b3AgIG9mZnNldD0iMC45ODciIHN0eWxlPSJzdG9wLWNvbG9yOiNCQTgyMDciLz4KCTwvcmFkaWFsR3JhZGllbnQ+Cgk8cGF0aCBpZD0iWE1MSURfM18iIGNsYXNzPSJzdDE3IiBkPSJNMzUuNCwyMS4zYzAsMC42LTEsMS0yLjEsMC45Yy0xLjEtMC4xLTIuMS0wLjYtMi4xLTEuM3MxLTEsMi4xLTAuOQoJCUMzNC41LDIwLjIsMzUuNCwyMC43LDM1LjQsMjEuM3oiLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMzNfIiBjeD0iLTQwNDkuMDU3OSIgY3k9IjI0MTQuMjg1MiIgcj0iNS45Mjg1IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjMwMzcgLTQuMzQ2MjA4ZS0wMyAtNy41ODc3MjFlLTAzIDAuMjQ4NCAtMTE3Mi42NTQ4IC01ODUuMTI5KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguMTMwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0RFN0YxNCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuMzM4OSIgc3R5bGU9InN0b3AtY29sb3I6I0RBN0QxMiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNTk5MyIgc3R5bGU9InN0b3AtY29sb3I6I0NFNzYwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuODYwMiIgc3R5bGU9InN0b3AtY29sb3I6I0JBNkMwNSIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQUQ2NTAwIi8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzJfIiBjbGFzcz0ic3QxOCIgZD0iTTM5LjYsMzIuN2MwLDAuOC0wLjksMS4zLTEuOSwxLjNjLTEtMC4xLTEuOC0wLjgtMS44LTEuNnMwLjktMS4zLDEuOS0xLjMKCQlDMzguOSwzMS4yLDM5LjcsMzEuOSwzOS42LDMyLjd6Ii8+CgkKCQk8cmFkaWFsR3JhZGllbnQgaWQ9IlNWR0lEXzRfIiBjeD0iLTQwNTMuNTYzIiBjeT0iMjM4NC41NTI1IiByPSI4Ljg0MDgiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMzAzNyAtNC4zNDYyMDhlLTAzIC03LjU4NzcyMWUtMDMgMC4yNDg0IC0xMTcyLjY1NDggLTU4NS4xMjkpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CgkJPHN0b3AgIG9mZnNldD0iOC42MDAwMDBlLTAyIiBzdHlsZT0ic3RvcC1jb2xvcjojQzc3MzE1Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC4zNzc0IiBzdHlsZT0ic3RvcC1jb2xvcjojQzM3MTEzIi8+CgkJPHN0b3AgIG9mZnNldD0iMC42NzE4IiBzdHlsZT0ic3RvcC1jb2xvcjojQjc2QzBGIi8+CgkJPHN0b3AgIG9mZnNldD0iMC45NjY5IiBzdHlsZT0ic3RvcC1jb2xvcjojQTM2NDA3Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC45ODciIHN0eWxlPSJzdG9wLWNvbG9yOiNBMTYzMDYiLz4KCTwvcmFkaWFsR3JhZGllbnQ+Cgk8cGF0aCBjbGFzcz0ic3QxOSIgZD0iTTM4LjQsMjUuNmMwLDAuNSwwLjUsMS4xLDEuMywxLjJjMC43LDAuMSwxLjMtMC40LDEuMy0wLjljMC0wLjEsMC0wLjEsMC0wLjJsMCwwYzAtMC40LTAuNi0wLjgtMS4zLTAuOAoJCWMtMC41LTAuMS0xLDAuMS0xLjMsMC40bDAsMEMzOC40LDI1LjMsMzguNCwyNS41LDM4LjQsMjUuNnoiLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMzRfIiBjeD0iLTQwNjUuOTc0NiIgY3k9IjI0MDMuNDE4MiIgcj0iNC4zOTg1IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjMwMzcgLTQuMzQ2MjA4ZS0wMyAtNy41ODc3MjFlLTAzIDAuMjQ4NCAtMTE3Mi42NTQ4IC01ODUuMTI5KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguMTMwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0RFN0YxNCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuMzM4OSIgc3R5bGU9InN0b3AtY29sb3I6I0RBN0QxMiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNTk5MyIgc3R5bGU9InN0b3AtY29sb3I6I0NFNzYwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuODYwMiIgc3R5bGU9InN0b3AtY29sb3I6I0JBNkMwNSIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQUQ2NTAwIi8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzFfIiBjbGFzcz0ic3QyMCIgZD0iTTQ0LjMsMjkuOWMwLDAuNC0wLjQsMC43LTEsMC43cy0xLTAuNC0xLTAuOWMwLTAuNCwwLjQtMC43LDEtMC43CgkJQzQzLjgsMjkuMSw0NC4zLDI5LjQsNDQuMywyOS45eiIvPgo8L2c+CjxnIGlkPSJMYXllcl8xXzNfIj4KCQoJCTxsaW5lYXJHcmFkaWVudCBpZD0iWE1MSURfMzZfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0yNDIyLjUxNzMiIHkxPSIxMDA1LjY1NDYiIHgyPSItMjQyMi41MTczIiB5Mj0iMTAxNC43NjQzIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjYyODcgLTMuOTI5MDMwZS0wMiAtNS4wNDc5OThlLTAyIDAuODA3NyAtMTQ0MC42ODE1IC04OTIuMDUxMykiPgoJCTxzdG9wICBvZmZzZXQ9IjEuOTcwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0Y3QkMwMCIvPgoJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNGQ0Q3M0UiLz4KCTwvbGluZWFyR3JhZGllbnQ+Cgk8cGF0aCBpZD0iWE1MSURfMTRfIiBjbGFzcz0ic3QyMSIgZD0iTTQwLjYsMTcuMmwtMC42LDcuMmwtMTYuNy0zLjhjLTAuNi0wLjItMS4xLTAuNy0xLTEuM2MwLjItMS45LDAuMy0zLjksMC40LTUuOAoJCWMwLTAuNCwwLjEtMC44LDAuNS0wLjljMC40LTAuMSwwLjcsMC4xLDEsMC4ybDIsMC41YzAsMC4xLDAsMC4yLDAsMC4yYy0wLjEsMC44LDAuNiwxLjUsMS41LDEuNmMwLjgsMC4xLDEuNS0wLjQsMS43LTEuMWw2LjIsMS41CgkJYy0wLjEsMC4xLTAuMSwwLjItMC4xLDAuM2MwLDAuNCwwLjMsMC43LDAuNywwLjhzMC44LTAuMiwwLjktMC41di0wLjFDMzcuMiwxNi4yLDQwLjYsMTcuMiw0MC42LDE3LjJ6Ii8+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNMjIuOCwxMy43YzAuMi0wLjUsMC0xLjMsMS41LTAuN2wyLDAuNWMwLjItMC40LDAuOS0wLjgsMS43LTAuN2MwLjksMC4xLDEuNiwwLjcsMS41LDEuM2MwLDAuMSwwLDAuMSwwLDAuMgoJCWw2LjIsMS41bDAsMGMwLjItMC4yLDAuNC0wLjMsMC43LTAuMmMwLjQsMC4xLDAuNywwLjMsMC43LDAuNWwzLjQsMUwzMiwxMC4zYy0wLjgtMC42LTEuOC0wLjktMi44LTAuOGMtMSwwLjItMi4yLDAuNC0zLjEsMC43CgkJYy0xLjEsMC40LTIuMSwxLjItMi44LDEuN2MwLDAtMC4zLDAuMy0wLjQsMC41QzIyLjksMTIuOCwyMi44LDEzLjIsMjIuOCwxMy43eiIvPgoJPGcgY2xhc3M9InN0MiI+CgkJPHBhdGggY2xhc3M9InN0MyIgZD0iTTI5LjgsOS45YzAuNywwLDEuMywwLjMsMS45LDAuN2w4LjQsNi43bC0wLjUsNi41bC0xNi4xLTMuN2MtMC40LTAuMS0wLjYtMC40LTAuNi0wLjgKCQkJYzAuMS0xLjEsMC4yLTIuMiwwLjMtMy4zYzAuMS0wLjgsMC4xLTEuNiwwLjItMi40YzAtMC4xLDAtMC4xLDAtMC4ybDAsMGMwLTAuMywwLjEtMC42LDAuMi0wLjhjMC4xLTAuMiwwLjMtMC40LDAuMy0wLjQKCQkJYzAuNi0wLjUsMS42LTEuMywyLjYtMS42YzAuNy0wLjMsMS43LTAuNSwzLTAuNkMyOS40LDkuOSwyOS42LDkuOSwyOS44LDkuOSBNMjkuOSw5LjVjLTAuMiwwLTAuNCwwLTAuNiwwYy0xLDAuMi0yLjIsMC40LTMuMSwwLjcKCQkJYy0xLjEsMC40LTIuMSwxLjItMi44LDEuN2MwLDAtMC4zLDAuMy0wLjQsMC41Yy0wLjEsMC4zLTAuMiwwLjUtMC4yLDFjMCwwLjEsMCwwLjIsMCwwLjJjLTAuMiwxLjktMC4zLDMuOS0wLjQsNS44CgkJCWMtMC4xLDAuNSwwLjQsMS4xLDEsMS4zbDE2LjYsMy45bDAuNi03LjNMMzIsMTAuM0MzMS40LDkuOCwzMC42LDkuNiwyOS45LDkuNUwyOS45LDkuNXoiLz4KCTwvZz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMzdfIiBjeD0iLTYzMDAuMDcxMyIgY3k9IjMxODQuNDk5MyIgcj0iNi44Njg3IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjE3OTYgLTYuODIyNTU0ZS0wMyAtMS4zOTA2NjJlLTAyIDAuMTY1NiAtMTA2MS43Nzg2IC01NTIuMjIzMykiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KCQk8c3RvcCAgb2Zmc2V0PSI4LjEzMDAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNERTdGMTQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjMzODkiIHN0eWxlPSJzdG9wLWNvbG9yOiNEQTdEMTIiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjU5OTMiIHN0eWxlPSJzdG9wLWNvbG9yOiNDRTc2MEQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjg2MDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNCQTZDMDUiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjk4NyIgc3R5bGU9InN0b3AtY29sb3I6I0FENjUwMCIvPgoJPC9yYWRpYWxHcmFkaWVudD4KCTxwYXRoIGlkPSJYTUxJRF8xM18iIGNsYXNzPSJzdDIyIiBkPSJNMjYuMSwxOC41Yy0wLjEsMC42LTAuNiwxLjEtMS4zLDFjLTAuNy0wLjEtMS4yLTAuNi0xLjItMS4zYzAuMS0wLjYsMC42LTEuMSwxLjMtMQoJCUMyNS43LDE3LjMsMjYuMiwxNy45LDI2LjEsMTguNXoiLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iU1ZHSURfNV8iIGN4PSItNjMxOS4zMDAzIiBjeT0iMzE1My4wMDk1IiByPSIxNC4xMzIxIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjE3OTYgLTYuODIyNTU0ZS0wMyAtMS4zOTA2NjJlLTAyIDAuMTY1NiAtMTA2MS43Nzg2IC01NTIuMjIzMykiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KCQk8c3RvcCAgb2Zmc2V0PSI4LjEzMDAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNERTdGMTQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjMzODkiIHN0eWxlPSJzdG9wLWNvbG9yOiNEQTdEMTIiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjU5OTMiIHN0eWxlPSJzdG9wLWNvbG9yOiNDRTc2MEQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjg2MDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNCQTZDMDUiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjk4NyIgc3R5bGU9InN0b3AtY29sb3I6I0FENjUwMCIvPgoJPC9yYWRpYWxHcmFkaWVudD4KCTxwYXRoIGNsYXNzPSJzdDIzIiBkPSJNMjYuMywxMy43Yy0wLjEsMC44LDAuNiwxLjUsMS41LDEuNmMwLjgsMC4xLDEuNS0wLjQsMS43LTEuMWgtMC4xaC0wLjFoMC4yYzAtMC4xLDAtMC4xLDAtMC4yCgkJYzAuMS0wLjYtMC42LTEuMy0xLjUtMS4zYy0wLjgtMC4xLTEuNCwwLjItMS43LDAuN0MyNi4zLDEzLjUsMjYuMywxMy42LDI2LjMsMTMuN3oiLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMzhfIiBjeD0iLTI4OTUuMTQ2NyIgY3k9IjcyMzAuOTM4NSIgcj0iNy4xMDIiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMTM5NSAwLjEwOTUgOS4zMzg2MjdlLTAyIDAuMTAxOSAtMTA0OS4wOTQyIC00MDkuMzQ3OCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KCQk8c3RvcCAgb2Zmc2V0PSI4Ljc1MDAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiNEOTk5MTQiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjQwOTgiIHN0eWxlPSJzdG9wLWNvbG9yOiNENTk2MTIiLz4KCQk8c3RvcCAgb2Zmc2V0PSIwLjczNSIgc3R5bGU9InN0b3AtY29sb3I6I0M5OEQwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQkE4MjA3Ii8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzEyXyIgY2xhc3M9InN0MjQiIGQ9Ik0zMC4zLDExLjJjMCwwLjMtMC40LDAuNC0wLjksMC40cy0wLjgtMC40LTAuOC0wLjZzMC40LTAuNCwwLjktMC40CgkJQzMwLDEwLjYsMzAuMywxMC45LDMwLjMsMTEuMnoiLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iWE1MSURfMzlfIiBjeD0iLTYzMzQuNDI1OCIgY3k9IjMxODEuNDYzNCIgcj0iMTUuNjcyNSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC4xNzk2IC02LjgyMjU1NGUtMDMgLTEuMzkwNjYyZS0wMiAwLjE2NTYgLTEwNjEuNzc4NiAtNTUyLjIyMzMpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CgkJPHN0b3AgIG9mZnNldD0iOC4xMzAwMDBlLTAyIiBzdHlsZT0ic3RvcC1jb2xvcjojREU3RjE0Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC4zMzg5IiBzdHlsZT0ic3RvcC1jb2xvcjojREE3RDEyIi8+CgkJPHN0b3AgIG9mZnNldD0iMC41OTkzIiBzdHlsZT0ic3RvcC1jb2xvcjojQ0U3NjBEIi8+CgkJPHN0b3AgIG9mZnNldD0iMC44NjAyIiBzdHlsZT0ic3RvcC1jb2xvcjojQkE2QzA1Ii8+CgkJPHN0b3AgIG9mZnNldD0iMC45ODciIHN0eWxlPSJzdG9wLWNvbG9yOiNBRDY1MDAiLz4KCTwvcmFkaWFsR3JhZGllbnQ+Cgk8cGF0aCBpZD0iWE1MSURfMTFfIiBjbGFzcz0ic3QyNSIgZD0iTTMzLjIsMTguNWMtMC4xLDEuMS0xLjEsMS44LTIuMiwxLjZzLTIuMS0xLjEtMi0yLjJjMC4xLTEuMSwxLjEtMS44LDIuMi0xLjYKCQlDMzIuNCwxNi42LDMzLjMsMTcuNSwzMy4yLDE4LjV6Ii8+CgkKCQk8cmFkaWFsR3JhZGllbnQgaWQ9IlhNTElEXzQwXyIgY3g9Ii02MzMxLjIzNDQiIGN5PSIzMTQ4LjIxNzMiIHI9IjEwLjkxMTgiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMTc5NiAtNi44MjI1NTRlLTAzIC0xLjM5MDY2MmUtMDIgMC4xNjU2IC0xMDYxLjc3ODYgLTU1Mi4yMjMzKSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguNzUwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0Q5OTkxNCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNDA5OCIgc3R5bGU9InN0b3AtY29sb3I6I0Q1OTYxMiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNzM1IiBzdHlsZT0ic3RvcC1jb2xvcjojQzk4RDBEIi8+CgkJPHN0b3AgIG9mZnNldD0iMC45ODciIHN0eWxlPSJzdG9wLWNvbG9yOiNCQTgyMDciLz4KCTwvcmFkaWFsR3JhZGllbnQ+Cgk8cGF0aCBpZD0iWE1MSURfMTBfIiBjbGFzcz0ic3QyNiIgZD0iTTMzLjksMTMuMmMwLDAuNC0wLjYsMC42LTEuMywwLjVjLTAuNi0wLjEtMS4yLTAuNC0xLjItMC45YzAtMC40LDAuNi0wLjYsMS4zLTAuNQoJCVMzMy45LDEyLjksMzMuOSwxMy4yeiIvPgoJCgkJPHJhZGlhbEdyYWRpZW50IGlkPSJYTUxJRF80MV8iIGN4PSItNjM1Ny4xNzYzIiBjeT0iMzE5Ni44MTkzIiByPSI1LjkyODUiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMTc5NiAtNi44MjI1NTRlLTAzIC0xLjM5MDY2MmUtMDIgMC4xNjU2IC0xMDYxLjc3ODYgLTU1Mi4yMjMzKSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguMTMwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0RFN0YxNCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuMzM4OSIgc3R5bGU9InN0b3AtY29sb3I6I0RBN0QxMiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNTk5MyIgc3R5bGU9InN0b3AtY29sb3I6I0NFNzYwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuODYwMiIgc3R5bGU9InN0b3AtY29sb3I6I0JBNkMwNSIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQUQ2NTAwIi8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzlfIiBjbGFzcz0ic3QyNyIgZD0iTTM2LDIwLjljMCwwLjUtMC41LDAuOS0xLjIsMC44Yy0wLjYtMC4xLTEuMS0wLjUtMS0xLjFjMC0wLjUsMC41LTAuOSwxLjItMC44CgkJQzM1LjYsMTkuOSwzNi4xLDIwLjMsMzYsMjAuOXoiLz4KCQoJCTxyYWRpYWxHcmFkaWVudCBpZD0iU1ZHSURfNl8iIGN4PSItNjM2MS42ODE2IiBjeT0iMzE2Ny4wODY3IiByPSI4Ljg0MDgiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMTc5NiAtNi44MjI1NTRlLTAzIC0xLjM5MDY2MmUtMDIgMC4xNjU2IC0xMDYxLjc3ODYgLTU1Mi4yMjMzKSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguNjAwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0M3NzMxNSIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuMzc3NCIgc3R5bGU9InN0b3AtY29sb3I6I0MzNzExMyIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNjcxOCIgc3R5bGU9InN0b3AtY29sb3I6I0I3NkMwRiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTY2OSIgc3R5bGU9InN0b3AtY29sb3I6I0EzNjQwNyIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQTE2MzA2Ii8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggY2xhc3M9InN0MjgiIGQ9Ik0zNS41LDE2LjFjMCwwLjQsMC4zLDAuNywwLjcsMC44YzAuNCwwLjEsMC44LTAuMiwwLjktMC41di0wLjFsMCwwYzAtMC4zLTAuNC0wLjUtMC43LTAuNQoJCWMtMC40LDAtMC42LDAtMC43LDAuMmwwLDBDMzUuNiwxNS45LDM1LjUsMTYsMzUuNSwxNi4xeiIvPgoJCgkJPHJhZGlhbEdyYWRpZW50IGlkPSJYTUxJRF80Ml8iIGN4PSItNjM3NC4wOTMzIiBjeT0iMzE4NS45NTI0IiByPSI0LjM5ODUiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLTAuMTc5NiAtNi44MjI1NTRlLTAzIC0xLjM5MDY2MmUtMDIgMC4xNjU2IC0xMDYxLjc3ODYgLTU1Mi4yMjMzKSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgoJCTxzdG9wICBvZmZzZXQ9IjguMTMwMDAwZS0wMiIgc3R5bGU9InN0b3AtY29sb3I6I0RFN0YxNCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuMzM4OSIgc3R5bGU9InN0b3AtY29sb3I6I0RBN0QxMiIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuNTk5MyIgc3R5bGU9InN0b3AtY29sb3I6I0NFNzYwRCIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuODYwMiIgc3R5bGU9InN0b3AtY29sb3I6I0JBNkMwNSIvPgoJCTxzdG9wICBvZmZzZXQ9IjAuOTg3IiBzdHlsZT0ic3RvcC1jb2xvcjojQUQ2NTAwIi8+Cgk8L3JhZGlhbEdyYWRpZW50PgoJPHBhdGggaWQ9IlhNTElEXzhfIiBjbGFzcz0ic3QyOSIgZD0iTTM4LjksMTkuMWMwLDAuMy0wLjMsMC40LTAuNiwwLjRjLTAuNCwwLTAuNS0wLjMtMC41LTAuNnMwLjMtMC40LDAuNi0wLjQKCQlDMzguNywxOC41LDM4LjksMTguOCwzOC45LDE5LjF6Ii8+CjwvZz4KPC9zdmc+Cg==","base_uri":"https://bafybeibghcllcmurku7lxyg4wgxn2zsu5qqk7h4r6bmyhpztmyd564cx54.ipfs.nftstorage.link","reference":null,"reference_hash":null},"tokensLeft":184,"vip":false,"nfts":[],"mintRateLimit":10} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index f19555c..1435b47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2333,6 +2333,11 @@ schema-utils "^2.6.5" source-map "^0.7.3" +"@radix-ui/colors@^0.1.8": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@radix-ui/colors/-/colors-0.1.8.tgz#b08c62536fc462a87632165fb28e9b18f9bd047e" + integrity sha512-jwRMXYwC0hUo0mv6wGpuw254Pd9p/R6Td5xsRpOmaWkUHlooNWqVcadgyzlRumMq3xfOTXwJReU0Jv+EIy4Jbw== + "@radix-ui/number@0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-0.1.0.tgz#73ad13d5cc5f75fa5e147d72e5d5d5e50d688256" @@ -2388,6 +2393,27 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-dialog@^0.1.7": + version "0.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-0.1.7.tgz#285414cf66f5bbf42bc9935314e0381abe01e7d0" + integrity sha512-jXt8srGhHBRvEr9jhEAiwwJzWCWZoGRJ030aC9ja/gkRJbZdy0iD3FwXf+Ff4RtsZyLUMHW7VUwFOlz3Ixe1Vw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "0.1.0" + "@radix-ui/react-compose-refs" "0.1.0" + "@radix-ui/react-context" "0.1.1" + "@radix-ui/react-dismissable-layer" "0.1.5" + "@radix-ui/react-focus-guards" "0.1.0" + "@radix-ui/react-focus-scope" "0.1.4" + "@radix-ui/react-id" "0.1.5" + "@radix-ui/react-portal" "0.1.4" + "@radix-ui/react-presence" "0.1.2" + "@radix-ui/react-primitive" "0.1.4" + "@radix-ui/react-slot" "0.1.2" + "@radix-ui/react-use-controllable-state" "0.1.0" + aria-hidden "^1.1.1" + react-remove-scroll "^2.4.0" + "@radix-ui/react-dismissable-layer@0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-0.1.5.tgz#9379032351e79028d472733a5cc8ba4a0ea43314" @@ -2432,6 +2458,11 @@ "@radix-ui/react-primitive" "0.1.4" "@radix-ui/react-use-callback-ref" "0.1.0" +"@radix-ui/react-icons@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.1.1.tgz#38d2aa548035dd3b799c169bd17177b1cec3152b" + integrity sha512-xc3wQC59rsFylVbSusQCrrM+6695ppF730Q6yqzhRdqDcRNWIm2R6ngpzBoSOQMcwnq4p805F+Gr7xo4fmtN1A== + "@radix-ui/react-id@0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-0.1.5.tgz#010d311bedd5a2884c1e9bb6aaaa4e6cc1d1d3b8" @@ -2668,6 +2699,11 @@ escape-string-regexp "^2.0.0" lodash.deburr "^4.1.0" +"@stitches/react@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@stitches/react/-/react-1.2.8.tgz#954f8008be8d9c65c4e58efa0937f32388ce3a38" + integrity sha512-9g9dWI4gsSVe8bNLlb+lMkBYsnIKCZTmvqvDG+Avnn69XfmHZKiaMrx7cgTaddq7aTPPmXiTsbFcUy0xgI4+wA== + "@swc/helpers@^0.3.6": version "0.3.8" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.3.8.tgz#5b9ecf4ee480ca00f1ffbc2d1a5d4eed0d1afe81"