From 30e906bb14806ab43192a4c13606aa7e7cb4a5c4 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 6 Feb 2025 12:07:00 +0530 Subject: [PATCH] Create lock on every fourth twitter post --- src/app/api/lock/route.ts | 47 ++------------------------------------ src/app/api/tweet/route.ts | 17 +++++++++----- src/utils/create-lock.ts | 42 +++++++++++++++++++++++++++++++++- src/utils/verifyPayment.ts | 3 +-- 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/app/api/lock/route.ts b/src/app/api/lock/route.ts index 65882bf..ce4dc3c 100644 --- a/src/app/api/lock/route.ts +++ b/src/app/api/lock/route.ts @@ -1,52 +1,9 @@ import { NextRequest, NextResponse } from "next/server"; -import { Connection, Keypair, PublicKey } from "@solana/web3.js"; -import { createLock } from "../../../utils/create-lock"; -import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; -import { BN } from "bn.js"; -import Big from 'big.js'; +import { Connection} from "@solana/web3.js"; +import { createRewardLock, extractInfo } from "../../../utils/create-lock"; const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL!); -async function extractInfo(transactionSignature: string) { - const transaction = await connection.getParsedTransaction(transactionSignature, 'confirmed'); - if (!transaction) { - throw new Error('Transaction not found'); - } - - const transferInstruction = transaction.transaction.message.instructions.find( - (instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID) - ); - - if (!transferInstruction || !('parsed' in transferInstruction)) { - throw new Error('Transfer instruction not found'); - } - - const { info: { amount, authority } } = transferInstruction.parsed; - return { authority, amount }; -} - -async function createRewardLock(authority: string, amount: string) { - const { WSOL_LOCKER_PRIVATE_KEY, CLIFF_TIME, WSOL_MINT, NEXT_PUBLIC_MTM_TOKEN_MINT, REWARD_MULTIPLIER } = process.env; - if (!WSOL_LOCKER_PRIVATE_KEY || !CLIFF_TIME || !WSOL_MINT || !NEXT_PUBLIC_MTM_TOKEN_MINT || !REWARD_MULTIPLIER) { - throw new Error('Missing required environment variables'); - } - - const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); - const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_PRIVATE_KEY)); - const recipientPublicKey = new PublicKey(authority); - - const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_TOKEN_MINT}&vsToken=${WSOL_MINT}`; - const response = await fetch(url); - const { data } = await response.json(); - - const priceWSOLFor1MTM = new Big(data[NEXT_PUBLIC_MTM_TOKEN_MINT].price).toFixed(9); - const mtmAmount = new Big(amount).div(new Big(10).pow(6)); - const wsolAmount = new BN(new Big(mtmAmount).times(priceWSOLFor1MTM).times(new Big(10).pow(9)).times(REWARD_MULTIPLIER).toFixed(0)); - - return createLock(tokenLockerKeypair, recipientPublicKey, duration, wsolAmount); -} - export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url); diff --git a/src/app/api/tweet/route.ts b/src/app/api/tweet/route.ts index f514ff9..5c661bc 100644 --- a/src/app/api/tweet/route.ts +++ b/src/app/api/tweet/route.ts @@ -1,6 +1,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { saveTweet, verifySignatureInTweet } from '../../../utils/verifyTweet'; +import { createRewardLock, extractInfo } from '../../../utils/create-lock'; export async function POST(req: NextRequest): Promise { try { @@ -30,8 +31,17 @@ export async function POST(req: NextRequest): Promise { } const { isFourthUser } = await saveTweet({ transactionSignature: txSignature, url: memeUrl }); + if (isFourthUser) { - createTokenLockForRecipient(); + const { authority, amount } = await extractInfo(txSignature); + + if (!authority || Number(amount) <= 0) { + return NextResponse.json({ error: "Invalid transaction details" }, { status: 400 }); + } + + const escrow = await createRewardLock(authority, amount); + return NextResponse.json({ success: true, data: { escrow } }); + } return NextResponse.json({ success: isVerified, message: 'Tweet verified' }) @@ -59,8 +69,3 @@ const extractData = (tweet: string | object) => { handle: handleMatch ? handleMatch[1] : null, }; }; - -// TODO: Implement function to create lock for a recipient -const createTokenLockForRecipient = () => { - console.log('Lock created'); -} diff --git a/src/utils/create-lock.ts b/src/utils/create-lock.ts index da5ac80..4ada5e9 100644 --- a/src/utils/create-lock.ts +++ b/src/utils/create-lock.ts @@ -2,12 +2,13 @@ import assert from 'assert'; import BN from 'bn.js'; import 'dotenv/config'; import bs58 from 'bs58'; +import Big from 'big.js'; import * as anchor from "@coral-xyz/anchor"; import { TOKEN_PROGRAM_ID, } from "@solana/spl-token"; -import { Connection, PublicKey } from "@solana/web3.js"; +import { Connection, Keypair, PublicKey } from "@solana/web3.js"; import { createVestingPlanV2 } from '../locker-utils'; @@ -64,3 +65,42 @@ export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipi return escrow; } + +export async function extractInfo(transactionSignature: string) { + const transaction = await connection.getParsedTransaction(transactionSignature, 'confirmed'); + if (!transaction) { + throw new Error('Transaction not found'); + } + + const transferInstruction = transaction.transaction.message.instructions.find( + (instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID) + ); + + if (!transferInstruction || !('parsed' in transferInstruction)) { + throw new Error('Transfer instruction not found'); + } + + const { info: { amount, authority } } = transferInstruction.parsed; + return { authority, amount }; +} + +export async function createRewardLock(authority: string, amount: string) { + const { WSOL_LOCKER_PRIVATE_KEY, CLIFF_TIME, WSOL_MINT, NEXT_PUBLIC_MTM_TOKEN_MINT, REWARD_MULTIPLIER } = process.env; + if (!WSOL_LOCKER_PRIVATE_KEY || !CLIFF_TIME || !WSOL_MINT || !NEXT_PUBLIC_MTM_TOKEN_MINT || !REWARD_MULTIPLIER) { + throw new Error('Missing required environment variables'); + } + + const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); + const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_PRIVATE_KEY)); + const recipientPublicKey = new PublicKey(authority); + + const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_TOKEN_MINT}&vsToken=${WSOL_MINT}`; + const response = await fetch(url); + const { data } = await response.json(); + + const priceWSOLFor1MTM = new Big(data[NEXT_PUBLIC_MTM_TOKEN_MINT].price).toFixed(9); + const mtmAmount = new Big(amount).div(new Big(10).pow(6)); + const wsolAmount = new BN(new Big(mtmAmount).times(priceWSOLFor1MTM).times(new Big(10).pow(9)).times(REWARD_MULTIPLIER).toFixed(0)); + + return createLock(tokenLockerKeypair, recipientPublicKey, duration, wsolAmount); +} diff --git a/src/utils/verifyPayment.ts b/src/utils/verifyPayment.ts index 92986d8..63dc2e2 100644 --- a/src/utils/verifyPayment.ts +++ b/src/utils/verifyPayment.ts @@ -4,8 +4,6 @@ import BN from 'bn.js'; import { Connection } from '@solana/web3.js'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; -import { Payment } from '../entity/Payment'; - assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required'); const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL; @@ -44,6 +42,7 @@ export async function markSignatureAsUsed(transactionSignature: string): Promise }); } +// TODO: Verify that payment receiver is correct export async function verifyPayment( transactionSignature: string, tokenAmount: BN,