bunch of stuff

This commit is contained in:
zramsay 2024-12-20 14:53:36 -05:00
parent 1a9869b0d9
commit aeb240cfc5
10 changed files with 590 additions and 40 deletions

175
package-lock.json generated
View File

@ -9,9 +9,11 @@
"version": "0.1.0",
"dependencies": {
"@fal-ai/client": "^1.2.1",
"@google/generative-ai": "^0.21.0",
"@solana/spl-token": "^0.3.8",
"@solana/web3.js": "^1.78.4",
"next": "13.5.4",
"openai": "^4.77.0",
"react": "^18",
"react-dom": "^18"
},
@ -144,6 +146,14 @@
"node": ">=18.0.0"
}
},
"node_modules/@google/generative-ai": {
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz",
"integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==",
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@ -752,6 +762,15 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz",
"integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA=="
},
"node_modules/@types/node-fetch": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz",
"integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==",
"dependencies": {
"@types/node": "*",
"form-data": "^4.0.0"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.14",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
@ -900,6 +919,17 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/acorn": {
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
@ -1176,6 +1206,11 @@
"integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
"dev": true
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/autoprefixer": {
"version": "10.4.15",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz",
@ -1596,6 +1631,17 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
@ -1767,6 +1813,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@ -2567,6 +2621,14 @@
"node": ">=0.10.0"
}
},
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
@ -2744,6 +2806,36 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/form-data-encoder": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="
},
"node_modules/formdata-node": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
"integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
"dependencies": {
"node-domexception": "1.0.0",
"web-streams-polyfill": "4.0.0-beta.3"
},
"engines": {
"node": ">= 12.20"
}
},
"node_modules/fraction.js": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
@ -3888,6 +3980,25 @@
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
@ -4005,6 +4116,24 @@
}
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@ -4191,6 +4320,39 @@
"wrappy": "1"
}
},
"node_modules/openai": {
"version": "4.77.0",
"resolved": "https://registry.npmjs.org/openai/-/openai-4.77.0.tgz",
"integrity": "sha512-WWacavtns/7pCUkOWvQIjyOfcdr9X+9n9Vvb0zFeKVDAqwCMDHB+iSr24SVaBAhplvSG6JrRXFpcNM9gWhOGIw==",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7"
},
"bin": {
"openai": "bin/cli"
},
"peerDependencies": {
"zod": "^3.23.8"
},
"peerDependenciesMeta": {
"zod": {
"optional": true
}
}
},
"node_modules/openai/node_modules/@types/node": {
"version": "18.19.68",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.68.tgz",
"integrity": "sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@ -5649,6 +5811,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/update-browserslist-db": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
@ -5727,6 +5894,14 @@
"node": ">=10.13.0"
}
},
"node_modules/web-streams-polyfill": {
"version": "4.0.0-beta.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
"integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
"engines": {
"node": ">= 14"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",

View File

@ -10,9 +10,11 @@
},
"dependencies": {
"@fal-ai/client": "^1.2.1",
"@google/generative-ai": "^0.21.0",
"@solana/spl-token": "^0.3.8",
"@solana/web3.js": "^1.78.4",
"next": "13.5.4",
"openai": "^4.77.0",
"react": "^18",
"react-dom": "^18"
},

View File

@ -0,0 +1,65 @@
import { NextRequest, NextResponse } from 'next/server'
if (!process.env.ADOBE_API_KEY || !process.env.ADOBE_CLIENT_SECRET) {
throw new Error('Adobe API credentials not configured')
}
const ADOBE_API_URL = 'https://firefly-api.adobe.io
export async function POST(req: NextRequest): Promise<NextResponse> {
try {
const { prompt } = await req.json()
if (!prompt) {
return NextResponse.json(
{ error: 'Prompt is required' },
{ status: 400 }
)
}
// Adobe Firefly API request
const response = await fetch(ADOBE_API_URL, {
method: 'POST',
headers: {
'x-api-key': process.env.ADOBE_API_KEY,
'Authorization': `Bearer ${process.env.ADOBE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt: prompt,
n: 1,
size: { width: 1024, height: 1024 },
styles: ["default"],
seed: Math.floor(Math.random() * 1000000),
quality: "standard",
contentClass: "photo"
}),
})
if (!response.ok) {
const errorData = await response.json()
console.error('Adobe API error:', errorData)
throw new Error(`Adobe API error: ${response.status}`)
}
const data = await response.json()
console.log('Adobe generation result:', data)
// Extract the image URL from the response
const imageUrl = data.outputs?.[0]?.image
if (!imageUrl) {
throw new Error('No image URL in response')
}
return NextResponse.json({ imageUrl })
} catch (error) {
console.error('Adobe generation error:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to generate image' },
{ status: 500 }
)
}
}
export const dynamic = 'force-dynamic'

View File

@ -0,0 +1,37 @@
import { NextRequest, NextResponse } from 'next/server'
import { GoogleGenerativeAI } from '@google/generative-ai'
if (!process.env.GEMINI_API_KEY) {
throw new Error('GEMINI_API_KEY is not configured in environment variables')
}
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY)
const model = genAI.getGenerativeModel({ model: "gemini-1.5-pro" })
export async function POST(req: NextRequest): Promise<NextResponse> {
try {
const { prompt } = await req.json()
if (!prompt) {
return NextResponse.json(
{ error: 'Prompt is required' },
{ status: 400 }
)
}
const result = await model.generateContent(prompt)
const response = await result.response
const text = response.text()
console.log('Gemini generation result:', text)
return NextResponse.json({ response: text })
} catch (error) {
console.error('Gemini generation error:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to generate text' },
{ status: 500 }
)
}
}
export const dynamic = 'force-dynamic'

47
src/app/api/grok/route.ts Normal file
View File

@ -0,0 +1,47 @@
import { NextRequest, NextResponse } from 'next/server'
import OpenAI from "openai";
if (!process.env.XAI_API_KEY) {
throw new Error('XAI_API_KEY is not configured in environment variables')
}
const GROK_API_URL = 'https://api.openai.com/v1'
export async function POST(req: NextRequest): Promise<NextResponse> {
try {
const { prompt } = await req.json()
if (!prompt) {
return NextResponse.json(
{ error: 'Prompt is required' },
{ status: 400 }
)
}
const openai = new OpenAI({
apiKey: process.env.XAI_API_KEY,
baseURL: GROK_API_URL,
});
const image = await openai.images.generate({ model: "dall-e-3", prompt: prompt });
// Extract the image URL from Grok's response
const imageUrl = image.data[0].url
if (!imageUrl) {
console.error('No image in response:', data)
throw new Error('No image generated in Grok API result')
}
return NextResponse.json({ imageUrl })
} catch (error) {
console.error('Grok generation error:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to generate image' },
{ status: 500 }
)
}
}
export const dynamic = 'force-dynamic'

View File

@ -5,7 +5,10 @@ import WalletHeader from '../components/WalletHeader'
import AIServiceCard from '../components/AIServiceCard'
import TextGenerationCard from '../components/TextGenerationCard'
import { generateWithFlux, FluxGenerationResult } from '../services/fluxService'
import { generateWithAdobe, AdobeGenerationResult } from '../services/adobeService'
import { generateWithGrok, GrokGenerationResult } from '../services/grokService'
import { generateWithOllama, OllamaGenerationResult } from '../services/ollamaService'
import { generateWithGemini, GeminiGenerationResult } from '../services/geminiService'
import { processMTMPayment } from '../services/paymentService'
interface WalletState {
@ -65,6 +68,42 @@ const Page: React.FC = (): React.ReactElement => {
return generateWithFlux(prompt)
}
const handleGrokGeneration = async (prompt: string): Promise<GrokGenerationResult> => {
if (!walletState.connected || !walletState.publicKey || !window.solflare) {
return { error: 'Wallet not connected' }
}
const paymentResult = await processMTMPayment(
walletState.publicKey,
5, // 5 MTM tokens for Grok premium service
window.solflare
)
if (!paymentResult.success) {
return { error: paymentResult.error }
}
return generateWithGrok(prompt)
}
const handleAdobeGeneration = async (prompt: string): Promise<AdobeGenerationResult> => {
if (!walletState.connected || !walletState.publicKey || !window.solflare) {
return { error: 'Wallet not connected' }
}
const paymentResult = await processMTMPayment(
walletState.publicKey,
4, // 4 MTM tokens for Adobe premium service
window.solflare
)
if (!paymentResult.success) {
return { error: paymentResult.error }
}
return generateWithAdobe(prompt)
}
const handleOllamaGeneration = async (prompt: string): Promise<OllamaGenerationResult> => {
if (!walletState.connected || !walletState.publicKey || !window.solflare) {
return { error: 'Wallet not connected' }
@ -85,16 +124,36 @@ const Page: React.FC = (): React.ReactElement => {
return generateWithOllama(prompt)
}
const handleGeminiGeneration = async (prompt: string): Promise<GeminiGenerationResult> => {
if (!walletState.connected || !walletState.publicKey || !window.solflare) {
return { error: 'Wallet not connected' }
}
// First process payment
const paymentResult = await processMTMPayment(
walletState.publicKey,
3, // 3 MTM tokens for Gemini
window.solflare
)
if (!paymentResult.success) {
return { error: paymentResult.error }
}
// Then generate text
return generateWithGemini(prompt)
}
return (
<div className="min-h-screen w-full flex flex-col items-center bg-gradient-to-b from-gray-900 via-gray-800 to-gray-900">
<div className="container max-w-6xl mx-auto px-4 py-8">
<div className="container max-w-7xl mx-auto px-4 py-8">
{/* Header */}
<div className="text-center mb-8">
<h1 className="text-4xl sm:text-5xl font-bold mb-4 text-transparent bg-clip-text bg-gradient-to-r from-green-400 to-emerald-600">
AI Content Generator
Mark's Meme Market
</h1>
<p className="text-gray-400 text-lg mb-8">
Generate amazing content using different AI models
Generate memes using various AI models
</p>
<WalletHeader
@ -105,15 +164,34 @@ const Page: React.FC = (): React.ReactElement => {
</div>
{/* AI Services Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<AIServiceCard
title="Flux Meme Generator"
description="Generate creative memes and images using Flux AI"
description="Generate images using Flux AI"
tokenCost={1}
isWalletConnected={walletState.connected}
onGenerate={handleFluxGeneration}
/>
<AIServiceCard
title="X Grok Vision"
description="Advanced image generation by X's Grok AI"
tokenCost={5}
isWalletConnected={walletState.connected}
onGenerate={handleGrokGeneration}
/>
{/*
<AIServiceCard
title="Adobe Meme Generator"
description="Generate images images using Adobe"
tokenCost={4}
isWalletConnected={walletState.connected}
onGenerate={handleAdobeGeneration}
/>*/}
{/* for another app
<TextGenerationCard
title="Ollama AI Chat"
description="Get intelligent responses using local LLaMA2 model"
@ -121,12 +199,20 @@ const Page: React.FC = (): React.ReactElement => {
isWalletConnected={walletState.connected}
onGenerate={handleOllamaGeneration}
/>
<TextGenerationCard
title="Gemini Pro"
description="Advanced AI responses powered by Google's Gemini 1.5"
tokenCost={3}
isWalletConnected={walletState.connected}
onGenerate={handleGeminiGeneration}
/> */}
</div>
{/* Info Section */}
<div className="mt-12 text-center text-gray-400">
<p className="text-sm">
Powered by Flux AI and Ollama LLaMA2 Requires MTM tokens for generation
Powered by Mark Requires MTM tokens
</p>
</div>
</div>

View File

@ -0,0 +1,35 @@
export interface AdobeGenerationResult {
imageUrl?: string
error?: string
}
export async function generateWithAdobe(prompt: string): Promise<AdobeGenerationResult> {
try {
const response = await fetch('/api/adobe', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt }),
})
if (!response.ok) {
throw new Error('Failed to generate image')
}
const data = await response.json()
console.log('Raw Adobe Firefly response:', data)
if (data.imageUrl) {
return { imageUrl: data.imageUrl }
} else {
console.error('Unexpected response structure:', data)
throw new Error('Invalid response format from Adobe API')
}
} catch (error) {
console.error('Adobe generation error:', error)
return {
error: error instanceof Error ? error.message : 'Generation failed'
}
}
}

View File

@ -0,0 +1,35 @@
export interface GeminiGenerationResult {
textResponse?: string
error?: string
}
export async function generateWithGemini(prompt: string): Promise<GeminiGenerationResult> {
try {
const response = await fetch('/api/gemini', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt }),
})
if (!response.ok) {
throw new Error('Failed to generate text')
}
const data = await response.json()
console.log('Raw Gemini response:', data)
if (data.response) {
return { textResponse: data.response }
} else {
console.error('Unexpected response structure:', data)
throw new Error('Invalid response format from Gemini API')
}
} catch (error) {
console.error('Generation error:', error)
return {
error: error instanceof Error ? error.message : 'Generation failed'
}
}
}

View File

@ -0,0 +1,35 @@
export interface GrokGenerationResult {
imageUrl?: string
error?: string
}
export async function generateWithGrok(prompt: string): Promise<GrokGenerationResult> {
try {
const response = await fetch('/api/grok', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt }),
})
if (!response.ok) {
throw new Error('Failed to generate image')
}
const data = await response.json()
console.log('Raw Grok response:', data)
if (data.imageUrl) {
return { imageUrl: data.imageUrl }
} else {
console.error('Unexpected response structure:', data)
throw new Error('Invalid response format from Grok API')
}
} catch (error) {
console.error('Grok generation error:', error)
return {
error: error instanceof Error ? error.message : 'Generation failed'
}
}
}

View File

@ -1,4 +1,4 @@
import { Connection, PublicKey, Transaction } from '@solana/web3.js'
import { Connection, PublicKey, Transaction, SystemProgram } from '@solana/web3.js'
import {
TOKEN_PROGRAM_ID,
createTransferInstruction,
@ -9,7 +9,7 @@ import {
// Constants
const MTM_TOKEN_MINT: string = '97RggLo3zV5kFGYW4yoQTxr4Xkz4Vg2WPHzNYXXWpump'
const PAYMENT_RECEIVER_ADDRESS: string = '9B3mGyeJTUN7ZTqyLWHLL37zL92eif239hH2pYSkvq8J'
const PAYMENT_RECEIVER_ADDRESS: string = 'FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FFcowRobY'
// RPC Configuration
const SOLANA_RPC_URL: string = 'https://young-radial-orb.solana-mainnet.quiknode.pro/67612b364664616c29514e551bf5de38447ca3d4'
@ -30,6 +30,20 @@ export interface PaymentResult {
error?: string
}
async function findAssociatedTokenAddress(
walletAddress: PublicKey,
tokenMintAddress: PublicKey
): Promise<PublicKey> {
return PublicKey.findProgramAddressSync(
[
walletAddress.toBuffer(),
TOKEN_PROGRAM_ID.toBuffer(),
tokenMintAddress.toBuffer(),
],
ASSOCIATED_TOKEN_PROGRAM_ID
)[0]
}
export async function processMTMPayment(
walletPublicKey: string,
tokenAmount: number,
@ -40,69 +54,88 @@ export async function processMTMPayment(
const mintPublicKey = new PublicKey(MTM_TOKEN_MINT)
const receiverPublicKey = new PublicKey(PAYMENT_RECEIVER_ADDRESS)
const senderATA = await getAssociatedTokenAddress(
mintPublicKey,
senderPublicKey
console.log('Processing payment with keys:', {
sender: senderPublicKey.toBase58(),
mint: mintPublicKey.toBase58(),
receiver: receiverPublicKey.toBase58(),
})
// Find ATAs
const senderATA = await findAssociatedTokenAddress(
senderPublicKey,
mintPublicKey
)
const receiverATA = await getAssociatedTokenAddress(
mintPublicKey,
receiverPublicKey
const receiverATA = await findAssociatedTokenAddress(
receiverPublicKey,
mintPublicKey
)
console.log('Token accounts:', {
senderATA: senderATA.toBase58(),
receiverATA: receiverATA.toBase58(),
})
const transaction = new Transaction()
// Check and create ATAs if needed
const receiverATAInfo = await connection.getAccountInfo(receiverATA)
// Check if accounts exist
const [senderATAInfo, receiverATAInfo] = await Promise.all([
connection.getAccountInfo(senderATA),
connection.getAccountInfo(receiverATA),
])
// Create ATAs if they don't exist
if (!receiverATAInfo) {
console.log('Creating receiver token account')
transaction.add(
createATAInstruction(
senderPublicKey,
receiverATA,
receiverPublicKey,
mintPublicKey,
TOKEN_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
createAssociatedTokenAccountInstruction(
senderPublicKey, // payer
receiverATA, // ata
receiverPublicKey, // owner
mintPublicKey // mint
)
)
}
const senderATAInfo = await connection.getAccountInfo(senderATA)
if (!senderATAInfo) {
console.log('Creating sender token account')
transaction.add(
createATAInstruction(
senderPublicKey,
senderATA,
senderPublicKey,
mintPublicKey,
TOKEN_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
createAssociatedTokenAccountInstruction(
senderPublicKey, // payer
senderATA, // ata
senderPublicKey, // owner
mintPublicKey // mint
)
)
}
// Add transfer instruction
const transferInstruction = createTransferInstruction(
senderATA,
receiverATA,
senderPublicKey,
BigInt(tokenAmount * (10 ** 6))
transaction.add(
createTransferInstruction(
senderATA, // from
receiverATA, // to
senderPublicKey, // owner
BigInt(tokenAmount * (10 ** 6)) // amount
)
)
transaction.add(transferInstruction)
const latestBlockhash = await connection.getLatestBlockhash('confirmed')
transaction.recentBlockhash = latestBlockhash.blockhash
transaction.feePayer = senderPublicKey
console.log('Sending transaction...')
const { signature } = await solflareWallet.signAndSendTransaction(transaction)
console.log('Transaction sent:', signature)
const confirmation = await connection.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
})
}, 'confirmed')
if (confirmation.value.err) {
return { success: false, error: 'Payment failed to confirm' }
console.error('Transaction error:', confirmation.value.err)
throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`)
}
return { success: true }