phantom
This commit is contained in:
parent
af601f4bbf
commit
14d278510c
src
@ -5,53 +5,33 @@ import WalletHeader from '../components/WalletHeader'
|
|||||||
import AIServiceCard from '../components/AIServiceCard'
|
import AIServiceCard from '../components/AIServiceCard'
|
||||||
import { generateChatResponse, ChatGenerationResult, CHAT_MODELS } from '../services/chatService'
|
import { generateChatResponse, ChatGenerationResult, CHAT_MODELS } from '../services/chatService'
|
||||||
import { processMTMPayment } from '../services/paymentService'
|
import { processMTMPayment } from '../services/paymentService'
|
||||||
|
import { connectWallet, WalletState } from '../services/walletService'
|
||||||
interface WalletState {
|
import { WalletType } from '../services/types'
|
||||||
connected: boolean
|
|
||||||
publicKey: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
solflare: any;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Page: React.FC = (): React.ReactElement => {
|
const Page: React.FC = (): React.ReactElement => {
|
||||||
const [walletState, setWalletState] = useState<WalletState>({
|
const [walletState, setWalletState] = useState<WalletState>({
|
||||||
connected: false,
|
connected: false,
|
||||||
publicKey: null,
|
publicKey: null,
|
||||||
|
type: null
|
||||||
})
|
})
|
||||||
|
|
||||||
const connectWallet = async (): Promise<void> => {
|
const handleConnect = async (walletType: WalletType): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
if (typeof window === 'undefined' || !window.solflare) {
|
const newWalletState = await connectWallet(walletType)
|
||||||
throw new Error('Solflare wallet not found! Please install it first.')
|
setWalletState(newWalletState)
|
||||||
}
|
|
||||||
|
|
||||||
await window.solflare.connect()
|
|
||||||
|
|
||||||
if (!window.solflare.publicKey) {
|
|
||||||
throw new Error('Failed to connect to wallet')
|
|
||||||
}
|
|
||||||
|
|
||||||
const publicKey: string = window.solflare.publicKey.toString()
|
|
||||||
setWalletState({
|
|
||||||
connected: true,
|
|
||||||
publicKey,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Wallet connection error:', error)
|
console.error('Wallet connection error:', error)
|
||||||
setWalletState({
|
setWalletState({
|
||||||
connected: false,
|
connected: false,
|
||||||
publicKey: null,
|
publicKey: null,
|
||||||
|
type: null
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChatGeneration = (modelId: string, cost: number) => {
|
const handleChatGeneration = (modelId: string, cost: number) => {
|
||||||
return async (prompt: string): Promise<ChatGenerationResult> => {
|
return async (prompt: string): Promise<ChatGenerationResult> => {
|
||||||
if (!walletState.connected || !walletState.publicKey || !window.solflare) {
|
if (!walletState.connected || !walletState.publicKey) {
|
||||||
return { error: 'Wallet not connected' }
|
return { error: 'Wallet not connected' }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +39,7 @@ const Page: React.FC = (): React.ReactElement => {
|
|||||||
const paymentResult = await processMTMPayment(
|
const paymentResult = await processMTMPayment(
|
||||||
walletState.publicKey,
|
walletState.publicKey,
|
||||||
cost,
|
cost,
|
||||||
window.solflare
|
walletState.type
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!paymentResult.success) {
|
if (!paymentResult.success) {
|
||||||
@ -84,9 +64,8 @@ const Page: React.FC = (): React.ReactElement => {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<WalletHeader
|
<WalletHeader
|
||||||
isConnected={walletState.connected}
|
walletState={walletState}
|
||||||
publicKey={walletState.publicKey}
|
onConnect={handleConnect}
|
||||||
onConnect={connectWallet}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,30 +1,36 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { WalletState, SUPPORTED_WALLETS } from '../services/walletService'
|
||||||
|
import { WalletType } from '../services/types'
|
||||||
|
|
||||||
interface WalletHeaderProps {
|
interface WalletHeaderProps {
|
||||||
isConnected: boolean
|
walletState: WalletState
|
||||||
publicKey: string | null
|
onConnect: (walletType: WalletType) => Promise<void>
|
||||||
onConnect: () => Promise<void>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const WalletHeader: React.FC<WalletHeaderProps> = ({ isConnected, publicKey, onConnect }) => {
|
const WalletHeader: React.FC<WalletHeaderProps> = ({ walletState, onConnect }) => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full bg-slate-900/50 backdrop-blur-lg rounded-xl shadow-lg border border-orange-800/50 mb-8 p-4">
|
<div className="w-full bg-slate-900/50 backdrop-blur-lg rounded-xl shadow-lg border border-orange-800/50 mb-8 p-4">
|
||||||
{!isConnected ? (
|
{!walletState.connected ? (
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
{SUPPORTED_WALLETS.map((wallet) => (
|
||||||
<button
|
<button
|
||||||
onClick={onConnect}
|
key={wallet.type}
|
||||||
className="w-full bg-gradient-to-r from-orange-500 to-amber-500 hover:from-orange-600 hover:to-amber-600
|
onClick={() => onConnect(wallet.type)}
|
||||||
|
className="bg-gradient-to-r from-orange-500 to-amber-500 hover:from-orange-600 hover:to-amber-600
|
||||||
text-white font-semibold py-3 px-6 rounded-lg transition-all duration-200
|
text-white font-semibold py-3 px-6 rounded-lg transition-all duration-200
|
||||||
shadow-lg hover:shadow-orange-500/25"
|
shadow-lg hover:shadow-orange-500/25"
|
||||||
>
|
>
|
||||||
Connect Solflare Wallet
|
Connect {wallet.name}
|
||||||
</button>
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-slate-300">Connected Wallet</span>
|
<span className="text-slate-300">Connected Wallet</span>
|
||||||
<span className="px-3 py-1 bg-amber-500/20 rounded-full text-amber-200 text-sm">
|
<span className="px-3 py-1 bg-amber-500/20 rounded-full text-amber-200 text-sm">
|
||||||
{publicKey?.slice(0, 22)}...
|
{walletState.publicKey?.slice(0, 22)}...
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -6,22 +6,19 @@ import {
|
|||||||
createAssociatedTokenAccountInstruction,
|
createAssociatedTokenAccountInstruction,
|
||||||
ASSOCIATED_TOKEN_PROGRAM_ID
|
ASSOCIATED_TOKEN_PROGRAM_ID
|
||||||
} from '@solana/spl-token'
|
} from '@solana/spl-token'
|
||||||
|
import { WalletType } from './types'
|
||||||
|
|
||||||
// Constants
|
|
||||||
const MTM_TOKEN_MINT: string = '97RggLo3zV5kFGYW4yoQTxr4Xkz4Vg2WPHzNYXXWpump'
|
const MTM_TOKEN_MINT: string = '97RggLo3zV5kFGYW4yoQTxr4Xkz4Vg2WPHzNYXXWpump'
|
||||||
const PAYMENT_RECEIVER_ADDRESS: string = 'FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FFcowRobY'
|
const PAYMENT_RECEIVER_ADDRESS: string = 'FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FFcowRobY'
|
||||||
|
|
||||||
// RPC Configuration
|
|
||||||
const SOLANA_RPC_URL: string = 'https://young-radial-orb.solana-mainnet.quiknode.pro/67612b364664616c29514e551bf5de38447ca3d4'
|
const SOLANA_RPC_URL: string = 'https://young-radial-orb.solana-mainnet.quiknode.pro/67612b364664616c29514e551bf5de38447ca3d4'
|
||||||
const SOLANA_WEBSOCKET_URL: string = 'wss://young-radial-orb.solana-mainnet.quiknode.pro/67612b364664616c29514e551bf5de38447ca3d4'
|
const SOLANA_WEBSOCKET_URL: string = 'wss://young-radial-orb.solana-mainnet.quiknode.pro/67612b364664616c29514e551bf5de38447ca3d4'
|
||||||
|
|
||||||
// Initialize connection with WebSocket support
|
|
||||||
const connection = new Connection(
|
const connection = new Connection(
|
||||||
SOLANA_RPC_URL,
|
SOLANA_RPC_URL,
|
||||||
{
|
{
|
||||||
commitment: 'confirmed',
|
commitment: 'confirmed',
|
||||||
wsEndpoint: SOLANA_WEBSOCKET_URL,
|
wsEndpoint: SOLANA_WEBSOCKET_URL,
|
||||||
confirmTransactionInitialTimeout: 60000, // 60 seconds
|
confirmTransactionInitialTimeout: 60000,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,12 +41,28 @@ async function findAssociatedTokenAddress(
|
|||||||
)[0]
|
)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WalletAdapter {
|
||||||
|
signAndSendTransaction(transaction: Transaction): Promise<{ signature: string }>
|
||||||
|
}
|
||||||
|
|
||||||
export async function processMTMPayment(
|
export async function processMTMPayment(
|
||||||
walletPublicKey: string,
|
walletPublicKey: string,
|
||||||
tokenAmount: number,
|
tokenAmount: number,
|
||||||
solflareWallet: any
|
walletType: WalletType
|
||||||
): Promise<PaymentResult> {
|
): Promise<PaymentResult> {
|
||||||
try {
|
try {
|
||||||
|
let wallet: WalletAdapter | null = null;
|
||||||
|
|
||||||
|
if (walletType === 'phantom') {
|
||||||
|
wallet = window.phantom?.solana || null;
|
||||||
|
} else if (walletType === 'solflare') {
|
||||||
|
wallet = window.solflare || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wallet) {
|
||||||
|
throw new Error(`${walletType} wallet not found`)
|
||||||
|
}
|
||||||
|
|
||||||
const senderPublicKey = new PublicKey(walletPublicKey)
|
const senderPublicKey = new PublicKey(walletPublicKey)
|
||||||
const mintPublicKey = new PublicKey(MTM_TOKEN_MINT)
|
const mintPublicKey = new PublicKey(MTM_TOKEN_MINT)
|
||||||
const receiverPublicKey = new PublicKey(PAYMENT_RECEIVER_ADDRESS)
|
const receiverPublicKey = new PublicKey(PAYMENT_RECEIVER_ADDRESS)
|
||||||
@ -60,7 +73,6 @@ export async function processMTMPayment(
|
|||||||
receiver: receiverPublicKey.toBase58(),
|
receiver: receiverPublicKey.toBase58(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Find ATAs
|
|
||||||
const senderATA = await findAssociatedTokenAddress(
|
const senderATA = await findAssociatedTokenAddress(
|
||||||
senderPublicKey,
|
senderPublicKey,
|
||||||
mintPublicKey
|
mintPublicKey
|
||||||
@ -78,21 +90,19 @@ export async function processMTMPayment(
|
|||||||
|
|
||||||
const transaction = new Transaction()
|
const transaction = new Transaction()
|
||||||
|
|
||||||
// Check if accounts exist
|
|
||||||
const [senderATAInfo, receiverATAInfo] = await Promise.all([
|
const [senderATAInfo, receiverATAInfo] = await Promise.all([
|
||||||
connection.getAccountInfo(senderATA),
|
connection.getAccountInfo(senderATA),
|
||||||
connection.getAccountInfo(receiverATA),
|
connection.getAccountInfo(receiverATA),
|
||||||
])
|
])
|
||||||
|
|
||||||
// Create ATAs if they don't exist
|
|
||||||
if (!receiverATAInfo) {
|
if (!receiverATAInfo) {
|
||||||
console.log('Creating receiver token account')
|
console.log('Creating receiver token account')
|
||||||
transaction.add(
|
transaction.add(
|
||||||
createAssociatedTokenAccountInstruction(
|
createAssociatedTokenAccountInstruction(
|
||||||
senderPublicKey, // payer
|
senderPublicKey,
|
||||||
receiverATA, // ata
|
receiverATA,
|
||||||
receiverPublicKey, // owner
|
receiverPublicKey,
|
||||||
mintPublicKey // mint
|
mintPublicKey
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -101,21 +111,20 @@ export async function processMTMPayment(
|
|||||||
console.log('Creating sender token account')
|
console.log('Creating sender token account')
|
||||||
transaction.add(
|
transaction.add(
|
||||||
createAssociatedTokenAccountInstruction(
|
createAssociatedTokenAccountInstruction(
|
||||||
senderPublicKey, // payer
|
senderPublicKey,
|
||||||
senderATA, // ata
|
senderATA,
|
||||||
senderPublicKey, // owner
|
senderPublicKey,
|
||||||
mintPublicKey // mint
|
mintPublicKey
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add transfer instruction
|
|
||||||
transaction.add(
|
transaction.add(
|
||||||
createTransferInstruction(
|
createTransferInstruction(
|
||||||
senderATA, // from
|
senderATA,
|
||||||
receiverATA, // to
|
receiverATA,
|
||||||
senderPublicKey, // owner
|
senderPublicKey,
|
||||||
BigInt(tokenAmount * (10 ** 6)) // amount
|
BigInt(tokenAmount * (10 ** 6))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -124,7 +133,7 @@ export async function processMTMPayment(
|
|||||||
transaction.feePayer = senderPublicKey
|
transaction.feePayer = senderPublicKey
|
||||||
|
|
||||||
console.log('Sending transaction...')
|
console.log('Sending transaction...')
|
||||||
const { signature } = await solflareWallet.signAndSendTransaction(transaction)
|
const { signature } = await wallet.signAndSendTransaction(transaction)
|
||||||
console.log('Transaction sent:', signature)
|
console.log('Transaction sent:', signature)
|
||||||
|
|
||||||
const confirmation = await connection.confirmTransaction({
|
const confirmation = await connection.confirmTransaction({
|
||||||
@ -147,3 +156,4 @@ export async function processMTMPayment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
src/services/types.ts
Normal file
20
src/services/types.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export type WalletType = 'solflare' | 'phantom'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
solflare?: {
|
||||||
|
connect(): Promise<void>
|
||||||
|
disconnect(): Promise<void>
|
||||||
|
publicKey?: { toString(): string }
|
||||||
|
signAndSendTransaction(transaction: any): Promise<{ signature: string }>
|
||||||
|
}
|
||||||
|
phantom?: {
|
||||||
|
solana?: {
|
||||||
|
connect(): Promise<{ publicKey: { toString(): string } }>
|
||||||
|
disconnect(): Promise<void>
|
||||||
|
signAndSendTransaction(transaction: any): Promise<{ signature: string }>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
65
src/services/walletService.ts
Normal file
65
src/services/walletService.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { WalletType } from './types'
|
||||||
|
|
||||||
|
export interface WalletState {
|
||||||
|
connected: boolean
|
||||||
|
publicKey: string | null
|
||||||
|
type: WalletType | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WalletConfig {
|
||||||
|
type: WalletType
|
||||||
|
name: string
|
||||||
|
connect: () => Promise<{ publicKey: string } | null>
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectSolflare = async (): Promise<{ publicKey: string } | null> => {
|
||||||
|
if (!window.solflare) return null
|
||||||
|
await window.solflare.connect()
|
||||||
|
return window.solflare.publicKey ? { publicKey: window.solflare.publicKey.toString() } : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectPhantom = async (): Promise<{ publicKey: string } | null> => {
|
||||||
|
if (!window.phantom?.solana) return null
|
||||||
|
try {
|
||||||
|
const response = await window.phantom.solana.connect()
|
||||||
|
return response.publicKey ? { publicKey: response.publicKey.toString() } : null
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SUPPORTED_WALLETS: WalletConfig[] = [
|
||||||
|
{
|
||||||
|
type: 'solflare',
|
||||||
|
name: 'Solflare',
|
||||||
|
connect: connectSolflare
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'phantom',
|
||||||
|
name: 'Phantom',
|
||||||
|
connect: connectPhantom
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export async function connectWallet(type: WalletType): Promise<WalletState> {
|
||||||
|
const wallet = SUPPORTED_WALLETS.find(w => w.type === type)
|
||||||
|
if (!wallet) throw new Error('Unsupported wallet')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await wallet.connect()
|
||||||
|
if (!result) throw new Error(`${wallet.name} not found`)
|
||||||
|
|
||||||
|
return {
|
||||||
|
connected: true,
|
||||||
|
publicKey: result.publicKey,
|
||||||
|
type: wallet.type
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
connected: false,
|
||||||
|
publicKey: null,
|
||||||
|
type: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user