fix: withdrawal calls process-withdraw edge function, balance card UI improvements

- requestWithdraw() now calls process-withdraw edge function instead of
  just locking balance in DB. This triggers the full flow: lock balance,
  send blockchain TX, complete withdrawal.
- Balance card: numbers right-aligned, 2 decimal places instead of 4
- Added public SELECT RLS policy on payment_methods table (was blocking
  anon users from loading payment method dropdown)
This commit is contained in:
2026-02-23 22:19:06 +03:00
parent 646e388b28
commit b839a1834d
2 changed files with 19 additions and 25 deletions
+10 -16
View File
@@ -1011,7 +1011,8 @@ export async function getInternalBalance(userId: string, token: CryptoToken): Pr
/** /**
* Request a withdrawal from internal balance to external wallet * Request a withdrawal from internal balance to external wallet
* This creates a pending request that will be processed by backend service * Calls the process-withdraw edge function which handles the full flow:
* limit check → lock balance → blockchain TX → complete withdrawal
*/ */
export async function requestWithdraw( export async function requestWithdraw(
userId: string, userId: string,
@@ -1030,31 +1031,24 @@ export async function requestWithdraw(
throw new Error('Invalid wallet address'); throw new Error('Invalid wallet address');
} }
toast.info('Processing withdrawal request...'); toast.info('Processing withdrawal...');
// Call the database function const { data, error } = await supabase.functions.invoke('process-withdraw', {
const { data, error } = await supabase.rpc('request_withdraw', { body: { userId, token, amount, walletAddress }
p_user_id: userId,
p_token: token,
p_amount: amount,
p_wallet_address: walletAddress
}); });
if (error) throw error; if (error) throw error;
// Parse result if (!data?.success) {
const result = typeof data === 'string' ? JSON.parse(data) : data; throw new Error(data?.error || 'Withdrawal failed');
if (!result.success) {
throw new Error(result.error || 'Withdrawal request failed');
} }
toast.success(`Withdrawal request submitted! ${amount} ${token} will be sent to your wallet.`); toast.success(`${amount} ${token} sent to your wallet! TX: ${data.txHash?.slice(0, 10)}...`);
return result.request_id; return data.txHash || '';
} catch (error: unknown) { } catch (error: unknown) {
console.error('Request withdraw error:', error); console.error('Request withdraw error:', error);
const message = error instanceof Error ? error.message : 'Withdrawal request failed'; const message = error instanceof Error ? error.message : 'Withdrawal failed';
toast.error(message); toast.error(message);
throw error; throw error;
} }
@@ -49,7 +49,7 @@ export function InternalBalanceCard({ onDeposit, onWithdraw }: InternalBalanceCa
await fetchBalances(); await fetchBalances();
}; };
const formatBalance = (value: number, decimals: number = 4) => { const formatBalance = (value: number, decimals: number = 2) => {
return value.toLocaleString(undefined, { return value.toLocaleString(undefined, {
minimumFractionDigits: decimals, minimumFractionDigits: decimals,
maximumFractionDigits: decimals maximumFractionDigits: decimals
@@ -122,8 +122,8 @@ export function InternalBalanceCard({ onDeposit, onWithdraw }: InternalBalanceCa
<div className="grid grid-cols-2 gap-4 text-sm"> <div className="grid grid-cols-2 gap-4 text-sm">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Unlock className="h-4 w-4 text-green-500" /> <Unlock className="h-4 w-4 text-green-500 shrink-0" />
<div> <div className="flex-1 text-right">
<p className="text-muted-foreground text-xs">{t('p2pBalance.available')}</p> <p className="text-muted-foreground text-xs">{t('p2pBalance.available')}</p>
<p className="font-medium text-green-600"> <p className="font-medium text-green-600">
{formatBalance(balance.available_balance)} {formatBalance(balance.available_balance)}
@@ -131,8 +131,8 @@ export function InternalBalanceCard({ onDeposit, onWithdraw }: InternalBalanceCa
</div> </div>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Lock className="h-4 w-4 text-yellow-500" /> <Lock className="h-4 w-4 text-yellow-500 shrink-0" />
<div> <div className="flex-1 text-right">
<p className="text-muted-foreground text-xs">{t('p2pBalance.lockedEscrow')}</p> <p className="text-muted-foreground text-xs">{t('p2pBalance.lockedEscrow')}</p>
<p className="font-medium text-yellow-600"> <p className="font-medium text-yellow-600">
{formatBalance(balance.locked_balance)} {formatBalance(balance.locked_balance)}
@@ -142,13 +142,13 @@ export function InternalBalanceCard({ onDeposit, onWithdraw }: InternalBalanceCa
</div> </div>
<div className="mt-3 pt-3 border-t grid grid-cols-2 gap-2 text-xs text-muted-foreground"> <div className="mt-3 pt-3 border-t grid grid-cols-2 gap-2 text-xs text-muted-foreground">
<div> <div className="flex justify-between">
<span>{t('p2pBalance.totalDeposited')}</span> <span>{t('p2pBalance.totalDeposited')}</span>
<span className="text-foreground">{formatBalance(balance.total_deposited, 2)}</span> <span className="text-foreground">{formatBalance(balance.total_deposited)}</span>
</div> </div>
<div> <div className="flex justify-between">
<span>{t('p2pBalance.totalWithdrawn')}</span> <span>{t('p2pBalance.totalWithdrawn')}</span>
<span className="text-foreground">{formatBalance(balance.total_withdrawn, 2)}</span> <span className="text-foreground">{formatBalance(balance.total_withdrawn)}</span>
</div> </div>
</div> </div>
</div> </div>