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 && (
+

+ )}
+
+
{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",