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
88 changes: 30 additions & 58 deletions src/components/ui/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,77 +1,49 @@
import { ActionType } from '@/types/actions';

interface TabItem<T> {
value: T;
label: string;
}

interface TabsPropsGeneric<T extends string> {
interface TabsProps<T extends string> {
activeTab: T;
setActiveTab: React.Dispatch<React.SetStateAction<T>>;
setActiveTab: React.Dispatch<React.SetStateAction<T>> | ((tab: T) => void);
tabs: TabItem<T>[];
isProcessing?: boolean;
className?: string;
}

interface TabsPropsAction {
activeTab: ActionType;
setActiveTab: React.Dispatch<React.SetStateAction<ActionType>>;
tabs?: never;
className?: string;
}

type TabsProps<T extends string = ActionType> = T extends ActionType
? TabsPropsAction
: TabsPropsGeneric<T>;

export default function Tabs<T extends string = ActionType>({
activeTab,
setActiveTab,
export default function Tabs<T extends string>({
activeTab,
setActiveTab,
tabs,
className
}: TabsProps<T> | TabsPropsGeneric<T>) {
isProcessing,
className,
}: TabsProps<T>) {
const safeSetActiveTab = (tab: T) => {
if (isProcessing && tab !== activeTab) return;
setActiveTab(tab);
};

const tabClass = (tab: T) =>
`flex-1 py-2 px-4 rounded-lg font-medium transition-colors focus:outline-none focus:ring-0 ${activeTab === tab
? 'bg-indigo-600 text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
`flex-1 py-2 px-4 rounded-lg font-medium transition-colors focus:outline-none focus:ring-0 ${isProcessing && tab !== activeTab
? 'bg-gray-200 text-gray-400 cursor-not-allowed opacity-60 border-0'
: activeTab === tab
? 'bg-indigo-600 text-white'
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
}`;

// If custom tabs are provided, use them
if (tabs) {
return (
<div className={`${className ?? ''}`}>
<div className="flex space-x-2">
{tabs.map((tab) => (
<button
key={tab.value}
onClick={() => setActiveTab(tab.value as any)}
className={tabClass(tab.value as T)}
>
{tab.label}
</button>
))}
</div>
</div>
);
}

// Default action tabs layout
return (
<div className={`${className ?? ''}`}>
<div className="flex space-x-4 mb-3">
<button onClick={() => setActiveTab('deposit' as any)} className={tabClass('deposit' as T)}>
Deposit
</button>
<button onClick={() => setActiveTab('redeem' as any)} className={tabClass('redeem' as T)}>
Redeem
</button>
</div>
<div className="flex space-x-4">
<button onClick={() => setActiveTab('mint' as any)} className={tabClass('mint' as T)}>
Mint
</button>
<button onClick={() => setActiveTab('withdraw' as any)} className={tabClass('withdraw' as T)}>
Withdraw
</button>
<div className={tabs.length === 4 ? "grid grid-cols-2 gap-2" : "flex space-x-2"}>
{tabs.map((tab) => (
<button
key={tab.value}
onClick={() => safeSetActiveTab(tab.value)}
className={tabClass(tab.value)}
disabled={isProcessing && tab.value !== activeTab}
>
{tab.label}
</button>
))}
</div>
</div>
);
Expand Down
7 changes: 6 additions & 1 deletion src/components/vault/Actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Tabs from '@/components/ui/Tabs';
import ActionWrapper from '@/components/actions/ActionWrapper';
import { ActionType } from '@/types/actions';
import DexLink from './DexLink';
import { ACTIONS_TABS } from '@/constants';

interface ActionsProps {
isSafe?: boolean;
Expand All @@ -13,7 +14,11 @@ export default function Actions({ isSafe = false }: ActionsProps) {

return (
<div className="bg-gray-50 rounded-lg p-4">
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} />
<Tabs
activeTab={activeTab}
setActiveTab={setActiveTab}
tabs={ACTIONS_TABS}
/>
<ActionWrapper actionType={activeTab} isSafe={isSafe} />
<DexLink />
</div>
Expand Down
17 changes: 3 additions & 14 deletions src/components/vault/ActionsDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import Tabs from '@/components/ui/Tabs';
import ActionWrapper from '@/components/actions/ActionWrapper';
import { ActionType } from '@/types/actions';
import DexLink from './DexLink';
import { ACTIONS_TABS } from '@/constants';

interface ActionsProps {
isSafe?: boolean;
}

export default function ActionsDropdown({ isSafe = false } : ActionsProps ) {
export default function ActionsDropdown({ isSafe = false }: ActionsProps) {
const [isOpen, setIsOpen] = useState(false);
const [activeTab, setActiveTab] = useState<ActionType>('deposit');

Expand All @@ -33,22 +34,10 @@ export default function ActionsDropdown({ isSafe = false } : ActionsProps ) {
className={`transition-all duration-200 overflow-hidden ${isOpen ? 'max-h-[2000px] opacity-100 p-3' : 'max-h-0 opacity-0 pb-0'
}`}
>
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} />
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} tabs={ACTIONS_TABS} />
<ActionWrapper actionType={activeTab} isSafe={isSafe} />
<DexLink />
</div>
</div>
);
}

export function Actions({ isSafe = false }: ActionsProps) {
const [activeTab, setActiveTab] = useState<ActionType>('deposit');

return (
<div className="bg-gray-50 rounded-lg p-4">
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} />
<ActionWrapper actionType={activeTab} isSafe={isSafe} />
<DexLink />
</div>
);
}
13 changes: 11 additions & 2 deletions src/components/vault/FlashLoanDepositWithdraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default function FlashLoanDepositWithdraw() {

const [isOpen, setIsOpen] = useState(false);
const [activeTab, setActiveTab] = useState<ActionType>(availableTabs[0]?.value || 'deposit');
const [isProcessing, setIsProcessing] = useState(false);

if (availableTabs.length === 0) {
return null;
Expand Down Expand Up @@ -52,10 +53,18 @@ export default function FlashLoanDepositWithdraw() {
>
{availableTabs.length > 1 && (
<div className="mb-3">
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} tabs={availableTabs} />
<Tabs
activeTab={activeTab}
setActiveTab={setActiveTab}
tabs={availableTabs}
isProcessing={isProcessing}
/>
</div>
)}
<FlashLoanDepositWithdrawHandler actionType={activeTab} />
<FlashLoanDepositWithdrawHandler
actionType={activeTab}
setIsProcessing={setIsProcessing}
/>
</div>
</div>
);
Expand Down
13 changes: 11 additions & 2 deletions src/components/vault/FlashLoanDepositWithdrawForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default function FlashLoanDepositWithdrawForm() {
]

const [activeTab, setActiveTab] = useState<ActionType>(tabs[0]?.value || 'deposit');
const [isProcessing, setIsProcessing] = useState(false);

return (
<div className="relative rounded-lg bg-gray-50 mb-4">
Expand All @@ -19,10 +20,18 @@ export default function FlashLoanDepositWithdrawForm() {
>
{tabs.length > 1 && (
<div className="mb-3">
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} tabs={tabs} />
<Tabs
activeTab={activeTab}
setActiveTab={setActiveTab}
tabs={tabs}
isProcessing={isProcessing}
/>
</div>
)}
<FlashLoanDepositWithdrawHandler actionType={activeTab} />
<FlashLoanDepositWithdrawHandler
actionType={activeTab}
setIsProcessing={setIsProcessing}
/>
</div>
</div>
);
Expand Down
66 changes: 40 additions & 26 deletions src/components/vault/FlashLoanDepositWithdrawHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type ActionType = 'deposit' | 'withdraw';

interface FlashLoanDepositWithdrawHandlerProps {
actionType: ActionType;
setIsProcessing: React.Dispatch<React.SetStateAction<boolean>>;
}

const GAS_RESERVE_MULTIPLIER = 3n;
Expand All @@ -49,7 +50,10 @@ const MINT_SLIPPAGE_DIVIDER = 1000000;
const FLASH_LOAN_DEPOSIT_WITHDRAW_PRECISION_DIVIDEND = 99999;
const FLASH_LOAN_DEPOSIT_WITHDRAW_PRECISION_DIVIDER = 100000;

export default function FlashLoanDepositWithdrawHandler({ actionType }: FlashLoanDepositWithdrawHandlerProps) {
export default function FlashLoanDepositWithdrawHandler({
actionType,
setIsProcessing
}: FlashLoanDepositWithdrawHandlerProps) {
const [inputValue, setInputValue] = useState('');
const [estimatedShares, setEstimatedShares] = useState<bigint | null>(null);
const [wrapError, setWrapError] = useState<string>('');
Expand Down Expand Up @@ -424,37 +428,47 @@ export default function FlashLoanDepositWithdrawHandler({ actionType }: FlashLoa

setWrapError('');
setWrapSuccess('');
setIsProcessing(true);

// If using ETH input for wstETH vault, wrap ETH to wstETH first
if (useEthWrapToWSTETH && isWstETHVault && ethToWrapValue && provider && signer) {
setIsWrapping(true);
const ethAmount = parseEther(ethToWrapValue);
try {
// If using ETH input for wstETH vault, wrap ETH to wstETH first
if (useEthWrapToWSTETH && isWstETHVault && ethToWrapValue && provider && signer) {
setIsWrapping(true);
const ethAmount = parseEther(ethToWrapValue);

const wrapResult = await wrapEthToWstEth(
provider,
signer,
ethAmount,
address,
setWrapSuccess,
setWrapError
);

if (!wrapResult) {
setIsWrapping(false);
setIsProcessing(false);
return; // Error already set by wrapEthToWstEth, finally will handle processing state
}

const wrapResult = await wrapEthToWstEth(
provider,
signer,
ethAmount,
address,
setWrapSuccess,
setWrapError
);

if (!wrapResult) {
// Refresh balances to get updated wstETH balance
await refreshBalances();
setIsWrapping(false);
return; // Error already set by wrapEthToWstEth
}

Comment thread
vsevolod-zhuravlov marked this conversation as resolved.
// Refresh balances to get updated wstETH balance
await refreshBalances();
setIsWrapping(false);
}

const success = await flashLoan.execute();
const success = await flashLoan.execute();

if (success) {
setInputValue('');
setEstimatedShares(null);
setEthToWrapValue('');
if (success) {
setInputValue('');
setEstimatedShares(null);
setEthToWrapValue('');
}
} catch (err) {
console.error('Error in handling flash loan submit:', err);
} finally {
setIsProcessing(false);
// Safety check to ensure isWrapping is also disabled if something failed during wrap
setIsWrapping(false);
}
};

Expand Down
13 changes: 11 additions & 2 deletions src/components/vault/FlashLoanHelper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default function FlashLoanHelper() {

const [isOpen, setIsOpen] = useState(false);
const [activeTab, setActiveTab] = useState<HelperType>(availableTabs[0]?.value || 'mint');
const [isProcessing, setIsProcessing] = useState(false);

if (availableTabs.length === 0) {
return null;
Expand Down Expand Up @@ -54,10 +55,18 @@ export default function FlashLoanHelper() {
>
{availableTabs.length > 1 && (
<div className="mb-3">
<Tabs activeTab={activeTab} setActiveTab={setActiveTab} tabs={availableTabs} />
<Tabs
activeTab={activeTab}
setActiveTab={setActiveTab}
tabs={availableTabs}
isProcessing={isProcessing}
/>
</div>
)}
<FlashLoanHelperHandler helperType={activeTab} />
<FlashLoanHelperHandler
helperType={activeTab}
setIsProcessing={setIsProcessing}
/>
</div>
</div>
);
Expand Down
Loading