-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 03dcd10
Showing
19 changed files
with
1,171 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
NEYNAR_API_KEY= | ||
THIRDWEB_ENGINE_URL= | ||
THIRDWEB_ENGINE_WALLET= | ||
THIRDWEB_ACCESS_TOKEN= | ||
NEXT_PUBLIC_SMART_CONTRACT= | ||
NEXT_PUBLIC_VERCEL_URL= | ||
NEXT_PUBLIC_CHAIN_ID= | ||
NODE_NEV= | ||
NEXT_PUBLIC_WARPCAST_CAST_HASH= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
.yarn/install-state.gz | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# local env files | ||
.env*.local | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts | ||
|
||
# Sentry Config File | ||
.sentryclirc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// next.config.js | ||
/** @type {import('next').NextConfig} */ | ||
const nextConfig = { | ||
async redirects() { | ||
return [ | ||
{ | ||
source: "/", | ||
destination: "/mint", | ||
permanent: true, | ||
}, | ||
]; | ||
}, | ||
reactStrictMode: true, | ||
}; | ||
|
||
module.exports = nextConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "thirdweb-engine-farcaster-frame", | ||
"version": "0.1.0", | ||
"private": true, | ||
"scripts": { | ||
"dev": "next dev", | ||
"build": "next build", | ||
"start": "next start", | ||
"lint": "next lint" | ||
}, | ||
"dependencies": { | ||
"bignumber.js": "^9.1.2", | ||
"ethers": "^5.4.6", | ||
"next": "14.1.0", | ||
"nextjs-cors": "^2.2.0", | ||
"react": "^18", | ||
"react-dom": "^18", | ||
"zod": "^3.22.4" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^20", | ||
"@types/react": "^18", | ||
"@types/react-dom": "^18", | ||
"typescript": "^5" | ||
} | ||
} |
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { config } from "@/config/config"; | ||
import { z } from "zod"; | ||
|
||
const balanceResponseSchema = z.object({ | ||
result: z.object({ | ||
value: z.string(), | ||
}), | ||
}); | ||
|
||
const mintResponseSchema = z.object({ | ||
result: z.object({ | ||
queueId: z.string(), | ||
}), | ||
}); | ||
|
||
export const httpFetchBalanceStatus = async () => { | ||
const response = await fetch( | ||
`${config.thirdweb.engine.url}/backend-wallet/${config.thirdweb.chainId}/${config.thirdweb.engine.wallet}/get-balance`, | ||
{ | ||
method: "GET", | ||
headers: { | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${config.thirdweb.engine.accessToken}`, | ||
"x-backend-wallet-address": config.thirdweb.engine.wallet!, | ||
}, | ||
} | ||
); | ||
|
||
const result = await response.json(); | ||
|
||
return balanceResponseSchema.parse(result); | ||
}; | ||
|
||
export const httpMint = async (receiver: string) => { | ||
const response = await fetch( | ||
`${config.thirdweb.engine.url}/contract/${config.thirdweb.chainId}/${config.contractAddress}/erc721/claim-to`, | ||
{ | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${config.thirdweb.engine.accessToken}`, | ||
"x-backend-wallet-address": config.thirdweb.engine.wallet!, | ||
}, | ||
body: JSON.stringify({ receiver: receiver.toLowerCase(), quantity: "1" }), | ||
} | ||
); | ||
|
||
const result = await response.json(); | ||
|
||
return mintResponseSchema.parse(result); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { ethers } from "ethers"; | ||
import { BigNumber } from "bignumber.js"; | ||
import { httpFetchBalanceStatus, httpMint } from "@/api/fetchers"; | ||
|
||
export class ThirdWebEngine { | ||
public static isBalanceLow = async () => { | ||
const { result } = await httpFetchBalanceStatus(); | ||
const formattedEther = ethers.utils.formatEther(result.value); | ||
const totalFormattedBalance = BigNumber(formattedEther).dp(4).toNumber(); | ||
return totalFormattedBalance < 0.01; | ||
}; | ||
|
||
public static mint = async (receiver: string) => { | ||
const response = await httpMint(receiver); | ||
return response; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { config } from "@/config/config"; | ||
import { z } from "zod"; | ||
|
||
// Neynar's api | ||
const apiUrl = "https://api.neynar.com"; | ||
|
||
const recastsSchema = z.array( | ||
z.object({ | ||
fid: z.number(), | ||
fname: z.string().min(1), | ||
}) | ||
); | ||
|
||
const validateMessageSchema = z.object({ | ||
valid: z.literal(true), | ||
action: z.object({ | ||
interactor: z.object({ | ||
fid: z.number(), | ||
username: z.string(), | ||
custody_address: z.string().startsWith("0x"), | ||
}), | ||
tapped_button: z.object({ | ||
index: z.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)]), | ||
}), | ||
}), | ||
}); | ||
|
||
export class Warpcast { | ||
private static get computeDefaultHeader() { | ||
return { | ||
api_key: config.neynar.apiKey as string, | ||
"content-type": "application/json", | ||
}; | ||
} | ||
|
||
private static async fetchAllRecasts() { | ||
const url = `${apiUrl}/v2/farcaster/cast?identifier=${config.warpcast.castHas}&type=hash`; | ||
|
||
const response = await fetch(url, { | ||
headers: Warpcast.computeDefaultHeader, | ||
method: "GET", | ||
}); | ||
|
||
const data = await response | ||
.json() | ||
.then((res) => res.cast.reactions.recasts) | ||
.then(recastsSchema.parse); | ||
|
||
return data; | ||
} | ||
|
||
public static async hasRecasted(fid: number) { | ||
const recasts = await Warpcast.fetchAllRecasts(); | ||
return recasts.some((recast) => recast.fid === fid); | ||
} | ||
|
||
public static async validateMessage(messageBytes: string) { | ||
const url = `${apiUrl}/v2/farcaster/frame/validate`; | ||
|
||
const response = await fetch(url, { | ||
headers: Warpcast.computeDefaultHeader, | ||
method: "POST", | ||
body: JSON.stringify({ | ||
message_bytes_in_hex: messageBytes, | ||
}), | ||
}); | ||
|
||
const data = await response | ||
.json() | ||
.then((res) => res) | ||
.then(validateMessageSchema.parse); | ||
|
||
return data.action; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
export const config = { | ||
neynar: { | ||
apiKey: process.env.NEYNAR_API_KEY, | ||
}, | ||
contractAddress: process.env.NEXT_PUBLIC_SMART_CONTRACT, | ||
warpcast: { | ||
castHas: process.env.NEXT_PUBLIC_WARPCAST_CAST_HASH, | ||
}, | ||
thirdweb: { | ||
chainId: Number(process.env.NEXT_PUBLIC_CHAIN_ID), | ||
engine: { | ||
url: process.env.THIRDWEB_ENGINE_URL, | ||
wallet: process.env.THIRDWEB_ENGINE_WALLET, | ||
accessToken: process.env.THIRDWEB_ACCESS_TOKEN, | ||
}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import "@/styles/globals.css"; | ||
import type { AppProps } from "next/app"; | ||
|
||
export default function App({ Component, pageProps }: AppProps) { | ||
return <Component {...pageProps} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Html, Head, Main, NextScript } from "next/document"; | ||
|
||
export default function Document() { | ||
return ( | ||
<Html lang="en"> | ||
<Head> | ||
<meta property="og:title" content={`Thirdweb Frame`} /> | ||
<meta property="og:image" content={`<next_js_image_path>`} /> | ||
<meta property="fc:frame" content="vNext" /> | ||
<meta property="fc:frame:image" content={`<next_js_image_path>`} /> | ||
<meta property="fc:frame:post_url" content={`<next_js_image_path>`} /> | ||
<meta property="fc:frame:button:1" content="Get started" /> | ||
</Head> | ||
<body> | ||
<Main /> | ||
<NextScript /> | ||
</body> | ||
</Html> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { computeHtml } from "@/utils/compute-html"; | ||
import { Warpcast } from "../../classes/Warpcast"; | ||
import { NextApiRequest, NextApiResponse } from "next"; | ||
import NextCors from "nextjs-cors"; | ||
import { z } from "zod"; | ||
import { ThirdWebEngine } from "@/classes/ThirdWebEngine"; | ||
|
||
const requestBodyWarpcastSchema = z.object({ | ||
trustedData: z.object({ | ||
messageBytes: z.string().min(5), | ||
}), | ||
}); | ||
|
||
const requestQuerySchema = z.object({ | ||
type: z.union([z.literal("start"), z.literal("recast"), z.literal("mint")]), | ||
}); | ||
|
||
export default async function handler( | ||
req: NextApiRequest, | ||
res: NextApiResponse | ||
) { | ||
await NextCors(req, res, { | ||
methods: ["GET", "POST"], | ||
origin: "*", | ||
}); | ||
|
||
if (req.method !== "POST") { | ||
return res.status(400).send({ error: "invalid method" }); | ||
} | ||
|
||
try { | ||
const { type } = requestQuerySchema.parse(req.query); | ||
|
||
const { trustedData } = requestBodyWarpcastSchema.parse(req.body); | ||
|
||
const action = await Warpcast.validateMessage(trustedData.messageBytes); | ||
|
||
if (type === "start") { | ||
const isBalanceLow = await ThirdWebEngine.isBalanceLow(); | ||
|
||
if (isBalanceLow) { | ||
return res.status(200).send( | ||
computeHtml({ | ||
imagePath: "<next_js_image_path>", | ||
postType: "follow", | ||
content: "Sorry we went out of gas :(", | ||
}) | ||
); | ||
} | ||
|
||
return res.status(200).send( | ||
computeHtml({ | ||
imagePath: "<next_js_image_path>", | ||
postType: "recast", | ||
content: "Re-cast to mint", | ||
}) | ||
); | ||
} | ||
|
||
if (type === "recast") { | ||
const hasRecasted = await Warpcast.hasRecasted(action.interactor.fid); | ||
|
||
if (!hasRecasted) { | ||
return res.status(200).send( | ||
computeHtml({ | ||
imagePath: "<next_js_image_path>", | ||
postType: "recast", | ||
content: "Re-cast is required to mint the NFT", | ||
}) | ||
); | ||
} | ||
|
||
return res.status(200).send( | ||
computeHtml({ | ||
imagePath: "<next_js_image_path>", | ||
postType: "mint", | ||
content: "Mint NFT", | ||
}) | ||
); | ||
} | ||
|
||
if (type === "mint") { | ||
await ThirdWebEngine.mint(action.interactor.custody_address); | ||
|
||
return res.status(200).send( | ||
computeHtml({ | ||
imagePath: "<next_js_image_path>", | ||
postType: "start", // Do your own custom post_url after user has minted the NFT + clicks your button | ||
content: "Congrats! You minted the NFT", | ||
}) | ||
); | ||
} | ||
} catch (err) { | ||
return res.status(200).send( | ||
computeHtml({ | ||
imagePath: "<next_js_image_path>", | ||
postType: "start", | ||
content: "Something went wrong", | ||
}) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function FramesMint() { | ||
return <h1>{`Thirdweb Engine Farcaster Frame :)`}</h1>; | ||
} |
Oops, something went wrong.