Skip to content

Commit

Permalink
Merge pull request #4 from DappyKit/feat/auto-export-clickcaster
Browse files Browse the repository at this point in the history
feat: export frames to clickcaster
  • Loading branch information
IgorShadurin authored Jun 8, 2024
2 parents 96bae4b + 2c7878c commit 8b4927e
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 6 deletions.
3 changes: 3 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ ENV_TYPE=development

# Base URL for the application
PUBLIC_URL=

# Clickcaster export URL
CLICKCASTER_EXPORT_URL=https://clickcaster.xyz/v1/provider/add-frame
10 changes: 7 additions & 3 deletions frame/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ app.frame('/register-app', async c => {
updateSession(currentSession, { ethAddress: inputText || '', ethAddressBytes: messageBytes })
} else if (previousScreen === SCREEN_SAVE_FRAME) {
nextScreen = SCREEN_SAVE_CALLBACK
intents.unshift(<TextInput placeholder="Enter Callback URL" />)
intents.unshift(<TextInput placeholder="Enter Webhook URL" />)
updateSession(currentSession, { frameUrl: inputText || '', frameUrlBytes: messageBytes })
} else if (previousScreen === SCREEN_SAVE_CALLBACK) {
updateSession(currentSession, { callbackUrl: inputText || '', callbackUrlBytes: messageBytes })
Expand All @@ -322,7 +322,11 @@ app.frame('/register-app', async c => {
validateUrl(sessionData.callbackUrl)

const { ethAddressBytes, frameUrlBytes, callbackUrlBytes } = sessionData
await createApp(ethAddressBytes, frameUrlBytes, callbackUrlBytes)
const createResponse = await createApp(ethAddressBytes, frameUrlBytes, callbackUrlBytes)

if (createResponse.status !== 'ok') {
throw new Error(`Failed to create an app: ${JSON.stringify(createResponse)}`)
}
sessions.delete(currentSession)

intents = [
Expand Down Expand Up @@ -354,7 +358,7 @@ app.frame('/register-app', async c => {
<Text align="center" size="18">
{previousScreen === SCREEN_START && 'Enter your Ethereum address'}
{previousScreen === SCREEN_SAVE_ETH && 'Enter your Frame URL'}
{previousScreen === SCREEN_SAVE_FRAME && 'Enter your Callback URL'}
{previousScreen === SCREEN_SAVE_FRAME && 'Enter your Webhook URL'}
{previousScreen === SCREEN_SAVE_CALLBACK && 'Your application registered successfully!'}
</Text>
</VStack>
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"lint:check": "eslint \"src/**/*.ts\" \"test/**/*.ts\" && prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
"check:types": "tsc --project tsconfig.test.json",
"check-all": "npm run lint:check && npm run check:types && npm run test",
"test-request": "ts-node src/scripts/create-auth-request.ts"
"test-request": "ts-node src/scripts/create-auth-request.ts",
"export-frames-clickcaster": "ts-node src/scripts/export-frames-clickcaster.ts"
},
"author": "DappyKit",
"license": "MIT",
Expand Down
11 changes: 11 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export interface IConfigData {
* IPFS URL
*/
ipfsUrl: string

/**
* Clickcaster export URL
*/
clickcasterExportUrl: string
}

/**
Expand All @@ -45,6 +50,7 @@ let configData: IConfigData = {
signer: '',
pinataJWT: '',
ipfsUrl: '',
clickcasterExportUrl: '',
}

/**
Expand Down Expand Up @@ -75,12 +81,17 @@ export function loadConfig(): void {
throw new Error('IPFS_URL env variable not set')
}

if (!process.env.CLICKCASTER_EXPORT_URL) {
throw new Error('CLICKCASTER_EXPORT_URL env variable not set')
}

configData.neynarApiKey = process.env.NEYNAR_API_KEY
configData.publicUrl = process.env.PUBLIC_URL
configData.authorizedFrameUrl = process.env.AUTHORIZED_FRAME_URL
configData.signer = process.env.SIGNER
configData.pinataJWT = process.env.PINATA_JWT
configData.ipfsUrl = process.env.IPFS_URL
configData.clickcasterExportUrl = process.env.CLICKCASTER_EXPORT_URL
}

/**
Expand Down
11 changes: 9 additions & 2 deletions src/controllers/v1/app/create-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { getConfigData } from '../../../config'
import { ICreateResponse } from './interface/ICreateResponse'
import { getAppBySignerAddress, upsertApp } from '../../../db/app'
import { ICreateRequest } from './interface/ICreateRequest'
import { getAppCreateData } from './utils/app-create-utils'
import { exportFrameToClickcaster, getAppCreateData } from './utils/app-create-utils'
import { Wallet } from 'ethers'

/**
* Creates an app with messages from the trusted Frame.
Expand All @@ -17,7 +18,7 @@ export default async (
next: NextFunction,
): Promise<void> => {
try {
const { neynarApiKey, authorizedFrameUrl } = getConfigData()
const { neynarApiKey, authorizedFrameUrl, signer, clickcasterExportUrl } = getConfigData()
const { frameUrl, frameCallbackUrl, frameOwnerFid, signerAddress } = await getAppCreateData(
neynarApiKey,
authorizedFrameUrl,
Expand All @@ -36,6 +37,12 @@ export default async (
signer_address: signerAddress,
})

try {
await exportFrameToClickcaster(clickcasterExportUrl, frameOwnerFid, frameUrl, signerAddress, new Wallet(signer))
} catch (e) {
console.error('Error exporting frame to Clickcaster', e) // eslint-disable-line no-console
}

res.json({
status: 'ok',
})
Expand Down
27 changes: 27 additions & 0 deletions src/controllers/v1/app/utils/app-create-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { validateFrameUrl } from '../../../../utils/frame'
import { isWithinMaxMinutes } from '../../../../utils/time'
import { prepareEthAddress } from '../../../../utils/eth'
import { prepareUrl } from '../../../../utils/url'
import { ISigner } from '../../../../service/delegated-fs/interfaces'
import { postJsonData } from '../../../../utils/http'

export const MAX_REQUESTS_TIME_MINUTES = 10

Expand Down Expand Up @@ -121,3 +123,28 @@ export async function getAppCreateData(
signerAddress,
}
}

/**
* Exports the frame to Clickcaster.
* @param clickcasterExportUrl Clickcaster export URL
* @param fid Frame owner FID
* @param frameUrl Frame URL
* @param signerAddress Signer address
* @param signer Signer
*/
export async function exportFrameToClickcaster(
clickcasterExportUrl: string,
fid: number,
frameUrl: string,
signerAddress: string,
signer: ISigner,
): Promise<Response> {
const signature = signer.signMessage(`${fid.toString()}${frameUrl}${prepareEthAddress(signerAddress)}`)

return postJsonData(clickcasterExportUrl, {
fid,
frameUrl,
signerAddress,
signature,
})
}
7 changes: 7 additions & 0 deletions src/db/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ export interface IApp {
updated_at: string
}

/**
* Gets all apps.
*/
export async function getAllApps(): Promise<IApp[]> {
return db(TABLE_NAME).select('*')
}

export async function upsertApp(userData: Omit<IApp, 'created_at' | 'updated_at'>): Promise<void> {
const date = db.fn.now()
const newItem = { ...userData, updated_at: date }
Expand Down
27 changes: 27 additions & 0 deletions src/scripts/export-frames-clickcaster.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getConfigData, loadConfig } from '../config'
import { getAllApps } from '../db/app'
import { Wallet } from 'ethers'
import { exportFrameToClickcaster } from '../controllers/v1/app/utils/app-create-utils'

async function start() {
loadConfig()
const { signer, clickcasterExportUrl } = getConfigData()
const mainSigner = new Wallet(signer)

const apps = await getAllApps()
console.log('Found apps:', apps.length) // eslint-disable-line no-console
for (const [index, app] of apps.entries()) {
const response = await exportFrameToClickcaster(
clickcasterExportUrl,
app.fid,
app.frame_url,
app.signer_address,
mainSigner,
)
console.log(`[${(index + 1) / apps.length}] Exported frame response:`, response) // eslint-disable-line no-console
}

process.exit(0)
}

start().then()
15 changes: 15 additions & 0 deletions src/utils/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,18 @@ export async function callbackFrameUrl(url: string, data: ICallbackResult): Prom
}
}
}

/**
* Makes an HTTP POST request to the specified URL with the provided data.
* @param url The URL to which the POST request should be sent.
* @param data The data to be sent with the POST request. This data is converted into a JSON string for the request body.
*/
export async function postJsonData(url: string, data: unknown): Promise<Response> {
return fetch(url, {
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
})
}

0 comments on commit 8b4927e

Please sign in to comment.