feat: use Edge Functions for P2P operations with Telegram auth

- createFiatOffer: Uses create-offer-telegram Edge Function
- AdList: Uses get-my-offers Edge Function for "my-ads" tab
- Added adType parameter to CreateOfferParams

Fixes RLS issues where Telegram auth doesn't set auth.uid().
This commit is contained in:
Kurdistan Tech Ministry committed 2026-02-03 22:10:03 +03:00
1 parent 549d97b90c
commit 0bcdf5740b
2 files changed
+58 -62

No files matched your search

+24 -4
View File
@@ -51,7 +51,29 @@ export function AdList({ type, filters }: AdListProps) {
try {
let offersData: P2PFiatOffer[] = [];
// Build base query
// For "my-ads", use Edge Function to bypass RLS (Telegram auth doesn't set auth.uid())
if (type === 'my-ads') {
const sessionToken = localStorage.getItem('p2p_session');
if (!sessionToken) {
setOffers([]);
setLoading(false);
return;
}
const { data, error } = await supabase.functions.invoke('get-my-offers', {
body: { sessionToken }
});
if (error) {
console.error('Get my offers error:', error);
setOffers([]);
setLoading(false);
return;
}
offersData = data?.offers || [];
} else {
// Build base query for public offers
let query = supabase.from('p2p_fiat_offers').select('*');
if (type === 'buy') {
@@ -60,9 +82,6 @@ export function AdList({ type, filters }: AdListProps) {
} else if (type === 'sell') {
// Sell tab = show BUY offers (user wants to sell to buyers)
query = query.eq('ad_type', 'buy').eq('status', 'open').gt('remaining_amount', 0);
} else if (type === 'my-ads' && user) {
// My offers - show all of user's offers
query = query.eq('seller_id', user.id);
}
// Apply filters if provided
@@ -102,6 +121,7 @@ export function AdList({ type, filters }: AdListProps) {
const { data } = await query;
offersData = data || [];
}
// Enrich with reputation, payment method, and merchant tier
const enrichedOffers = await Promise.all(
+34 -58
View File
@@ -159,6 +159,7 @@ export interface CreateOfferParams {
timeLimitMinutes?: number;
minOrderAmount?: number;
maxOrderAmount?: number;
adType?: 'buy' | 'sell'; // Default: 'sell'
}
export interface AcceptOfferParams {
@@ -411,9 +412,9 @@ async function logAction(
/**
* Create a new P2P fiat offer (OKX-Style Internal Ledger)
*
* Steps:
* Uses Edge Function to:
* 1. Lock crypto from internal balance (NO blockchain tx)
* 2. Create offer record in Supabase
* 2. Create offer record in Supabase (bypasses RLS with service role)
* 3. Update escrow tracking
*/
export async function createFiatOffer(params: CreateOfferParams): Promise<string> {
@@ -426,74 +427,49 @@ export async function createFiatOffer(params: CreateOfferParams): Promise<string
paymentDetails,
timeLimitMinutes = DEFAULT_PAYMENT_DEADLINE_MINUTES,
minOrderAmount,
maxOrderAmount
maxOrderAmount,
adType = 'sell'
} = params;
try {
// Get current user
const userId = await getCurrentUserId();
if (!userId) throw new Error('Not authenticated');
// Get session token for Edge Function authentication
const sessionToken = localStorage.getItem('p2p_session');
if (!sessionToken) throw new Error('Not authenticated. Please log in again.');
toast.info('Locking crypto from your balance...');
toast.info('Creating offer...');
// 1. Lock crypto from internal balance (NO blockchain tx!)
const { data: lockResult, error: lockError } = await supabase.rpc('lock_escrow_internal', {
p_user_id: userId,
p_token: token,
p_amount: amountCrypto
});
if (lockError) throw lockError;
// Parse result
const lockResponse = typeof lockResult === 'string' ? JSON.parse(lockResult) : lockResult;
if (!lockResponse.success) {
throw new Error(lockResponse.error || 'Failed to lock balance');
}
toast.success('Balance locked successfully');
// 2. Encrypt payment details (AES-256-GCM)
// Encrypt payment details (AES-256-GCM) before sending
const encryptedDetails = await encryptPaymentDetails(paymentDetails);
// 3. Create offer in Supabase
const { data: offer, error: offerError } = await supabase
.from('p2p_fiat_offers')
.insert({
seller_id: userId,
seller_wallet: '', // No longer needed with internal ledger
// Call Edge Function (handles escrow locking + offer creation with service role)
const { data, error } = await supabase.functions.invoke('create-offer-telegram', {
body: {
sessionToken,
token,
amount_crypto: amountCrypto,
fiat_currency: fiatCurrency,
fiat_amount: fiatAmount,
price_per_unit: fiatAmount / amountCrypto,
payment_method_id: paymentMethodId,
payment_details_encrypted: encryptedDetails,
min_order_amount: minOrderAmount,
max_order_amount: maxOrderAmount,
time_limit_minutes: timeLimitMinutes,
status: 'open',
remaining_amount: amountCrypto,
escrow_locked_at: new Date().toISOString()
})
.select()
.single();
if (offerError) throw offerError;
// 4. Audit log
await logAction('offer', offer.id, 'create_offer', {
token,
amount_crypto: amountCrypto,
fiat_currency: fiatCurrency,
fiat_amount: fiatAmount,
escrow_type: 'internal_ledger'
amountCrypto,
fiatCurrency,
fiatAmount,
paymentMethodId,
paymentDetailsEncrypted: encryptedDetails,
minOrderAmount: minOrderAmount || null,
maxOrderAmount: maxOrderAmount || null,
timeLimitMinutes,
adType
}
});
if (error) {
console.error('Edge Function error:', error);
throw new Error(error.message || 'Failed to create offer');
}
if (!data?.success) {
throw new Error(data?.error || 'Failed to create offer');
}
toast.success(`Offer created! Selling ${amountCrypto} ${token} for ${fiatAmount} ${fiatCurrency}`);
return offer.id;
return data.offer_id;
} catch (error: unknown) {
console.error('Create offer error:', error);
const message = error instanceof Error ? error.message : 'Failed to create offer';