mirror of
https://github.com/pezkuwichain/pezkuwi-p2p-mobile.git
synced 2026-10-20 18:56:22 +00:00
feat: add full p2p-fiat library with escrow support
- Copy complete p2p-fiat.ts from pwap/shared with all functions - Add createFiatOffer with internal ledger escrow lock - Add AES-256-GCM encryption for payment details - Add audit logging, reputation management - Update CreateAd.tsx to use createFiatOffer instead of direct insert - Remove unused wallet context from CreateAd
This commit is contained in:
1 parent
8676e947a5
commit
4074e7a884
2 files changed
+748
-315
No files matched your search
@@ -1,17 +1,15 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useAuth } from '@/contexts/AuthContext';
|
import { useAuth } from '@/contexts/AuthContext';
|
||||||
import { useWallet } from '@/contexts/WalletContext';
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||||
import { toast } from 'sonner';
|
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
import { supabase } from '@/lib/supabase';
|
|
||||||
import {
|
import {
|
||||||
getPaymentMethods,
|
getPaymentMethods,
|
||||||
validatePaymentDetails,
|
validatePaymentDetails,
|
||||||
|
createFiatOffer,
|
||||||
type PaymentMethod,
|
type PaymentMethod,
|
||||||
type FiatCurrency,
|
type FiatCurrency,
|
||||||
type CryptoToken
|
type CryptoToken
|
||||||
@@ -23,7 +21,6 @@ interface CreateAdProps {
|
|||||||
|
|
||||||
export function CreateAd({ onAdCreated }: CreateAdProps) {
|
export function CreateAd({ onAdCreated }: CreateAdProps) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { address } = useWallet();
|
|
||||||
|
|
||||||
const [paymentMethods, setPaymentMethods] = useState<PaymentMethod[]>([]);
|
const [paymentMethods, setPaymentMethods] = useState<PaymentMethod[]>([]);
|
||||||
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethod | null>(null);
|
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState<PaymentMethod | null>(null);
|
||||||
@@ -75,16 +72,11 @@ export function CreateAd({ onAdCreated }: CreateAdProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateAd = async () => {
|
const handleCreateAd = async () => {
|
||||||
console.log('🔥 handleCreateAd called', { address, user: user?.id });
|
if (!user) {
|
||||||
|
|
||||||
if (!address || !user) {
|
|
||||||
toast.error('Please connect your wallet and log in');
|
|
||||||
console.log('❌ No address or user', { address, user });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedPaymentMethod) {
|
if (!selectedPaymentMethod) {
|
||||||
toast.error('Please select a payment method');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,8 +87,6 @@ export function CreateAd({ onAdCreated }: CreateAdProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!validation.valid) {
|
if (!validation.valid) {
|
||||||
const firstError = Object.values(validation.errors)[0];
|
|
||||||
toast.error(firstError);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,63 +95,32 @@ export function CreateAd({ onAdCreated }: CreateAdProps) {
|
|||||||
const fiatAmt = parseFloat(fiatAmount);
|
const fiatAmt = parseFloat(fiatAmount);
|
||||||
|
|
||||||
if (!cryptoAmt || cryptoAmt <= 0) {
|
if (!cryptoAmt || cryptoAmt <= 0) {
|
||||||
toast.error('Invalid crypto amount');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fiatAmt || fiatAmt <= 0) {
|
if (!fiatAmt || fiatAmt <= 0) {
|
||||||
toast.error('Invalid fiat amount');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedPaymentMethod.min_trade_amount && fiatAmt < selectedPaymentMethod.min_trade_amount) {
|
|
||||||
toast.error(`Minimum trade amount: ${selectedPaymentMethod.min_trade_amount} ${fiatCurrency}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedPaymentMethod.max_trade_amount && fiatAmt > selectedPaymentMethod.max_trade_amount) {
|
|
||||||
toast.error(`Maximum trade amount: ${selectedPaymentMethod.max_trade_amount} ${fiatCurrency}`);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Insert offer into Supabase
|
// Use createFiatOffer which handles escrow locking via internal ledger
|
||||||
// Note: payment_details_encrypted is stored as JSON string (encryption handled server-side in prod)
|
await createFiatOffer({
|
||||||
const { data, error } = await supabase
|
|
||||||
.from('p2p_fiat_offers')
|
|
||||||
.insert({
|
|
||||||
seller_id: user.id,
|
|
||||||
seller_wallet: address,
|
|
||||||
ad_type: adType,
|
|
||||||
token,
|
token,
|
||||||
amount_crypto: cryptoAmt,
|
amountCrypto: cryptoAmt,
|
||||||
remaining_amount: cryptoAmt,
|
fiatCurrency,
|
||||||
fiat_currency: fiatCurrency,
|
fiatAmount: fiatAmt,
|
||||||
fiat_amount: fiatAmt,
|
paymentMethodId: selectedPaymentMethod.id,
|
||||||
payment_method_id: selectedPaymentMethod.id,
|
paymentDetails,
|
||||||
payment_details_encrypted: JSON.stringify(paymentDetails),
|
timeLimitMinutes: timeLimit,
|
||||||
time_limit_minutes: timeLimit,
|
minOrderAmount: minOrderAmount ? parseFloat(minOrderAmount) : undefined,
|
||||||
min_order_amount: minOrderAmount ? parseFloat(minOrderAmount) : null,
|
maxOrderAmount: maxOrderAmount ? parseFloat(maxOrderAmount) : undefined,
|
||||||
max_order_amount: maxOrderAmount ? parseFloat(maxOrderAmount) : null,
|
});
|
||||||
status: 'open'
|
|
||||||
})
|
|
||||||
.select()
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
console.error('❌ Supabase error:', error);
|
|
||||||
toast.error(error.message || 'Failed to create offer');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ Offer created successfully:', data);
|
|
||||||
toast.success('Ad created successfully!');
|
|
||||||
onAdCreated();
|
onAdCreated();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (import.meta.env.DEV) console.error('Create ad error:', error);
|
if (import.meta.env.DEV) console.error('Create ad error:', error);
|
||||||
toast.error('Failed to create offer');
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
+734
-260
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user