diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..52edac4 --- /dev/null +++ b/.env.sample @@ -0,0 +1,18 @@ +# https://console.upstash.com/redis +REDIS_URL= +REDIS_TOKEN= + +# https://warpcast.com/~/developers/api-keys +DC_API_KEY= + +# https://console.upstash.com/qstash +QSTASH_CURRENT_SIGNING_KEY= +QSTASH_NEXT_SIGNING_KEY= +# Qstash can be removed if using Vercel Cron +# https://vercel.com/docs/cron-jobs/quickstart +# DC cron endpoint: frame/send-notifications +# /frame/[[...routes]]/route.tsx line 44 - 61 can be removed and verified with vercel cron env + +NEXT_PUBLIC_SITE_URL= +NEXT_PUBLIC_GOOGLE_ANALYTICS_ID= +NEXT_PUBLIC_HOTJAR_ID= \ No newline at end of file diff --git a/.gitignore b/.gitignore index c87c9b3..5aad37a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ yarn-error.log* # local env files .env*.local +.env # vercel .vercel diff --git a/app/frame/[[...routes]]/route.tsx b/app/frame/[[...routes]]/route.tsx new file mode 100644 index 0000000..11589ff --- /dev/null +++ b/app/frame/[[...routes]]/route.tsx @@ -0,0 +1,702 @@ +/* eslint-disable react/no-unescaped-entities */ +/* eslint-disable react/jsx-key */ +/** @jsxImportSource frog/jsx */ + +import { Button, Frog, TextInput } from 'frog' +import { devtools } from 'frog/dev' +// import { neynar } from 'frog/hubs' +import { handle } from 'frog/next' +import { serveStatic } from 'frog/serve-static' +import { waitUntil } from "@vercel/functions"; +import { neynar } from "frog/hubs"; +import { Address } from "viem"; +import { base } from "viem/chains"; +import { unlockAbi } from "../abi"; +import config from "../config"; +import { + DAYS_CONTRACT_ADDRESSES, + MAIN_SITE_URL, + UNLOCK_REDIS_KEY, +} from "../constants"; +import { sdkInstance, qstashReceiver, kvStore } from "../services"; +import { + getCurrentDateUTC, + getDayImage, + getUserNftBalance, + getValidKeysForUser, + registerUserForNotifications, + sentDcToUser, +} from "../utils"; + +const app = new Frog({ + assetsPath: "/", + basePath: "/frame", + hub: neynar({ apiKey: "NEYNAR_FROG_FM" }), + title: "Unlock Protocol Advent Calendar", + browserLocation: MAIN_SITE_URL, + verifyOrigin: false, +}) + +// Uncomment to use Edge Runtime +// export const runtime = 'edge' + +app.hono.post("/send-notifications", async (c) => { + const signature = c.req.header("Upstash-Signature"); + console.log({ signature }); + if (!signature) { + console.log("Signature missing from request"); + return c.json({ error: "No signature provided" }, 400); + } + const body = await c.req.text(); + const currentDay = getCurrentDateUTC(); + + const isValid = await qstashReceiver + .verify({ + body, + signature, + }) + .catch((e) => false); + + if (!isValid) { + return c.json({ error: "Invalid signature" }, 400); + } + + const notificationMessage = ` + Day ${currentDay} NFT is now mintable!. + + ${config.PROD_URL}/frame/dc + + N/B: Unsubscribe from getting these notifications through the frame. + `; + async function sendUserDCs() { + // const userFids = [846887]; + const userFids = await kvStore.smembers(UNLOCK_REDIS_KEY); + for (const fid of userFids) { + console.log("Sending DC to user", fid); + await sentDcToUser(Number(fid), notificationMessage); + } + } + waitUntil(sendUserDCs()); + + return c.json({ message: "Notifications sent successfully" }); +}); + +app.frame("/", (c) => { + return c.res({ + image: "/start-image", + intents: [], + }); +}); + + +app.frame("/dc", (c) => { + return c.res({ + image: "/start-image", + intents: [ + , + , + ], + }); +}); + +app.image('/start-image', (c) => { + return c.res({ + image: , + }) +}) + +function StartImage() { + return ( +
+
+

+ Welcome to Unlock Protocol +

+

+ Advent Calendar +

+
+
+ + {/* Decorative Elements */} +
+
+
+ ); +} + +app.frame('/', (c) => { + const { buttonValue, inputText, status } = c + const fruit = inputText || buttonValue + return c.res({ + image: ( +
+
+ {status === 'response' + ? `Nice choice.${fruit ? ` ${fruit.toUpperCase()}!!` : ''}` + : 'Welcome!'} +
+
+ ), + intents: [ + , + , + , + , + status === 'response' && Reset, + ], + }) +}) + +function SubscribeImage({ type }: { type: "sub" | "unsub" }) { + const text = + type === "sub" + ? "You have successfully Subscribed to get DC notifications" + : "You have successfully Unsubscribed from DC notifications"; + return ( +
+
+

+ {text} +

+
+ + {/* Decorative Elements */} +
+
+
+ ); +} + + +app.frame("/sub", async (c) => { + const verified = c.verified; + const frameData = c.frameData; + if (!verified || !frameData) { + return c.error({ + message: "User not verified", + }); + } + const userFid = frameData.fid; + //check kv store set if fid is contained + const isSubscribed = await kvStore.sismember(UNLOCK_REDIS_KEY, userFid); + console.log({ userFid, isSubscribed }); + if (isSubscribed) { + await kvStore.srem(UNLOCK_REDIS_KEY, userFid); + } else { + await kvStore.sadd(UNLOCK_REDIS_KEY, userFid); + } + + return c.res({ + image: , + intents: [ + , + , + ], + }); +}); + +app.frame("/calendar", async (c) => { + const frameData = c.frameData; + const verified = c.verified; + + if (!frameData) { + return c.error({ + message: "Frame data missing", + }); + } + const userFid = frameData.fid; + const res = await sdkInstance.getUsersByFid([userFid]); + if (res.error) { + return c.error({ + message: "Something went wrong getting your user data", + }); + } + const userAddress = res.data[0].ethAddresses[0] as Address; + const today = getCurrentDateUTC(); + const validKeys = await getValidKeysForUser(userAddress, today); + console.log({ validKeys, userAddress }); + let nextMintableDay = -1; + for (let i = 0; i < today; i++) { + const hasMembership = validKeys[i]; + const hasPreviousMembership = i === 0 ? true : validKeys[i - 1]; + if (!hasMembership && hasPreviousMembership) { + nextMintableDay = i; + } + } + + const remainingDays = + nextMintableDay == -1 || nextMintableDay == today - 1 + ? 0 + : validKeys.slice(nextMintableDay + 1).length; + + const action = + nextMintableDay == -1 ? "/" : `/finish/${nextMintableDay}/${remainingDays}`; + return c.res({ + action, + image: ( + + ), + intents: [ + nextMintableDay === -1 ? ( + Home + ) : ( + + Mint Day {`${Number(nextMintableDay) + 1}`} + + ), + ], + }); +}); + +function AdventCalendarImage({ + currentDay, + nextMintableDay, +}: { + currentDay: number; + nextMintableDay: number; +}) { + const boxSize = 100; + const gap = 24; + function getBoxColor(day: number) { + if (nextMintableDay == -1 && day <= currentDay) { + return "#48bb78"; // green + } else if (day < nextMintableDay + 1) { + return "#48bb78"; // green + } else if (day >= nextMintableDay + 1 && day <= currentDay) { + return "#ed8936"; // orange + } else if (day > currentDay) { + return "#718096"; // gray + } else { + return "#4299e1"; // blue + } + } + + const rowStyle = { + display: "flex", + gap: `${gap}px`, + width: "100%", + justifyContent: "center", + marginBottom: `${gap}px`, + }; + + const boxStyle = (day: number) => ({ + width: boxSize, + height: boxSize, + backgroundColor: getBoxColor(day), + display: "flex", + alignItems: "center", + justifyContent: "center", + fontSize: 24, + fontWeight: "bold", + borderRadius: 8, + color: "white", + }); + + const containerStyle = { + width: "100%", + height: "100%", + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + backgroundColor: "#1a365d", + color: "white", + fontFamily: "sans-serif", + }; + + const messageStyle = { + fontSize: 32, + fontWeight: "bold", + marginTop: 32, + textAlign: "center", + display: "flex", + gap: "2px", + }; + + let rows: number[][] = []; + if (nextMintableDay == -1) { + const row1 = Array.from({ length: 8 }, (_, i) => i + 1); + const row2 = Array.from({ length: 8 }, (_, i) => i + 9); + const row3 = Array.from({ length: 8 }, (_, i) => i + 17); + rows = [row1, row2, row3]; + } else { + const row1 = Array.from({ length: 6 }, (_, i) => i + 1); + const row2 = Array.from({ length: 6 }, (_, i) => i + 7); + const row3 = Array.from({ length: 6 }, (_, i) => i + 13); + const row4 = Array.from({ length: 6 }, (_, i) => i + 19); + rows = [row1, row2, row3, row4]; + } + + return ( +
+

+ Your Advent Calendar 2024 +

+ {rows.map((row, i) => ( +
+ {row.map((day) => ( +
+ {day} +
+ ))} +
+ ))} + {nextMintableDay === -1 && ( +
+ 🎉 Congratulations! + You have minted all days. Come back tomorrow! 🎉 +
+ )} +
+ ); +} + +app.frame("/finish/:day/:remainingDays", async (c) => { + const { day, remainingDays: remDays } = c.req.param(); + const remainingDays = Number(remDays); + const verified = c.verified; + const frameData = c.frameData; + const mintedDay = Number(day) + 1; + + if (verified && frameData) { + const userFid = frameData.fid; + await registerUserForNotifications(userFid); + } + const mintedImageUrl = getDayImage(mintedDay); + return c.res({ + image: ( + + ), + + intents: [], + }); +}); + +interface FinalImageProps { + mintedDay: number; + remainingDays: number; + mintedImageUrl: string; +} + +function FinalImage({ + mintedDay, + remainingDays, + mintedImageUrl, +}: FinalImageProps) { + const containerStyle = { + width: "100%", + height: "100%", + display: "flex", + flexDirection: "column" as const, + alignItems: "center", + justifyContent: "center", + backgroundColor: "#1a365d", + color: "white", + fontFamily: "sans-serif", + padding: "40px", + gap: "24px", + }; + + const cardStyle = { + backgroundColor: "#2d3748", + borderRadius: "16px", + padding: "32px", + display: "flex", + flexDirection: "column" as const, + alignItems: "center", + gap: "24px", + maxWidth: "600px", + width: "100%", + boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)", + }; + + const imageStyle = { + width: "300px", + height: "300px", + borderRadius: "12px", + objectFit: "cover" as const, + backgroundColor: "#4a5568", + }; + + const titleStyle = { + fontSize: "32px", + fontWeight: "bold", + textAlign: "center" as const, + color: "#48bb78", + }; + + const messageStyle = { + fontSize: "24px", + textAlign: "center" as const, + color: "#e2e8f0", + lineHeight: 1.6, + }; + + const getMessage = () => { + if (remainingDays === 0) { + if (mintedDay === 24) { + return ( + + Congratulations! You've completed all days of the advent calendar! + + ); + } + return Come back tomorrow to mint day {mintedDay + 1}.; + } + + const daysText = remainingDays === 1 ? "day" : "days"; + + return ( + + You still have + + {remainingDays} {daysText} + {" "} + to be minted. + + ); + }; + + return ( +
+
+

Day {mintedDay} Successfully Minted! 🎉

+ + {mintedImageUrl && ( + {`Day + )} + +

{getMessage()}

+
+
+ ); +} + +app.transaction("/tx/:day/:address", async (c) => { + const { day, address } = c.req.param(); + const userAddress = address as Address; + const nextMintableDay = Number(day); + if (isNaN(nextMintableDay)) { + return c.error({ message: "Invalid day" }); + } + console.log({ currentDay: nextMintableDay }); + const currentDayContract = DAYS_CONTRACT_ADDRESSES[nextMintableDay]; + console.log({ currentDayContract }); + const userBalance = await getUserNftBalance( + userAddress, + currentDayContract + ).catch((e) => null); + if (userBalance === null) { + return c.error({ + message: "You already minted today's NFT", + }); + } + + if (Number(userBalance) == 1) { + return c.error({ + message: "You already minted today's NFT", + }); + } + + //complexlity.eth + const referrerAddress = + "0x8ff47879d9eE072b593604b8b3009577Ff7d6809" as Address; + return c.contract({ + abi: unlockAbi, + functionName: "purchase", + //@ts-ignore: 0x0 = 0n same thing + args: [["0x0"], [userAddress], [referrerAddress], [userAddress], ["0x0"]], + to: currentDayContract, + chainId: `eip155:${base.id}`, + //@ts-ignore: 0x0 = 0n same thing + value: "0x0", + }); +}); + + +devtools(app, { serveStatic }) + +export const GET = handle(app) +export const POST = handle(app) + +// NOTE: That if you are using the devtools and enable Edge Runtime, you will need to copy the devtools +// static assets to the public folder. You can do this by adding a script to your package.json: +// ```json +// { +// scripts: { +// "copy-static": "cp -r ./node_modules/frog/_lib/ui/.frog ./public/.frog" +// } +// } +// ``` +// Next, you'll want to set up the devtools to use the correct assets path: +// ```ts +// devtools(app, { assetsPath: '/.frog' }) +// ``` diff --git a/app/frame/abi.ts b/app/frame/abi.ts new file mode 100644 index 0000000..64cccfc --- /dev/null +++ b/app/frame/abi.ts @@ -0,0 +1,3999 @@ +export const unlockAbi = [ + { inputs: [], name: "CANNOT_APPROVE_SELF", type: "error" }, + { inputs: [], name: "CANT_BE_SMALLER_THAN_SUPPLY", type: "error" }, + { inputs: [], name: "CANT_EXTEND_NON_EXPIRING_KEY", type: "error" }, + { inputs: [], name: "GAS_REFUND_FAILED", type: "error" }, + { inputs: [], name: "INSUFFICIENT_ERC20_VALUE", type: "error" }, + { inputs: [], name: "INSUFFICIENT_VALUE", type: "error" }, + { inputs: [], name: "INVALID_ADDRESS", type: "error" }, + { + inputs: [{ internalType: "uint8", name: "hookIndex", type: "uint8" }], + name: "INVALID_HOOK", + type: "error", + }, + { inputs: [], name: "INVALID_LENGTH", type: "error" }, + { inputs: [], name: "INVALID_TOKEN", type: "error" }, + { inputs: [], name: "KEY_NOT_VALID", type: "error" }, + { inputs: [], name: "KEY_TRANSFERS_DISABLED", type: "error" }, + { inputs: [], name: "LOCK_HAS_CHANGED", type: "error" }, + { inputs: [], name: "LOCK_SOLD_OUT", type: "error" }, + { inputs: [], name: "MAX_KEYS_REACHED", type: "error" }, + { inputs: [], name: "MIGRATION_REQUIRED", type: "error" }, + { inputs: [], name: "NON_COMPLIANT_ERC721_RECEIVER", type: "error" }, + { inputs: [], name: "NON_RENEWABLE_LOCK", type: "error" }, + { inputs: [], name: "NOT_ENOUGH_FUNDS", type: "error" }, + { inputs: [], name: "NOT_ENOUGH_TIME", type: "error" }, + { inputs: [], name: "NOT_READY_FOR_RENEWAL", type: "error" }, + { inputs: [], name: "NO_SUCH_KEY", type: "error" }, + { inputs: [], name: "NULL_VALUE", type: "error" }, + { inputs: [], name: "ONLY_KEY_MANAGER_OR_APPROVED", type: "error" }, + { inputs: [], name: "ONLY_LOCK_MANAGER", type: "error" }, + { inputs: [], name: "ONLY_LOCK_MANAGER_OR_KEY_GRANTER", type: "error" }, + { inputs: [], name: "OUT_OF_RANGE", type: "error" }, + { inputs: [], name: "OWNER_CANT_BE_ADDRESS_ZERO", type: "error" }, + { inputs: [], name: "SCHEMA_VERSION_NOT_CORRECT", type: "error" }, + { inputs: [], name: "TRANSFER_TO_SELF", type: "error" }, + { inputs: [], name: "TransferFailed", type: "error" }, + { inputs: [], name: "UNAUTHORIZED", type: "error" }, + { inputs: [], name: "UNAUTHORIZED_KEY_MANAGER_UPDATE", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "approved", + type: "address", + }, + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "Approval", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "operator", + type: "address", + }, + { + indexed: false, + internalType: "bool", + name: "approved", + type: "bool", + }, + ], + name: "ApprovalForAll", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sendTo", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "refund", + type: "uint256", + }, + ], + name: "CancelKey", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "onKeyPurchaseHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onKeyCancelHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onValidKeyHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onTokenURIHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onKeyTransferHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onKeyExtendHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onKeyGrantHook", + type: "address", + }, + ], + name: "EventHooksUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "prevExpiration", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "newExpiration", + type: "uint256", + }, + ], + name: "ExpirationChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "ExpireKey", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "refundValue", + type: "uint256", + }, + ], + name: "GasRefundValueChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "refundedAmount", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "tokenAddress", + type: "address", + }, + ], + name: "GasRefunded", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint8", + name: "version", + type: "uint8", + }, + ], + name: "Initialized", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "newTimestamp", + type: "uint256", + }, + ], + name: "KeyExtended", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "KeyGranterAdded", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "KeyGranterRemoved", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + indexed: true, + internalType: "address", + name: "_newManager", + type: "address", + }, + ], + name: "KeyManagerChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "updatedRecordsCount", + type: "uint256", + }, + ], + name: "KeysMigrated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "expirationDuration", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "maxNumberOfKeys", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "maxKeysPerAcccount", + type: "uint256", + }, + ], + name: "LockConfig", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "LockManagerAdded", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "LockManagerRemoved", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "string", + name: "name", + type: "string", + }, + { + indexed: false, + internalType: "string", + name: "symbol", + type: "string", + }, + { + indexed: false, + internalType: "string", + name: "baseTokenURI", + type: "string", + }, + ], + name: "LockMetadata", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "previousOwner", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "oldKeyPrice", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "keyPrice", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "oldTokenAddress", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "tokenAddress", + type: "address", + }, + ], + name: "PricingChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "referrer", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "fee", + type: "uint256", + }, + ], + name: "ReferrerFee", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "freeTrialLength", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "refundPenaltyBasisPoints", + type: "uint256", + }, + ], + name: "RefundPenaltyChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "bytes32", + name: "previousAdminRole", + type: "bytes32", + }, + { + indexed: true, + internalType: "bytes32", + name: "newAdminRole", + type: "bytes32", + }, + ], + name: "RoleAdminChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleGranted", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleRevoked", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "from", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "to", + type: "address", + }, + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "Transfer", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "transferFeeBasisPoints", + type: "uint256", + }, + ], + name: "TransferFeeChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "lockAddress", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "unlockAddress", + type: "address", + }, + ], + name: "UnlockCallFailed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "tokenAddress", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "Withdrawal", + type: "event", + }, + { + inputs: [], + name: "DEFAULT_ADMIN_ROLE", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "addLockManager", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_approved", type: "address" }, + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + ], + name: "approve", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_keyOwner", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "balance", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }], + name: "burn", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }], + name: "cancelAndRefund", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "expirationDuration", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + ], + name: "expireAndRefundFor", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_value", type: "uint256" }, + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + { internalType: "address", name: "_referrer", type: "address" }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "extend", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "freeTrialLength", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "gasRefundValue", + outputs: [ + { internalType: "uint256", name: "_refundValue", type: "uint256" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }], + name: "getApproved", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }], + name: "getCancelAndRefundValue", + outputs: [{ internalType: "uint256", name: "refund", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_keyOwner", type: "address" }], + name: "getHasValidKey", + outputs: [{ internalType: "bool", name: "isValid", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "bytes32", name: "role", type: "bytes32" }], + name: "getRoleAdmin", + outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + { internalType: "uint256", name: "_time", type: "uint256" }, + ], + name: "getTransferFee", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + { internalType: "uint256", name: "_duration", type: "uint256" }, + ], + name: "grantKeyExtension", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address[]", + name: "_recipients", + type: "address[]", + }, + { + internalType: "uint256[]", + name: "_expirationTimestamps", + type: "uint256[]", + }, + { + internalType: "address[]", + name: "_keyManagers", + type: "address[]", + }, + ], + name: "grantKeys", + outputs: [{ internalType: "uint256[]", name: "", type: "uint256[]" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "role", type: "bytes32" }, + { internalType: "address", name: "account", type: "address" }, + ], + name: "grantRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "role", type: "bytes32" }, + { internalType: "address", name: "account", type: "address" }, + ], + name: "hasRole", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address payable", + name: "_lockCreator", + type: "address", + }, + { + internalType: "uint256", + name: "_expirationDuration", + type: "uint256", + }, + { internalType: "address", name: "_tokenAddress", type: "address" }, + { internalType: "uint256", name: "_keyPrice", type: "uint256" }, + { + internalType: "uint256", + name: "_maxNumberOfKeys", + type: "uint256", + }, + { internalType: "string", name: "_lockName", type: "string" }, + ], + name: "initialize", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_owner", type: "address" }, + { internalType: "address", name: "_operator", type: "address" }, + ], + name: "isApprovedForAll", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "isLockManager", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "isOwner", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + { internalType: "address", name: "_referrer", type: "address" }, + ], + name: "isRenewable", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }], + name: "isValidKey", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }], + name: "keyExpirationTimestampFor", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "", type: "uint256" }], + name: "keyManagerOf", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "keyPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_from", type: "address" }, + { internalType: "address", name: "_recipient", type: "address" }, + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + ], + name: "lendKey", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "maxKeysPerAddress", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "maxNumberOfKeys", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_tokenIdFrom", type: "uint256" }, + { internalType: "uint256", name: "_tokenIdTo", type: "uint256" }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + ], + name: "mergeKeys", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "bytes", name: "", type: "bytes" }], + name: "migrate", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "numberOfOwners", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyCancelHook", + outputs: [ + { + internalType: "contract ILockKeyCancelHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyExtendHook", + outputs: [ + { + internalType: "contract ILockKeyExtendHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyGrantHook", + outputs: [ + { + internalType: "contract ILockKeyGrantHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyPurchaseHook", + outputs: [ + { + internalType: "contract ILockKeyPurchaseHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyTransferHook", + outputs: [ + { + internalType: "contract ILockKeyTransferHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onTokenURIHook", + outputs: [ + { + internalType: "contract ILockTokenURIHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onValidKeyHook", + outputs: [ + { + internalType: "contract ILockValidKeyHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }], + name: "ownerOf", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "publicLockVersion", + outputs: [{ internalType: "uint16", name: "", type: "uint16" }], + stateMutability: "pure", + type: "function", + }, + { + inputs: [ + { internalType: "uint256[]", name: "_values", type: "uint256[]" }, + { + internalType: "address[]", + name: "_recipients", + type: "address[]", + }, + { + internalType: "address[]", + name: "_referrers", + type: "address[]", + }, + { + internalType: "address[]", + name: "_keyManagers", + type: "address[]", + }, + { internalType: "bytes[]", name: "_data", type: "bytes[]" }, + ], + name: "purchase", + outputs: [{ internalType: "uint256[]", name: "", type: "uint256[]" }], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_recipient", type: "address" }, + { internalType: "address", name: "_referrer", type: "address" }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "purchasePriceFor", + outputs: [ + { internalType: "uint256", name: "minKeyPrice", type: "uint256" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "referrerFees", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "refundPenaltyBasisPoints", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + { internalType: "address", name: "_referrer", type: "address" }, + ], + name: "renewMembershipFor", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "renounceLockManager", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "role", type: "bytes32" }, + { internalType: "address", name: "account", type: "address" }, + ], + name: "renounceRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "role", type: "bytes32" }, + { internalType: "address", name: "account", type: "address" }, + ], + name: "revokeRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_from", type: "address" }, + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + ], + name: "safeTransferFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_from", type: "address" }, + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "safeTransferFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "schemaVersion", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "bool", name: "_approved", type: "bool" }, + ], + name: "setApprovalForAll", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_onKeyPurchaseHook", + type: "address", + }, + { + internalType: "address", + name: "_onKeyCancelHook", + type: "address", + }, + { + internalType: "address", + name: "_onValidKeyHook", + type: "address", + }, + { + internalType: "address", + name: "_onTokenURIHook", + type: "address", + }, + { + internalType: "address", + name: "_onKeyTransferHook", + type: "address", + }, + { + internalType: "address", + name: "_onKeyExtendHook", + type: "address", + }, + { + internalType: "address", + name: "_onKeyGrantHook", + type: "address", + }, + ], + name: "setEventHooks", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_refundValue", type: "uint256" }, + ], + name: "setGasRefundValue", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + { internalType: "uint256", name: "_newExpiration", type: "uint256" }, + ], + name: "setKeyExpiration", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + { internalType: "address", name: "_keyManager", type: "address" }, + ], + name: "setKeyManagerOf", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "string", name: "_lockName", type: "string" }, + { internalType: "string", name: "_lockSymbol", type: "string" }, + { internalType: "string", name: "_baseTokenURI", type: "string" }, + ], + name: "setLockMetadata", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "setOwner", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_referrer", type: "address" }, + { internalType: "uint256", name: "_feeBasisPoint", type: "uint256" }, + ], + name: "setReferrerFee", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_tokenIdFrom", type: "uint256" }, + { internalType: "uint256", name: "_timeShared", type: "uint256" }, + ], + name: "shareKey", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "bytes4", name: "interfaceId", type: "bytes4" }], + name: "supportsInterface", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "symbol", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "tokenAddress", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_index", type: "uint256" }], + name: "tokenByIndex", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_keyOwner", type: "address" }, + { internalType: "uint256", name: "_index", type: "uint256" }, + ], + name: "tokenOfOwnerByIndex", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }], + name: "tokenURI", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_keyOwner", type: "address" }], + name: "totalKeys", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalSupply", + outputs: [ + { + internalType: "uint256", + name: "_totalKeysCreated", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "transferFeeBasisPoints", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_from", type: "address" }, + { internalType: "address", name: "_recipient", type: "address" }, + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + ], + name: "transferFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_recipient", type: "address" }, + { internalType: "uint256", name: "_tokenId", type: "uint256" }, + ], + name: "unlendKey", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "unlockProtocol", + outputs: [{ internalType: "contract IUnlock", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "_keyPrice", type: "uint256" }, + { internalType: "address", name: "_tokenAddress", type: "address" }, + ], + name: "updateKeyPricing", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_newExpirationDuration", + type: "uint256", + }, + { + internalType: "uint256", + name: "_maxNumberOfKeys", + type: "uint256", + }, + { + internalType: "uint256", + name: "_maxKeysPerAcccount", + type: "uint256", + }, + ], + name: "updateLockConfig", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_freeTrialLength", + type: "uint256", + }, + { + internalType: "uint256", + name: "_refundPenaltyBasisPoints", + type: "uint256", + }, + ], + name: "updateRefundPenalty", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "updateSchemaVersion", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_transferFeeBasisPoints", + type: "uint256", + }, + ], + name: "updateTransferFee", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_tokenAddress", type: "address" }, + { + internalType: "address payable", + name: "_recipient", + type: "address", + }, + { internalType: "uint256", name: "_amount", type: "uint256" }, + ], + name: "withdraw", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { stateMutability: "payable", type: "receive" }, +] as const; + +export const hookAbi = [ + { + inputs: [{ internalType: "uint256", name: "day", type: "uint256" }], + name: "BAD_DAY", + type: "error", + }, + { + inputs: [{ internalType: "uint256", name: "day", type: "uint256" }], + name: "MISSING_PREVIOUS_DAY", + type: "error", + }, + { + inputs: [{ internalType: "uint256", name: "day", type: "uint256" }], + name: "TOO_EARLY", + type: "error", + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "dayByLock", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address[]", name: "_locks", type: "address[]" }, + { internalType: "uint256", name: "_start", type: "uint256" }, + ], + name: "initialize", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "address", name: "", type: "address" }, + { internalType: "bytes", name: "", type: "bytes" }, + ], + name: "keyPurchasePrice", + outputs: [ + { internalType: "uint256", name: "minKeyPrice", type: "uint256" }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "", type: "uint256" }], + name: "lockByDay", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "tokenId", type: "uint256" }, + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "address", name: "", type: "address" }, + { internalType: "bytes", name: "", type: "bytes" }, + { internalType: "uint256", name: "", type: "uint256" }, + { internalType: "uint256", name: "", type: "uint256" }, + ], + name: "onKeyPurchase", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "start", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint256", name: "", type: "uint256" }], + name: "winnerIndices", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "uint256", name: "", type: "uint256" }, + { internalType: "uint256", name: "", type: "uint256" }, + ], + name: "winners", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, +]; + +export const PublicLockAbi = [ + { + inputs: [], + name: "CANNOT_APPROVE_SELF", + type: "error", + }, + { + inputs: [], + name: "CANT_EXTEND_NON_EXPIRING_KEY", + type: "error", + }, + { + inputs: [], + name: "GAS_REFUND_FAILED", + type: "error", + }, + { + inputs: [], + name: "INSUFFICIENT_ERC20_VALUE", + type: "error", + }, + { + inputs: [], + name: "INSUFFICIENT_VALUE", + type: "error", + }, + { + inputs: [], + name: "INVALID_ADDRESS", + type: "error", + }, + { + inputs: [ + { + internalType: "uint8", + name: "hookIndex", + type: "uint8", + }, + ], + name: "INVALID_HOOK", + type: "error", + }, + { + inputs: [], + name: "INVALID_LENGTH", + type: "error", + }, + { + inputs: [], + name: "INVALID_TOKEN", + type: "error", + }, + { + inputs: [], + name: "KEY_NOT_VALID", + type: "error", + }, + { + inputs: [], + name: "KEY_TRANSFERS_DISABLED", + type: "error", + }, + { + inputs: [], + name: "LOCK_HAS_CHANGED", + type: "error", + }, + { + inputs: [], + name: "LOCK_SOLD_OUT", + type: "error", + }, + { + inputs: [], + name: "MAX_KEYS_REACHED", + type: "error", + }, + { + inputs: [], + name: "MIGRATION_REQUIRED", + type: "error", + }, + { + inputs: [], + name: "NON_COMPLIANT_ERC721_RECEIVER", + type: "error", + }, + { + inputs: [], + name: "NON_RENEWABLE_LOCK", + type: "error", + }, + { + inputs: [], + name: "NOT_ENOUGH_FUNDS", + type: "error", + }, + { + inputs: [], + name: "NOT_ENOUGH_TIME", + type: "error", + }, + { + inputs: [], + name: "NOT_READY_FOR_RENEWAL", + type: "error", + }, + { + inputs: [], + name: "NO_SUCH_KEY", + type: "error", + }, + { + inputs: [], + name: "NULL_VALUE", + type: "error", + }, + { + inputs: [], + name: "ONLY_KEY_MANAGER_OR_APPROVED", + type: "error", + }, + { + inputs: [], + name: "ONLY_LOCK_MANAGER", + type: "error", + }, + { + inputs: [], + name: "ONLY_LOCK_MANAGER_OR_KEY_GRANTER", + type: "error", + }, + { + inputs: [], + name: "OUT_OF_RANGE", + type: "error", + }, + { + inputs: [], + name: "OWNER_CANT_BE_ADDRESS_ZERO", + type: "error", + }, + { + inputs: [], + name: "SCHEMA_VERSION_NOT_CORRECT", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + internalType: "uint256", + name: "length", + type: "uint256", + }, + ], + name: "StringsInsufficientHexLength", + type: "error", + }, + { + inputs: [], + name: "TRANSFER_TO_SELF", + type: "error", + }, + { + inputs: [], + name: "TransferFailed", + type: "error", + }, + { + inputs: [], + name: "UNAUTHORIZED", + type: "error", + }, + { + inputs: [], + name: "UNAUTHORIZED_KEY_MANAGER_UPDATE", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "approved", + type: "address", + }, + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "Approval", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sendTo", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "refund", + type: "uint256", + }, + ], + name: "CancelKey", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "onKeyPurchaseHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onKeyCancelHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onValidKeyHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onTokenURIHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onKeyTransferHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onKeyExtendHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onKeyGrantHook", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "onHasRoleHook", + type: "address", + }, + ], + name: "EventHooksUpdated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "prevExpiration", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "newExpiration", + type: "uint256", + }, + ], + name: "ExpirationChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "ExpireKey", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "refundValue", + type: "uint256", + }, + ], + name: "GasRefundValueChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "receiver", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "refundedAmount", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "tokenAddress", + type: "address", + }, + ], + name: "GasRefunded", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint8", + name: "version", + type: "uint8", + }, + ], + name: "Initialized", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "newTimestamp", + type: "uint256", + }, + ], + name: "KeyExtended", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "KeyGranterAdded", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "KeyGranterRemoved", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + indexed: true, + internalType: "address", + name: "_newManager", + type: "address", + }, + ], + name: "KeyManagerChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "updatedRecordsCount", + type: "uint256", + }, + ], + name: "KeysMigrated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "expirationDuration", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "maxNumberOfKeys", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "maxKeysPerAcccount", + type: "uint256", + }, + ], + name: "LockConfig", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "LockManagerAdded", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "LockManagerRemoved", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "string", + name: "name", + type: "string", + }, + { + indexed: false, + internalType: "string", + name: "symbol", + type: "string", + }, + { + indexed: false, + internalType: "string", + name: "baseTokenURI", + type: "string", + }, + ], + name: "LockMetadata", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "previousOwner", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256[]", + name: "tokenIds", + type: "uint256[]", + }, + { + indexed: false, + internalType: "uint256", + name: "purchases", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "extensions", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "payer", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "tokenAddress", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "totalPaid", + type: "uint256", + }, + ], + name: "PaymentReceipt", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "oldKeyPrice", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "keyPrice", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "oldTokenAddress", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "tokenAddress", + type: "address", + }, + ], + name: "PricingChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "referrer", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "fee", + type: "uint256", + }, + ], + name: "ReferrerFee", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "tokenAddress", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "referrer", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "fee", + type: "uint256", + }, + ], + name: "ReferrerPaid", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "freeTrialLength", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "refundPenaltyBasisPoints", + type: "uint256", + }, + ], + name: "RefundPenaltyChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "bytes32", + name: "previousAdminRole", + type: "bytes32", + }, + { + indexed: true, + internalType: "bytes32", + name: "newAdminRole", + type: "bytes32", + }, + ], + name: "RoleAdminChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleGranted", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleRevoked", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "from", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "to", + type: "address", + }, + { + indexed: true, + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "Transfer", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint256", + name: "transferFeeBasisPoints", + type: "uint256", + }, + ], + name: "TransferFeeChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "lockAddress", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "unlockAddress", + type: "address", + }, + ], + name: "UnlockCallFailed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "tokenAddress", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "recipient", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "Withdrawal", + type: "event", + }, + { + inputs: [], + name: "DEFAULT_ADMIN_ROLE", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_approved", + type: "address", + }, + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "approve", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_keyOwner", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { + internalType: "uint256", + name: "balance", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "burn", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "cancelAndRefund", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "expirationDuration", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + internalType: "uint256", + name: "_amount", + type: "uint256", + }, + ], + name: "expireAndRefundFor", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_value", + type: "uint256", + }, + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + internalType: "address", + name: "_referrer", + type: "address", + }, + { + internalType: "bytes", + name: "_data", + type: "bytes", + }, + ], + name: "extend", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "freeTrialLength", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "gasRefundValue", + outputs: [ + { + internalType: "uint256", + name: "_refundValue", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "getApproved", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "getCancelAndRefundValue", + outputs: [ + { + internalType: "uint256", + name: "refund", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_keyOwner", + type: "address", + }, + ], + name: "getHasValidKey", + outputs: [ + { + internalType: "bool", + name: "isValid", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + ], + name: "getRoleAdmin", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + internalType: "uint256", + name: "_time", + type: "uint256", + }, + ], + name: "getTransferFee", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + internalType: "uint256", + name: "_duration", + type: "uint256", + }, + ], + name: "grantKeyExtension", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address[]", + name: "_recipients", + type: "address[]", + }, + { + internalType: "uint256[]", + name: "_expirationTimestamps", + type: "uint256[]", + }, + { + internalType: "address[]", + name: "_keyManagers", + type: "address[]", + }, + ], + name: "grantKeys", + outputs: [ + { + internalType: "uint256[]", + name: "", + type: "uint256[]", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "grantRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "hasRole", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address payable", + name: "_lockCreator", + type: "address", + }, + { + internalType: "uint256", + name: "_expirationDuration", + type: "uint256", + }, + { + internalType: "address", + name: "_tokenAddress", + type: "address", + }, + { + internalType: "uint256", + name: "_keyPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "_maxNumberOfKeys", + type: "uint256", + }, + { + internalType: "string", + name: "_lockName", + type: "string", + }, + ], + name: "initialize", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "isLockManager", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "isOwner", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + internalType: "address", + name: "_referrer", + type: "address", + }, + ], + name: "isRenewable", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "isValidKey", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "keyExpirationTimestampFor", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "keyManagerOf", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "keyPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_from", + type: "address", + }, + { + internalType: "address", + name: "_recipient", + type: "address", + }, + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "lendKey", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "maxKeysPerAddress", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "maxNumberOfKeys", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenIdFrom", + type: "uint256", + }, + { + internalType: "uint256", + name: "_tokenIdTo", + type: "uint256", + }, + { + internalType: "uint256", + name: "_amount", + type: "uint256", + }, + ], + name: "mergeKeys", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes", + name: "", + type: "bytes", + }, + ], + name: "migrate", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "numberOfOwners", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onHasRoleHook", + outputs: [ + { + internalType: "contract ILockHasRoleHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyCancelHook", + outputs: [ + { + internalType: "contract ILockKeyCancelHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyExtendHook", + outputs: [ + { + internalType: "contract ILockKeyExtendHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyGrantHook", + outputs: [ + { + internalType: "contract ILockKeyGrantHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyPurchaseHook", + outputs: [ + { + internalType: "contract ILockKeyPurchaseHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onKeyTransferHook", + outputs: [ + { + internalType: "contract ILockKeyTransferHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onTokenURIHook", + outputs: [ + { + internalType: "contract ILockTokenURIHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "onValidKeyHook", + outputs: [ + { + internalType: "contract ILockValidKeyHook", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "ownerOf", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "publicLockVersion", + outputs: [ + { + internalType: "uint16", + name: "", + type: "uint16", + }, + ], + stateMutability: "pure", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256[]", + name: "_values", + type: "uint256[]", + }, + { + internalType: "address[]", + name: "_recipients", + type: "address[]", + }, + { + internalType: "address[]", + name: "_referrers", + type: "address[]", + }, + { + internalType: "address[]", + name: "_keyManagers", + type: "address[]", + }, + { + internalType: "bytes[]", + name: "_data", + type: "bytes[]", + }, + ], + name: "purchase", + outputs: [ + { + internalType: "uint256[]", + name: "", + type: "uint256[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + internalType: "address", + name: "recipient", + type: "address", + }, + { + internalType: "address", + name: "referrer", + type: "address", + }, + { + internalType: "address", + name: "protocolReferrer", + type: "address", + }, + { + internalType: "address", + name: "keyManager", + type: "address", + }, + { + internalType: "bytes", + name: "data", + type: "bytes", + }, + { + internalType: "uint256", + name: "additionalPeriods", + type: "uint256", + }, + ], + internalType: "struct MixinPurchase.PurchaseArgs[]", + name: "purchaseArgs", + type: "tuple[]", + }, + ], + name: "purchase", + outputs: [ + { + internalType: "uint256[]", + name: "", + type: "uint256[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_recipient", + type: "address", + }, + { + internalType: "address", + name: "_referrer", + type: "address", + }, + { + internalType: "bytes", + name: "_data", + type: "bytes", + }, + ], + name: "purchasePriceFor", + outputs: [ + { + internalType: "uint256", + name: "minKeyPrice", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "referrerFees", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "refundPenaltyBasisPoints", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + internalType: "address", + name: "_referrer", + type: "address", + }, + ], + name: "renewMembershipFor", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "renounceRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "revokeRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_from", + type: "address", + }, + { + internalType: "address", + name: "_to", + type: "address", + }, + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "safeTransferFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_from", + type: "address", + }, + { + internalType: "address", + name: "_to", + type: "address", + }, + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + internalType: "bytes", + name: "_data", + type: "bytes", + }, + ], + name: "safeTransferFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_onKeyPurchaseHook", + type: "address", + }, + { + internalType: "address", + name: "_onKeyCancelHook", + type: "address", + }, + { + internalType: "address", + name: "_onValidKeyHook", + type: "address", + }, + { + internalType: "address", + name: "_onTokenURIHook", + type: "address", + }, + { + internalType: "address", + name: "_onKeyTransferHook", + type: "address", + }, + { + internalType: "address", + name: "_onKeyExtendHook", + type: "address", + }, + { + internalType: "address", + name: "_onKeyGrantHook", + type: "address", + }, + { + internalType: "address", + name: "_onHasRoleHook", + type: "address", + }, + ], + name: "setEventHooks", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_refundValue", + type: "uint256", + }, + ], + name: "setGasRefundValue", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + internalType: "uint256", + name: "_newExpiration", + type: "uint256", + }, + ], + name: "setKeyExpiration", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + { + internalType: "address", + name: "_keyManager", + type: "address", + }, + ], + name: "setKeyManagerOf", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "string", + name: "_lockName", + type: "string", + }, + { + internalType: "string", + name: "_lockSymbol", + type: "string", + }, + { + internalType: "string", + name: "_baseTokenURI", + type: "string", + }, + ], + name: "setLockMetadata", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "setOwner", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_referrer", + type: "address", + }, + { + internalType: "uint256", + name: "_feeBasisPoint", + type: "uint256", + }, + ], + name: "setReferrerFee", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_to", + type: "address", + }, + { + internalType: "uint256", + name: "_tokenIdFrom", + type: "uint256", + }, + { + internalType: "uint256", + name: "_timeShared", + type: "uint256", + }, + ], + name: "shareKey", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes4", + name: "interfaceId", + type: "bytes4", + }, + ], + name: "supportsInterface", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "symbol", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "tokenAddress", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_index", + type: "uint256", + }, + ], + name: "tokenByIndex", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_keyOwner", + type: "address", + }, + { + internalType: "uint256", + name: "_index", + type: "uint256", + }, + ], + name: "tokenOfOwnerByIndex", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "tokenURI", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_keyOwner", + type: "address", + }, + ], + name: "totalKeys", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalSupply", + outputs: [ + { + internalType: "uint256", + name: "_totalKeysCreated", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "transferFeeBasisPoints", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_from", + type: "address", + }, + { + internalType: "address", + name: "_recipient", + type: "address", + }, + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "transferFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_recipient", + type: "address", + }, + { + internalType: "uint256", + name: "_tokenId", + type: "uint256", + }, + ], + name: "unlendKey", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "unlockProtocol", + outputs: [ + { + internalType: "contract IUnlock", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_keyPrice", + type: "uint256", + }, + { + internalType: "address", + name: "_tokenAddress", + type: "address", + }, + ], + name: "updateKeyPricing", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_newExpirationDuration", + type: "uint256", + }, + { + internalType: "uint256", + name: "_maxNumberOfKeys", + type: "uint256", + }, + { + internalType: "uint256", + name: "_maxKeysPerAcccount", + type: "uint256", + }, + ], + name: "updateLockConfig", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_freeTrialLength", + type: "uint256", + }, + { + internalType: "uint256", + name: "_refundPenaltyBasisPoints", + type: "uint256", + }, + ], + name: "updateRefundPenalty", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_transferFeeBasisPoints", + type: "uint256", + }, + ], + name: "updateTransferFee", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_tokenAddress", + type: "address", + }, + { + internalType: "address payable", + name: "_recipient", + type: "address", + }, + { + internalType: "uint256", + name: "_amount", + type: "uint256", + }, + ], + name: "withdraw", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + stateMutability: "payable", + type: "receive", + }, +]; diff --git a/app/frame/config.ts b/app/frame/config.ts new file mode 100644 index 0000000..05c0670 --- /dev/null +++ b/app/frame/config.ts @@ -0,0 +1,17 @@ +import dotenv from "dotenv"; +import z from "zod"; + +dotenv.config(); + +const configSchema = z.object({ + REDIS_URL: z.string(), + REDIS_TOKEN: z.string(), + DC_API_KEY: z.string(), + QSTASH_CURRENT_SIGNING_KEY: z.string(), + QSTASH_NEXT_SIGNING_KEY: z.string(), + PROD_URL: z.string().default("https://unlock-protocol-calendar.vercel.app"), +}); + +const config = configSchema.parse(process.env); + +export default config; diff --git a/app/frame/constants.ts b/app/frame/constants.ts new file mode 100644 index 0000000..6c9f52d --- /dev/null +++ b/app/frame/constants.ts @@ -0,0 +1,35 @@ +import { Address } from "viem"; + +export const HOOK_CONTRACT_ADDRESS = + "0x4266b16A605cF3360a074f9FfE24319C5Df636e8" as Address; + +export const DAYS_CONTRACT_ADDRESSES = [ + "0x8c807122859DAE7cf07dE09dC1aEBD997624a88b", + "0x1755b71F67bCe674C4Dbc8FC2F843135907A3C3F", + "0x4e123Cf06c56dfED85F4AeD22fAd56F2c6B930aa", + "0xA95Ad9567C52B56402fFDcc1607c85eB6034F621", + "0x2A7dE87001B6c22db28c70Be768A24F9dED69fe0", + "0xe1F9712a89aA8f231EA90AFfbC23c15B7774ccB5", + "0x634b60467e8c6c3801B6B24118572012aFAD1277", + "0xfF85A79f83A83541B95305EfC51e667798fEFC17", + "0x40735C2a9d2409A7b884Eb968c1BcA4AAB86894b", + "0x86d4B95Aa8f3254Cef72f99e9733CeA4648876e8", + "0xBBF737297ef1eB98D4224c36465b58F931C2a1B7", + "0xda3a1eB1BfCf1F8E210Fa95a87635e413061bABd", + "0x913DdA625233020c2C75834612b2dB632D838509", + "0x3065d6F2294Bd70D7baAA9335D6Ae46835070A21", + "0xb9E12e63Fe11716c9F4EEb5eB326dCDeeDC6f034", + "0xAdeb8fD2f2Ea28B75c95e516EcFa46A63Cb85024", + "0xe2d98B745A176e496DDA8A0aDa77e4DF849Bf616", + "0xcd48665b2b0b3C5AB7BAEC227A9c258aD146102a", + "0xF40d79EcD0CfFf65A9165Ef5B7e04B0beAe0f684", + "0x449EF538Ebc0e375cEda19b550DF2985E1f68dFB", + "0xb39AF229d6095863afddC2279cF1588f3FE033Ff", + "0x3e40bDc5468Ab233b75700b1762930340bb6a836", + "0xD7ac9645beb87421263E8267b50DD96D66CDb037", + "0x17083A10ABc7de9e91313a8EC445f92371C424A8", +] as Address[]; + +export const MAIN_SITE_URL = "https://advent.unlock-protocol.com"; + +export const UNLOCK_REDIS_KEY = `unlock-advent-calendar-2024`; diff --git a/app/frame/services.ts b/app/frame/services.ts new file mode 100644 index 0000000..8e62054 --- /dev/null +++ b/app/frame/services.ts @@ -0,0 +1,18 @@ +import { Receiver } from "@upstash/qstash"; +import { Redis } from "@upstash/redis"; +import uniFarcasterSdk from "uni-farcaster-sdk"; +import config from "./config"; + +export const kvStore = new Redis({ + url: config.REDIS_URL, + token: config.REDIS_TOKEN, +}); + +export const qstashReceiver = new Receiver({ + currentSigningKey: config.QSTASH_CURRENT_SIGNING_KEY, + nextSigningKey: config.QSTASH_NEXT_SIGNING_KEY, +}); + +export const sdkInstance = new uniFarcasterSdk({ + neynarApiKey: "NEYNAR_FROG_FM", +}); diff --git a/app/frame/utils.ts b/app/frame/utils.ts new file mode 100644 index 0000000..56e2769 --- /dev/null +++ b/app/frame/utils.ts @@ -0,0 +1,124 @@ +import { Address, createPublicClient, http } from "viem"; +import { base } from "viem/chains"; +import { hookAbi, PublicLockAbi, unlockAbi } from "./abi"; +import config from "./config"; +import { + DAYS_CONTRACT_ADDRESSES, + MAIN_SITE_URL, + UNLOCK_REDIS_KEY, +} from "./constants"; +import { kvStore } from "./services"; + +export function getCurrentDateUTC() { + return new Date().getUTCDate(); +} + +const publicClient = createPublicClient({ + chain: base, + transport: http(), +}); + +export function getUserNftBalance( + userAddress: Address, + dayContractAddress: Address +) { + return publicClient.readContract({ + abi: unlockAbi, + functionName: "balanceOf", + args: [userAddress], + address: dayContractAddress, + }); +} + +export const getValidKeysForUser = async ( + userAddress: Address, + currentDay: number +) => { + const validKeysContracts = DAYS_CONTRACT_ADDRESSES.slice(0, currentDay).map( + (lockAddress) => ({ + address: lockAddress, + abi: PublicLockAbi, + functionName: "getHasValidKey", + args: [userAddress], + }) + ); + + try { + const results = await publicClient.multicall({ + //@ts-expect-error: ts not sure if validKeysContracts fits the contracts type + contracts: validKeysContracts, + }); + + return results.map((result) => + result.status === "success" ? (result.result as boolean) : false + ); + } catch (error) { + console.error("Multicall error:", error); + return new Array(DAYS_CONTRACT_ADDRESSES.length).fill(false); + } +}; + +export const getLockAddresses = async (hookContractAddress: Address) => { + const days = Array.from({ length: 24 }, (_, i) => i + 1); + + const lockAddresses: Address[] = []; + + for (const day of days) { + try { + const lockAddress = await publicClient.readContract({ + address: hookContractAddress, + abi: hookAbi, + functionName: "lockByDay", + args: [day], + }); + + lockAddresses.push(lockAddress as Address); + } catch (error) { + console.error(`Error fetching lock for day ${day}:`, error); + } + } + + return lockAddresses; +}; + +export function getDayImage(day: number) { + return `${MAIN_SITE_URL}/images/nft/${day}.png`; +} + +export async function generateIdempotencyKey(input: string) { + const encoder = new TextEncoder(); + const data = encoder.encode(input); + + return crypto.subtle.digest("SHA-256", data).then((buffer) => { + const hashArray = Array.from(new Uint8Array(buffer)); + const hashHex = hashArray + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + return hashHex.substring(0, 16); + }); +} + +export async function sentDcToUser(fid: number, message: string) { + const idempotencyKey = await generateIdempotencyKey(message); + try { + await fetch("https://api.warpcast.com/v2/ext-send-direct-cast", { + method: "PUT", + headers: { + Authorization: `Bearer ${config.DC_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + recipientFid: fid, + message: message, + idempotencyKey, + }), + }); + } catch (error) { + console.error("Error sending direct cast:", error); + throw error; + } +} + +export async function registerUserForNotifications(fid: number) { + await kvStore.sadd(UNLOCK_REDIS_KEY, fid); +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..ed5f9dc Binary files /dev/null and b/bun.lockb differ diff --git a/package-lock.json b/package-lock.json index 75a4327..dc65bd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14817,6 +14817,21 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } } } } diff --git a/package.json b/package.json index 18336e0..5b6c3ee 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,18 @@ "lint": "next lint" }, "dependencies": { + "frog": "latest", + "hono": "^4", + "react": "^18", + "react-dom": "^18", + "next": "14.1.0", + "@upstash/qstash": "^2.7.17", + "@upstash/redis": "^1.34.3", + "@vercel/functions": "^1.5.1", + "dotenv": "^16.4.7", + "uni-farcaster-sdk": "^0.0.25", + "viem": "^2.21.53", + "zod": "^3.23.8", "@privy-io/react-auth": "^1.95.3", "@privy-io/wagmi": "^0.2.12", "@radix-ui/colors": "^3.0.0", @@ -16,9 +28,6 @@ "@solana/web3.js": "^1.95.8", "@tailwindcss/aspect-ratio": "^0.4.2", "@tanstack/react-query": "^5.62.2", - "@types/node": "^22.10.1", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", "@types/react-google-recaptcha": "^2.1.8", "@unlock-protocol/contracts": "^0.0.32", "@unlock-protocol/networks": "0.0.24", @@ -28,11 +37,8 @@ "axios": "1.7.7", "eslint": "8.54.0", "eslint-config-next": "14.0.3", - "next": "15.0.3", "next-seo": "^6.4.0", "postcss": "^8.4.19", - "react": "18.3.1", - "react-dom": "18.3.1", "react-ga4": "^2.1.0", "react-google-recaptcha": "^3.1.0", "react-hot-toast": "^2.4.1", @@ -41,8 +47,14 @@ "react-snowfall": "^2.2.0", "react-use-clipboard": "^1.0.9", "tailwindcss": "3.4.16", - "typescript": "5.7.2", "use-local-storage-state": "^19.1.0", "wagmi": "^2.13.3" + }, + "devDependencies": { + "typescript": "^5", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "vercel": "^32.4.1" } } diff --git a/pages/index.tsx b/pages/index.tsx index 149650d..75d88b4 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -2,13 +2,11 @@ import { Analytics } from "@vercel/analytics/react"; import Snowfall from "react-snowfall"; import Head from "next/head"; import Header from "../components/Header"; -import { useSearchParams } from "next/navigation"; import Link from "next/link"; import { Calendar } from "../components/Calendar"; import { AppConfig } from "../lib/AppConfig"; import { GetTokens } from "../components/GetTokens"; import { Hurricane } from "next/font/google"; -import Image from "next/image"; import Footer from "../components/Footer"; const hurricane = Hurricane({ @@ -17,14 +15,54 @@ const hurricane = Hurricane({ display: "swap", }); export default function Home() { - const start = 1; - const days = new Array(24).fill(0).map((d, i) => i + start); - const searchParams = useSearchParams(); - return ( <> 2024 Unlock Advent Calendar + {/* ----- FRAME START ------ */} + +{/* Using App directory you don't need to manually create the meta tags +./app/page.tsx + +import { getFrameMetadata } from 'frog/web' + +export async function generateMetadata(): Promise { + const frameTags = await getFrameMetadata( + `${process.env.VERCEL_URL || 'http://localhost:3000'}/frame`, + ) + return { + other: frameTags, + } +} + + */} + + + + + + + + + + + + + {/* ----- FRAME END ------ */} + - +
@@ -93,24 +131,3 @@ export default function Home() { ); } - -function FcFrame() { - return ( - <> - - - ); -} diff --git a/tsconfig.json b/tsconfig.json index 9c422e8..1016077 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,13 +22,19 @@ "react-dom", "node" ], - "incremental": true + "incremental": true, + "plugins": [ + { + "name": "next" + } + ] }, "include": [ - "next-env.d.ts", "**/*.ts", "**/*.tsx", - "layout/daySizes.js" + "layout/daySizes.js", + "next-env.d.ts", + ".next/types/**/*.ts" ], "exclude": [ "node_modules",