Reach me at @undefined on Warpcast if you have any questions!
Install Bun
curl -fsSL https://bun.sh/install | bash
# or
brew install oven-sh/bun/bun
bun install
# set up .env with .env.example
bun run dev
Head to http://localhost:8880/frames
-
Launch an ERC-20 with automated price curve through frames. example
-
Launch an ERC-1155 with automated price curve through frames. example
-
Launch a community ERC-1155 token with base asset predefined through frames. example
Built with a mix of Pinata & Frog & Frames.js πΈπΌοΈ
I believe each framework has its own strengths, and I wanted to leverage the best of all the worlds.
- π More abstraction. (double edged sword though)
- π Bun support.
- π Community middlewares.
- π₯² Lack of
getFrameHtml
andXMTP
support & utility functions.
- π Super powerful debugger. Always debugged with frames.js.
- π Helpful utility functions like
getFrameHtml
&getFrameFlattened
, which is useful for handling unexpected errors. - π Supports XMTP!
getXmtpFrameMessage
We use
getFrame
andgetFrameFlattened
on our separate front-end code, which is a huge life saver.
const other: Metadata['other'] = {
'fc:frame': 'vNext',
'fc:frame:image': ogUrl,
...farcasterMetadata,
};
const url = `https://mint.club/frames/buysell/preview/${contractType}-${chain}-${symbolOrAddress}`;
const htmlString = await ky(url)
.text()
.catch((e) => {
Logger.error(e);
return null;
});
if (htmlString) {
const { frame } = getFrame({
htmlString: htmlString,
url,
});
if (frame) {
const flattened = getFrameFlattened(frame);
Object.assign(other, flattened);
}
}
Also for handling unexpected errors
app.hono.onError((error, c) => {
console.error(error);
const html = getFrameHtml({
version: 'vNext',
postUrl: `${origin}/frames`,
image: `${origin}/frames/assets/error.png`,
imageAspectRatio: '1:1',
buttons: [
{
action: 'post',
label: 'Go back',
},
],
});
return c.html(html);
});
- π₯² I wish it was a little more abstracted for common functions like validating all frame messages with a flag.
I tried implementing XMTP in frog, but it seems like it's a bigger obstacle than I thought :c
- π I would have never thought of "Frame Analytics" if it wasn't for Pinata.
app.use(
'/erc1155',
fdk.analyticsMiddleware({
frameId: 'erc1155',
}),
);
app.use(
'/erc20',
fdk.analyticsMiddleware({
frameId: 'erc20',
}),
);
- π All the data (bonding curve data, image, metadata) lives on-chain.
- π₯² Wish it had middleware for frog like neynar.
// PinataService.ts
import fetch from 'node-fetch';
import { PINATA_API_JWT } from './server-env';
export async function uploadToPinata(blob: Blob) {
const data = new FormData();
data.append('file', blob);
const res = await fetch('https://api.pinata.cloud/pinning/pinFileToIPFS', {
method: 'POST',
headers: {
Authorization: `Bearer ${PINATA_API_JWT}`,
},
body: data,
});
const { IpfsHash } = (await res.json()) as {
IpfsHash: string;
};
return `ipfs://${IpfsHash}`;
}
export async function uploadMetadataToPinata(
imageHash: `ipfs://${string}`,
name: string,
) {
const defaultExternalLink = `https://mint.club`;
const defaultDescription = [
`A Bonding Curved ERC-1155 token powered by mint.club bonding curve protocol.`,
defaultExternalLink,
].join('\n\n');
const finalMetadata = {
name,
description: defaultDescription,
image: imageHash,
external_url: defaultExternalLink,
attributes: [],
};
const metadata = JSON.stringify(finalMetadata);
const metadataBuffer = new File([metadata], 'metadata.json', {
type: 'application/json',
});
return uploadToPinata(metadataBuffer);
}