import { NextRequest, NextResponse } from "next/server"; import { Connection, Keypair, ParsedInstruction, ParsedTransactionWithMeta, PartiallyDecodedInstruction, PublicKey } from "@solana/web3.js"; import { createLock } from "../../../utils/create-lock"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; import assert from "assert"; import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; import { BN, min } from "bn.js"; assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); const MTM_MINT_ADDRESS = process.env.NEXT_PUBLIC_MTM_TOKEN_MINT; function isParsedInstruction( instruction: ParsedInstruction | PartiallyDecodedInstruction ): instruction is ParsedInstruction { return (instruction as ParsedInstruction).parsed !== undefined; } async function extractMTMTransferDetails( connection: Connection, signature: string, mtmMintAddress: string // Mint address of the MTM token ) { try { // Fetch the transaction details using the signature const transaction: ParsedTransactionWithMeta | null = await connection.getParsedTransaction(signature, { maxSupportedTransactionVersion: 0, // Ensure compatibility with legacy transactions }); if (!transaction) { throw new Error('Transaction not found'); } // Extract the "from", "to", and "amount" for the MTM token transfer let fromAddress: string | null = null; let toAddress: string | null = null; let tokenAmount: number | null = null; // Function to process instructions const processInstructions = (instructions: ParsedInstruction[]) => { instructions.forEach((instruction: ParsedInstruction) => { if (instruction.programId.equals(new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'))) { // Check if the instruction is a transfer instruction if (instruction.parsed.type === 'transfer' || instruction.parsed.type === 'transferChecked') { const parsedInfo = instruction.parsed.info; const mint = parsedInfo.mint; // Check if the mint address matches the provided MTM mint address if (mint === mtmMintAddress) { fromAddress = parsedInfo.source; toAddress = parsedInfo.destination; tokenAmount = parsedInfo.amount; } } } }); }; // Filter out PartiallyDecodedInstruction and process only ParsedInstruction const parsedInstructions = transaction.transaction.message.instructions.filter(isParsedInstruction); // Process top-level instructions processInstructions(parsedInstructions); // Process inner instructions (if any) if (transaction.meta?.innerInstructions) { transaction.meta.innerInstructions.forEach((inner) => { const innerParsedInstructions = inner.instructions.filter(isParsedInstruction); processInstructions(innerParsedInstructions); }); } if (!fromAddress || !toAddress || !tokenAmount) { throw new Error('No matching MTM token transfer found in the transaction'); } return { fromAddress, toAddress, tokenAmount }; } catch (error) { console.error('Error extracting MTM transfer details:', error); throw error; } } export async function GET(req: NextRequest) { try { // Get signature from URL params const { searchParams } = new URL(req.url); const signature = searchParams.get('signature') || '4HBtnoNUuMGpmbhD9cPiJtbxkhux31pfZs3HYud5eopAU69RaC4UbJsYdj83eafFxV6eH8pSaRgqELrwyjrWp7yz'; let amount = new BN(0); if (!signature) { return NextResponse.json( { error: "Transaction signature is required" }, { status: 400 } ); } if (!MTM_MINT_ADDRESS) { return NextResponse.json( { error: "MTM_MINT_ADDRESS environment variable is not set" }, { status: 500 } ); } // Extract transaction details // const result = await extractMTMTransferDetails( // connection, // signature, // MTM_MINT_ADDRESS // ); // if (!result) { // return NextResponse.json( // { error: "Failed to extract transaction details" }, // { status: 500 } // ); // } // console.log({ result }); // // Validate extracted values // if (!result.fromAddress || result.tokenAmount <= 0) { // return NextResponse.json( // { error: "Invalid transaction details extracted" }, // { status: 400 } // ); // } assert(process.env.USER_PRIVATE_KEY, 'USER_PRIVATE_KEY is required'); assert(process.env.CLIFF_TIME, 'CLIFF_TIME is required'); const USER_PRIVATE_KEY = process.env.USER_PRIVATE_KEY; const CLIFF_TIME = process.env.CLIFF_TIME; const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(USER_PRIVATE_KEY)); const recipientPublicKey = new PublicKey('Bnnq8n3rRKZe8NJAYn4vBkxp1v8Bnc6zXpUPpDeujCu'); amount = amount.add(new BN(1000000)); // Call createLock function with extracted values let escrow; try { escrow = await createLock(tokenLockerKeypair, recipientPublicKey, duration, amount); } catch (error) { console.error('Error creating lock:', error); return NextResponse.json( { error: "Failed to create lock" }, { status: 500 } ); } console.log({ escrow }); // Return successful response return NextResponse.json({ success: true, data: { sender: recipientPublicKey, amount: amount, } }); } catch (error) { console.error('API route error:', error); return NextResponse.json( { error: "Internal server error" }, { status: 500 } ); } }