Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ VITE_API_BASE_URL="https://florin.bitcoinos.build"
VITE_EXPIRATION_HOURS=24
VITE_MIN_AMOUNT="0.0004"
VITE_MAX_AMOUNT="3"
VITE_EVM_CONFIRMATIONS_LOW=1
VITE_EVM_CONFIRMATIONS_HIGH=10
VITE_EVM_CONFIRMATIONS=10
VITE_EVM_CONFIRMATIONS_USD_AMOUNT=100
VITE_BTC_CONFIRMATIONS=6
VITE_DEFAULT_POSITION_ID=
6 changes: 2 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ jobs:
VITE_EXPIRATION_HOURS: ${{ secrets.VITE_EXPIRATION_HOURS }}
VITE_MIN_AMOUNT: ${{ secrets.VITE_MIN_AMOUNT }}
VITE_MAX_AMOUNT: ${{ secrets.VITE_MAX_AMOUNT }}
VITE_EVM_CONFIRMATIONS_LOW: ${{ secrets.VITE_EVM_CONFIRMATIONS_LOW }}
VITE_EVM_CONFIRMATIONS_HIGH: ${{ secrets.VITE_EVM_CONFIRMATIONS_HIGH }}
VITE_EVM_CONFIRMATIONS: ${{ secrets.VITE_EVM_CONFIRMATIONS }}
VITE_EVM_CONFIRMATIONS_USD_AMOUNT: ${{ secrets.VITE_EVM_CONFIRMATIONS_USD_AMOUNT }}
VITE_BTC_CONFIRMATIONS: ${{ secrets.VITE_BTC_CONFIRMATIONS }}
VITE_DEFAULT_POSITION_ID: ${{ secrets.VITE_DEFAULT_POSITION_ID }}
Expand All @@ -52,8 +51,7 @@ jobs:
VITE_EXPIRATION_HOURS: ${{ secrets.VITE_EXPIRATION_HOURS }}
VITE_MIN_AMOUNT: ${{ secrets.VITE_MIN_AMOUNT }}
VITE_MAX_AMOUNT: ${{ secrets.VITE_MAX_AMOUNT }}
VITE_EVM_CONFIRMATIONS_LOW: ${{ secrets.VITE_EVM_CONFIRMATIONS_LOW }}
VITE_EVM_CONFIRMATIONS_HIGH: ${{ secrets.VITE_EVM_CONFIRMATIONS_HIGH }}
VITE_EVM_CONFIRMATIONS: ${{ secrets.VITE_EVM_CONFIRMATIONS }}
VITE_EVM_CONFIRMATIONS_USD_AMOUNT: ${{ secrets.VITE_EVM_CONFIRMATIONS_USD_AMOUNT }}
VITE_BTC_CONFIRMATIONS: ${{ secrets.VITE_BTC_CONFIRMATIONS }}
VITE_DEFAULT_POSITION_ID: ${{ secrets.VITE_DEFAULT_POSITION_ID }}
3 changes: 1 addition & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ jobs:
VITE_EXPIRATION_HOURS: ${{ secrets.VITE_EXPIRATION_HOURS }}
VITE_MIN_AMOUNT: ${{ secrets.VITE_MIN_AMOUNT }}
VITE_MAX_AMOUNT: ${{ secrets.VITE_MAX_AMOUNT }}
VITE_EVM_CONFIRMATIONS_LOW: ${{ secrets.VITE_EVM_CONFIRMATIONS_LOW }}
VITE_EVM_CONFIRMATIONS_HIGH: ${{ secrets.VITE_EVM_CONFIRMATIONS_HIGH }}
VITE_EVM_CONFIRMATIONS: ${{ secrets.VITE_EVM_CONFIRMATIONS }}
VITE_EVM_CONFIRMATIONS_USD_AMOUNT: ${{ secrets.VITE_EVM_CONFIRMATIONS_USD_AMOUNT }}
VITE_BTC_CONFIRMATIONS: ${{ secrets.VITE_BTC_CONFIRMATIONS }}
VITE_DEFAULT_POSITION_ID: ${{ secrets.VITE_DEFAULT_POSITION_ID }}
Expand Down
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,11 @@ VITE_MIN_AMOUNT="0.0004"
# Maximum amount allowed for transactions (in BTC)
VITE_MAX_AMOUNT="3"

# Number of confirmations required for low-value related to VITE_EVM_CONFIRMATIONS_USD_AMOUNT EVM transactions
VITE_EVM_CONFIRMATIONS_LOW=1
# Number of confirmations required for EVM transactions to be completed
VITE_EVM_CONFIRMATIONS=10

# Number of confirmations required for high-value EVM transactions
VITE_EVM_CONFIRMATIONS_HIGH=10
# USD amount threshold that determines if the ui shows up the second step immediately or if wait for VITE_EVM_CONFIRMATIONS confirmations

# USD amount threshold that determines when to use high confirmations
VITE_EVM_CONFIRMATIONS_USD_AMOUNT=100

# Number of confirmations required for Bitcoin transactions
Expand Down
2 changes: 1 addition & 1 deletion src/components/amount-input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export const AmountInput = ({
disabled={readOnly}
/>
<span className="font-inter font-normal text-[10px] sm:text-[12px] leading-[100%] tracking-[0%] text-right align-middle text-text-secondary">
${calculateUsdValue}
${Number(calculateUsdValue).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</span>
{errorMessage && !readOnly && (
<span className="font-inter font-normal text-[11px] sm:text-[12px] leading-[100%] tracking-[0%] text-right text-red-500 mt-1">
Expand Down
2 changes: 1 addition & 1 deletion src/components/tabs-switcher/fee-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface FeeCardProps {

export function FeeCard({ toCurrency, isAnimating, amount, gasFee }: FeeCardProps) {
const amountNumber = parseFloat(amount) || 0;
const receiveAmount = Math.max(0, amountNumber - gasFee).toFixed(6);
const receiveAmount = Math.max(0, amountNumber).toFixed(6);

return (
<Card
Expand Down
9 changes: 4 additions & 5 deletions src/components/tabs-switcher/terms-section.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Card } from '@/components/ui/card';
import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
import { cn } from '@/lib/utils';
import { Link } from '@tanstack/react-router';

interface TermsSectionProps {
termsAccepted: boolean;
Expand Down Expand Up @@ -58,15 +59,13 @@ export function TermsSection({
<label htmlFor="terms" className="cursor-pointer">
I agree to
</label>{' '}
<a
href="https://app-florin.netlify.app/"
target="_blank"
rel="noopener noreferrer"
<Link
to="/terms"
className="text-[#FFAA2E] hover:underline mx-1"
onClick={(e) => e.stopPropagation()}
>
Terms & Conditions
</a>{' '}
</Link>{' '}
<label htmlFor="terms" className="cursor-pointer">
and understand the risks
</label>
Expand Down
5 changes: 5 additions & 0 deletions src/components/tabs-switcher/transfer-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ export function TransferForm({
placeholder="Paste your Bitcoin receiving address"
value={bitcoinAddress || ''}
onChange={(e) => setBitcoinAddress(e.target.value as Address)}
errorMessage={
bitcoinAddress && !bitcoinAddressValid
? 'Invalid Bitcoin address'
: undefined
}
className={cn(
bitcoinAddress &&
!bitcoinAddressValid &&
Expand Down
21 changes: 6 additions & 15 deletions src/components/transaction-tracker/btc-send-step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TransactionStep } from './transaction-step';
import { WarningMessage } from './warning-message';
import { AddressReveal } from './address-reveal';
import { WarningIcon } from './warning-icon';
import { useState, useEffect, useMemo } from 'react';
import { useMemo } from 'react';
import { ReservationStatus, Reservation } from '@/types';
import { useTimer } from './timer-logic';
import {
Expand All @@ -18,25 +18,22 @@ interface BtcSendStepProps {
amount: string;
recipientAddress?: string;
isSent: boolean;
confirmations: number;
state: ReservationStatus;
reservation: Reservation;
fiatAmount: string;
maxConfirmations: number;
isReadyToSend: boolean;
}

export function BtcSendStep({
amount,
recipientAddress,
isSent,
confirmations,
state,
reservation,
fiatAmount,
maxConfirmations,
isReadyToSend,
}: BtcSendStepProps) {
const warningMessage = 'You can use any bitcoin wallet to send funds.';
const [isReadyToSend, setIsReadyToSend] = useState(false);
const blockTimestamp = reservation.blockTimestamp
? reservation.blockTimestamp * 1000
: 0;
Expand All @@ -63,15 +60,9 @@ export function BtcSendStep({

const descriptionMessage = isSent
? 'You initiated transaction in your wallet to send BTC.'
: parseFloat(fiatAmount) > 100
? 'Your Bitcoin transaction has been detected. You need to send BTC from your bitcoin wallet to a specified address. If your transaction is $100+ in BTC, you must to wait for at least 6 confirmations before sending BTC. Make sure to send BTC within 12 hours.'
: 'Your Bitcoin transaction has been detected. You can send BTC from your bitcoin wallet to a specified address. Make sure to send BTC within 12 hours.';

useEffect(() => {
if (confirmations >= maxConfirmations) {
setIsReadyToSend(true);
}
}, [confirmations, maxConfirmations]);
: parseFloat(fiatAmount) > env.VITE_EVM_CONFIRMATIONS_USD_AMOUNT
? `Your Bitcoin transaction has been detected. You need to send BTC from your bitcoin wallet to a specified address. If your transaction is ${env.VITE_EVM_CONFIRMATIONS_USD_AMOUNT}+ in BTC, you must to wait for at least ${env.VITE_EVM_CONFIRMATIONS} confirmations before sending BTC. Make sure to send BTC within ${env.VITE_EXPIRATION_HOURS} hours.`
: `Your Bitcoin transaction has been detected. You can send BTC from your bitcoin wallet to a specified address. Make sure to send BTC within ${env.VITE_EXPIRATION_HOURS} hours.`;

const stepStatus =
reservation.state !== ReservationStatus.Expired &&
Expand Down
18 changes: 1 addition & 17 deletions src/components/transaction-tracker/btc-transaction-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import BtcLogo from '@/assets/bitcoin-logo.png';
import { CheckCircledIcon } from '@radix-ui/react-icons';
import { getExplorerUrl, truncateAddress } from '@/lib/utils';
import { InfoField } from './info-field';
import { env } from '@/config/env';

export interface BtcTransactionCardProps {
data: {
Expand All @@ -21,17 +20,11 @@ export interface BtcTransactionCardProps {
isStepThree?: boolean;
}

const isHighAmount = (amount: string) => {
const amountNumber = parseFloat(amount);
return amountNumber >= env.VITE_EVM_CONFIRMATIONS_USD_AMOUNT;
};

export function BtcTransactionCard({
data,
isStepThree = false,
}: BtcTransactionCardProps) {
const renderConfirmations = () => {
if (isHighAmount(data.fiatAmount)) {
return (
<div className="flex items-center justify-center gap-1">
<span className="text-white text-[12px] font-medium">
Expand All @@ -44,16 +37,7 @@ export function BtcTransactionCard({
)}
</div>
);
} else {
return (
<div className="flex items-center justify-center gap-1">
<span className="text-white text-[12px] font-medium">
{Math.min(Number(data?.confirmations) || 0, Number(data.maxConfirmations) || 0)}
</span>
<CheckCircledIcon className="text-green-600 w-4 h-4" />
</div>
);
}

};

const renderAmount = () => (
Expand Down
24 changes: 16 additions & 8 deletions src/components/transaction-tracker/position-tracker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { useEVMPositionPolling } from '@/hooks/useEVMPositionPolling';
import { useChainId } from 'wagmi';
import { formatUnits } from 'viem';
import { useMemo } from 'react';
import { useMemo, useState, useEffect } from 'react';
import { useBitcoinPrice } from '@/hooks/useBitcoinPrice';
import { PositionStatus } from '@/types';
import { POSITION_STATUS_MAP } from '../history-table/transaction-history-adapter';
Expand All @@ -27,26 +27,28 @@
id,
txHash,
}: PositionTrackerProps) {
const { data: position } = usePosition(id, {});
const [shouldPoll, setShouldPoll] = useState(open);
const { data: position } = usePosition(id, {
refetchInterval: shouldPoll ? 5000 : undefined,
});
const chainId = useChainId();
const { data: bitcoinPrice } = useBitcoinPrice();

const { evmPosition, isLoading, error } = useEVMPositionPolling({
positionId: id || '',
chainId: chainId || 0,
isActive: open,
isActive: shouldPoll,
});
const { data: bitcoinPrice } = useBitcoinPrice();

const amount = formatUnits(evmPosition?.originalAmount || 0n, 8);
const fiatAmount = useMemo(() => {
if (!evmPosition?.originalAmount || !bitcoinPrice?.bitcoin?.usd) return '0';
const usdValue = Number(amount) * bitcoinPrice.bitcoin.usd;
return usdValue.toFixed(2);
}, [evmPosition?.originalAmount, bitcoinPrice?.bitcoin?.usd]);

Check warning on line 48 in src/components/transaction-tracker/position-tracker.tsx

View workflow job for this annotation

GitHub Actions / lint-test-build

React Hook useMemo has a missing dependency: 'amount'. Either include it or remove the dependency array

const maxConfirmations =
Number(fiatAmount) > env.VITE_EVM_CONFIRMATIONS_USD_AMOUNT
? env.VITE_EVM_CONFIRMATIONS_HIGH
: env.VITE_EVM_CONFIRMATIONS_LOW;
const maxConfirmations = Number(env.VITE_EVM_CONFIRMATIONS);

const confirmations = useTxConfirmations({
isActive: open,
maxConfirmations: maxConfirmations,
Expand All @@ -55,6 +57,12 @@

const status = POSITION_STATUS_MAP[evmPosition?.status || 1];
const isPositionCompleted = status === PositionStatus.Closed;

useEffect(() => {
const should = open && !isPositionCompleted;
setShouldPoll(should);
}, [isPositionCompleted, open]);

const displayConfirmations = isPositionCompleted
? maxConfirmations
: confirmations;
Expand Down
38 changes: 21 additions & 17 deletions src/components/transaction-tracker/reservation-tracker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { BtcSendStep } from './btc-send-step';
import { EthCompletionCard } from './eth-completion-card';
import { BaseTransactionTracker } from './base-transaction-tracker';
import { useBitcoinPrice } from '@/hooks/useBitcoinPrice';
import { useMemo } from 'react';
import { useMemo, useState, useEffect } from 'react';
import { useTxConfirmations } from '@/hooks/useTxConfirmations';
import { useEVMReservationPolling } from '@/hooks/useEVMReservationPolling';
import { useChainId } from 'wagmi';
Expand All @@ -28,22 +28,20 @@ export function ReservationTracker({
id,
txHash,
}: ReservationTrackerProps) {
const [shouldPoll, setShouldPoll] = useState(open);
const { data } = useReservation(id, {
refetchInterval: 5000,
refetchInterval: shouldPoll ? 5000 : undefined,
});
const { data: bitcoinPrice } = useBitcoinPrice();
const chainId = useChainId();
const reservation = data?.data;

const {
evmReservation,
// isLoading: isEVMReservationLoading,
error: isEVMReservationError,
} = useEVMReservationPolling({
reservationId: id || '',
chainId: chainId || 0,
isActive: open,
});
const { evmReservation, error: isEVMReservationError } =
useEVMReservationPolling({
reservationId: id || '',
chainId: chainId || 0,
isActive: shouldPoll,
});

const amount = formatUnits(evmReservation?.tokenAmount || 0n, 8);

Expand All @@ -53,10 +51,8 @@ export function ReservationTracker({
return usdValue.toFixed(2);
}, [amount, bitcoinPrice?.bitcoin?.usd]);

const maxConfirmations =
Number(fiatAmount) > env.VITE_EVM_CONFIRMATIONS_USD_AMOUNT
? env.VITE_EVM_CONFIRMATIONS_HIGH
: env.VITE_EVM_CONFIRMATIONS_LOW;
const maxConfirmations = Number(env.VITE_EVM_CONFIRMATIONS);

const confirmations = useTxConfirmations({
isActive: open,
maxConfirmations: maxConfirmations,
Expand All @@ -70,6 +66,15 @@ export function ReservationTracker({

const status = RESERVATION_STATUS_MAP[evmReservation?.status || 0];
const bridgingCompleted = status === ReservationStatus.Settled;
const btcReadyToSend =
Number(fiatAmount) < env.VITE_EVM_CONFIRMATIONS_USD_AMOUNT ||
confirmations >= maxConfirmations;

useEffect(() => {
const should = open && !bridgingCompleted;
setShouldPoll(should);
}, [bridgingCompleted, open]);

const maxHeightClass = !bridgingCompleted
? 'max-h-[90vh] md:h-[813px]'
: 'max-h-[90vh]';
Expand Down Expand Up @@ -114,8 +119,7 @@ export function ReservationTracker({
<BtcSendStep
amount={amount}
isSent={bridgingCompleted}
confirmations={confirmations}
maxConfirmations={maxConfirmations}
isReadyToSend={btcReadyToSend}
recipientAddress={evmReservation.bitcoinAddress}
state={status}
reservation={{
Expand Down
8 changes: 7 additions & 1 deletion src/components/ui/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { cn } from '@/lib/utils';
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
icon?: React.ReactNode;
errorMessage?: string;
}

function Input({ className, type, icon, ...props }: InputProps) {
function Input({ className, type, icon, errorMessage, ...props }: InputProps) {
return (
<div className="relative w-full max-w-[408px]">
<input
Expand All @@ -31,6 +32,11 @@ function Input({ className, type, icon, ...props }: InputProps) {
{icon}
</div>
)}
{errorMessage && (
<span className="font-inter font-normal text-[11px] sm:text-[12px] leading-[100%] tracking-[0%] text-right text-red-500 mt-1 block">
{errorMessage}
</span>
)}
</div>
);
}
Expand Down
7 changes: 2 additions & 5 deletions src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,8 @@ const envSchema = z.object({
VITE_MAX_AMOUNT: z.string().transform((val) => Number(val)).refine((val) => val > 0, {
message: 'VITE_MAX_AMOUNT must be greater than 0',
}),
VITE_EVM_CONFIRMATIONS_LOW: z.string().transform((val) => Number(val)).refine((val) => val > 0, {
message: 'VITE_EVM_CONFIRMATIONS_LOW must be greater than 0',
}),
VITE_EVM_CONFIRMATIONS_HIGH: z.string().transform((val) => Number(val)).refine((val) => val > 0, {
message: 'VITE_EVM_CONFIRMATIONS_HIGH must be greater than 0',
VITE_EVM_CONFIRMATIONS: z.string().transform((val) => Number(val)).refine((val) => val > 0, {
message: 'VITE_EVM_CONFIRMATIONS must be greater than 0',
}),
VITE_EVM_CONFIRMATIONS_USD_AMOUNT: z.string().transform((val) => Number(val)).refine((val) => val > 0, {
message: 'VITE_EVM_CONFIRMATIONS_USD_AMOUNT must be greater than 0',
Expand Down
2 changes: 1 addition & 1 deletion src/constants/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ export const CURRENCY_SYMBOLS = {

export const NETWORK_NAMES = {
bitcoin: 'Bitcoin',
ethereum: 'Ethereum Network',
ethereum: 'Ethereum',
};
Loading
Loading