diff --git a/app/src/App.tsx b/app/src/App.tsx index 1590136..92b6c04 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -6,8 +6,9 @@ import { NetworkSelect } from './WalletContextProvider'; import { useNetwork } from './contexts/NetworkContext'; import '@solana/wallet-adapter-react-ui/styles.css'; import './style/App.css'; -import logo from '../public/favicon.png'; - +import './style/Theme.css'; +import './style/Typography.css'; +import logo from '../public/favicon.png'; const App: FC = () => { const { connected } = useWallet(); @@ -16,28 +17,26 @@ const App: FC = () => { return (
{!connected ? (
-
+
Fall Logo -

Welcome to Fall

-

Please connect your wallet to continue

+

Welcome to Fall

+

Please connect your wallet to continue

) : ( @@ -50,7 +49,7 @@ const App: FC = () => { href="https://github.com/yimingWOW/fall" target="_blank" rel="noopener noreferrer" - className="social-icon" + className="text-link" > @@ -58,7 +57,7 @@ const App: FC = () => { href="https://x.com/fall_labs" target="_blank" rel="noopener noreferrer" - className="social-icon" + className="text-link" > diff --git a/app/src/components/BorrowForm.tsx b/app/src/components/BorrowForm.tsx index 6d9c006..34eeefb 100644 --- a/app/src/components/BorrowForm.tsx +++ b/app/src/components/BorrowForm.tsx @@ -5,8 +5,9 @@ import { borrow } from '../utils/borrow'; import BN from 'bn.js'; import { PoolInfo } from '../utils/getPoolList'; import { PoolDetailInfo } from '../utils/getPoolDetail'; -import '../style/BorrowForm.css'; import { BASE_RATE } from '../utils/constants'; +import '../style/Theme.css'; +import '../style/Typography.css'; interface BorrowFormProps { pool: PoolInfo; @@ -68,25 +69,27 @@ export const BorrowForm: FC = ({ pool, details, onSuccess }) => } }; - const minCollateralRatio = (pool.minCollateralRatio / 100).toFixed(2); - return ( -
-

Borrow Tokens

+
+

Borrow Tokens

+ {error && ( -
+
{error}
)} +
-
-
-
Pool: {pool.poolPk.toString().slice(0, 4)}...{pool.poolPk.toString().slice(-4)}
-
Token A (Borrow): {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)}
-
Token B (Collateral): {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)}
-
Min Collateral Ratio: {minCollateralRatio}%
-
Base Rate: {(pool.fee / 100).toFixed(2)}%
-
-
+ +
Note: Please ensure you provide sufficient collateral based on the minimum collateral ratio.
- + +
+ +
); diff --git a/app/src/components/BorrowerPoolItem.tsx b/app/src/components/BorrowerPoolItem.tsx index f014d3f..07cac9d 100644 --- a/app/src/components/BorrowerPoolItem.tsx +++ b/app/src/components/BorrowerPoolItem.tsx @@ -6,8 +6,9 @@ import { RepayForm } from './RepayForm'; import { DepositCollateralForm } from './DepositCollateral'; import { PoolInfo } from '../utils/getPoolList'; import { PublicKey } from '@solana/web3.js'; -import '../style/BorrowerPoolItem.css'; import { BASE_RATE } from '../utils/constants'; +import '../style/Theme.css'; +import '../style/Typography.css'; interface PoolItemProps { pool: PoolInfo; @@ -42,158 +43,174 @@ export const BorrowerPoolItem: FC = ({ pool, onTxSuccess }) => { }, [pool, connection, walletPublicKey]); return ( -
- -
-

Current Prices

+
+
+

Current Prices

{isLoadingDetails ? ( -
Loading prices...
+
) : details ? ( <> -
- TokenA Amount in Liquidity Pool: - {details.poolInfo.tokenAAmount.toFixed(6)} - TokenB Amount in Liquidity Pool: - {details.poolInfo.tokenBAmount.toFixed(6)} -
-
- Price (A → B): - 1 A = {details.poolInfo.aToB.toFixed(6)} B - Price (B → A): - 1 B = {details.poolInfo.bToA.toFixed(6)} A +
+
+ + Price (A → B) + + + 1 A = {details.poolInfo.aToB.toFixed(6)} B + +
+
+ + Price (B → A) + + + 1 B = {details.poolInfo.bToA.toFixed(6)} A + +
) : ( -
Failed to load prices
+
Failed to load prices
)}
-
-

Credit Pool

+
+

Credit Pool

{isLoadingDetails ? ( -
Loading lending pool details...
+
) : details ? ( <> -
- Token A mint addr: - - {pool.mintA.toString()} - -
- -
- Credit Pool tokenA Amount: - {details.lendingPoolInfo.tokenAAmount.toFixed(6)} +
+
+ + TokenA Addr + + + {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)} + +
+
+ + TokenA Balance + + + {details.lendingPoolInfo.tokenAAmount.toFixed(6)} + +
-
- Token B mint addr: - - {pool.mintB.toString()} - -
- -
- Credit Pool tokenB Amount: - {details.lendingPoolInfo.tokenBAmount.toFixed(6)} +
+
+ + TokenB Addr + + + {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)} + +
+
+ + TokenB Balance + + + {details.lendingPoolInfo.tokenBAmount.toFixed(6)} + +
-
- Min Collateral Ratio: - - {(pool.minCollateralRatio/BASE_RATE).toFixed(6)} - +
+
+ + Min Collateral Ratio + + + {(pool.minCollateralRatio/BASE_RATE*100).toFixed(2)}% + +
- - ) : ( -
Failed to load credit pool details
+
Failed to load credit pool details
)}
-
-

Your Assets

+
+

Your Assets

{isLoadingDetails ? ( -
Loading credit pool details...
+
) : details ? ( <> -
- You have borrowed tokenA amount: - {details.userAssets.borrowReceiptAmount} -
- -
- You have deposited collateral TokenB amount: - {details.userAssets.collateralReceiptAmount} +
+
+ + Borrowed tokenA + + + {details.userAssets.borrowReceiptAmount} + +
+
+ + Collateral TokenB + + + {details.userAssets.collateralReceiptAmount} + +
) : ( -
Failed to load lending pool details
+
Failed to load lending pool details
)}
-
+
+

Actions

+ {isLoadingDetails ? ( -
Loading lending pool details...
+
) : details ? ( <> - -
-
- {showDepositCollateral && ( -
+ {showDepositCollateral ? ( { - onTxSuccess(signature); - setShowDepositCollateral(false); - fetchDetails(); - }} - /> -
- )} - - {Number(details.userAssets.borrowReceiptAmount) === 0 ? ( - { onTxSuccess(signature); fetchDetails(); }} /> ) : ( - { - onTxSuccess(signature); - fetchDetails(); - }} - /> + Number(details.userAssets.borrowReceiptAmount) === 0 ? ( + { + onTxSuccess(signature); + fetchDetails(); + }} + /> + ) : ( + { + onTxSuccess(signature); + fetchDetails(); + }} + /> + ) )} ) : ( -
Failed to load lending pool details
+
Failed to load lending pool details
)}
- -
- -
); }; \ No newline at end of file diff --git a/app/src/components/BorrowerPoolList.tsx b/app/src/components/BorrowerPoolList.tsx index 61c37cc..a28ae62 100644 --- a/app/src/components/BorrowerPoolList.tsx +++ b/app/src/components/BorrowerPoolList.tsx @@ -3,7 +3,8 @@ import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react'; import { getPoolList } from '../utils/getPoolList'; import { PoolInfo } from '../utils/getPoolList'; import { BorrowerPoolItem } from './BorrowerPoolItem'; -import '../style/BorrowerPoolList.css'; +import '../style/Theme.css'; +import '../style/Typography.css'; export const BorrowerPoolList: FC = () => { const wallet = useAnchorWallet(); @@ -32,24 +33,24 @@ export const BorrowerPoolList: FC = () => { if (isLoading) { return ( -
+
-

Loading pools...

); } if (selectedPool) { return ( -
+
+ { @@ -62,39 +63,49 @@ export const BorrowerPoolList: FC = () => { } return ( -
-
-

Available Credit Pools

+
+
+

Available Credit Pools

- Trading Pair - Pool Address - Action +
+ Trading Pair +
+
+ Pool Address +
+
+ Action +
{pools.length === 0 ? ( -
- No pools available +
+ No pools available
) : (
{pools.map((pool) => (
setSelectedPool(pool)} >
-
- {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)} - / - {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)} +
+ + {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)} + / + {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)} +
-
- {pool.poolPk.toString().slice(0, 4)}...{pool.poolPk.toString().slice(-4)} +
+ + {pool.poolPk.toString().slice(0, 4)}...{pool.poolPk.toString().slice(-4)} +
-
diff --git a/app/src/components/CreatePoolForm.tsx b/app/src/components/CreatePoolForm.tsx index 3348e0b..da81e05 100644 --- a/app/src/components/CreatePoolForm.tsx +++ b/app/src/components/CreatePoolForm.tsx @@ -3,16 +3,21 @@ import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react'; import { PublicKey } from '@solana/web3.js'; import { createPool } from '../utils/createPool'; import { useAmm } from '../contexts/AmmContext'; -import '../style/CreatePoolForm.css'; +import '../style/Theme.css'; +import '../style/Typography.css'; -export const CreatePoolForm: FC = () => { +interface CreatePoolFormProps { + onShowForm: (show: boolean) => void; + onSuccess?: (signature: string) => void; +} + +export const CreatePoolForm: FC = ({ onShowForm, onSuccess }) => { const wallet = useAnchorWallet(); const { connection } = useConnection(); const { amm } = useAmm(); const [error, setError] = useState(""); const [isLoading, setIsLoading] = useState(false); const [lastTxSignature, setLastTxSignature] = useState(""); - const [showForm, setShowForm] = useState(false); const [formData, setFormData] = useState({ mintA: '', mintB: '', @@ -53,7 +58,8 @@ export const CreatePoolForm: FC = () => { setLastTxSignature(signature.toString()); setFormData({ mintA: '', mintB: '', fee: '500' }); - setShowForm(false); + onSuccess?.(signature.toString()); + onShowForm(false); } catch (err) { console.error("Error creating pool:", err); setError(err instanceof Error ? err.message : "Failed to create pool"); @@ -62,54 +68,47 @@ export const CreatePoolForm: FC = () => { } }; - if (!showForm) { - return ( -
+ return ( +
+
- ); - } - return ( -
- -
-

Create New Pool

+
+

Create New Pool

{error && ( -
+
{error}
)} {lastTxSignature && ( -
- Pool created successfully! +
+ + Pool created successfully! + View transaction
)} -
+
- + AMM Pubkey { />
- + Token A Mint Address setFormData({...formData, mintA: e.target.value})} @@ -128,8 +128,9 @@ export const CreatePoolForm: FC = () => { />
- + Token B Mint Address setFormData({...formData, mintB: e.target.value})} @@ -139,8 +140,9 @@ export const CreatePoolForm: FC = () => { />
- + Fee (in basis points, 1-10000, default: 500 or 5%) setFormData({...formData, fee: e.target.value})} @@ -149,13 +151,15 @@ export const CreatePoolForm: FC = () => { disabled={isLoading} />
+
+ +
diff --git a/app/src/components/Dashboard.tsx b/app/src/components/Dashboard.tsx index 999bcac..13827fe 100644 --- a/app/src/components/Dashboard.tsx +++ b/app/src/components/Dashboard.tsx @@ -2,7 +2,6 @@ import { FC, useState } from 'react'; import { useWallet } from '@solana/wallet-adapter-react'; import { CreateAmmForm } from './CreateAmmForm'; import { AmmList } from './AmmList'; -import { CreatePoolForm } from './CreatePoolForm'; import { PoolList } from './PoolList'; import { LiquidateForm } from './LiquidateForm'; import { AmmProvider } from '../contexts/AmmContext'; @@ -10,7 +9,8 @@ import { LenderPoolList } from './LenderPoolList'; import { BorrowerPoolList } from './BorrowerPoolList'; import { EXCLUDED_PUBLIC_KEY } from '../utils/constants'; import { Guide } from './Guide'; -import '../style/Dashboard.css'; +import '../style/Theme.css'; +import '../style/Typography.css'; interface TabButtonProps { isActive: boolean; @@ -21,11 +21,11 @@ interface TabButtonProps { const TabButton: FC = ({ isActive, onClick, icon, label }) => ( ); @@ -34,108 +34,74 @@ const Dashboard: FC = () => { const [activeTab, setActiveTab] = useState('pool'); return ( -
- -
-
- - -
- {activeTab === 'amm' && publicKey?.toBase58() == EXCLUDED_PUBLIC_KEY ? ( -
- - -
- ) : activeTab === 'guide' ? ( -
- -
- ): activeTab === 'pool' ? ( -
-
- -
- -
- ) : activeTab === 'lenderPool' ? ( -
- -
- ) : activeTab === 'borrowerPool' ? ( -
- -
- ) : activeTab === 'liquidate' ? ( -
- -
- ) : ( - - )} -
-
-
-
-
- ); -}; - -const FutureForm: FC = () => { - return ( -
-

Coming Soon...

-
+
+ {activeTab === 'amm' && publicKey?.toBase58() == EXCLUDED_PUBLIC_KEY ? ( + <> + + + + ) : activeTab === 'guide' ? ( + + ) : activeTab === 'pool' ? ( + + ) : activeTab === 'lenderPool' ? ( + + ) : activeTab === 'borrowerPool' ? ( + + ) : activeTab === 'liquidate' ? ( + + ) : ( +
Coming Soon...
+ )} +
+ ); }; diff --git a/app/src/components/DepositCollateral.tsx b/app/src/components/DepositCollateral.tsx index 3a7b31f..6c48dfe 100644 --- a/app/src/components/DepositCollateral.tsx +++ b/app/src/components/DepositCollateral.tsx @@ -57,17 +57,18 @@ export const DepositCollateralForm: FC = ({ pool, on }; return ( -
-

DepositCollateral Tokens

+
+

Deposit Collateral

{error && ( -
+
{error}
)}
- + Amount to Deposit setAmount(e.target.value)} @@ -80,10 +81,10 @@ export const DepositCollateralForm: FC = ({ pool, on
diff --git a/app/src/components/DepositLiquidityForm.tsx b/app/src/components/DepositLiquidityForm.tsx index 8ae8ff5..c21a556 100644 --- a/app/src/components/DepositLiquidityForm.tsx +++ b/app/src/components/DepositLiquidityForm.tsx @@ -53,39 +53,48 @@ export const DepositLiquidityForm: FC = ({ }; return ( -
- {error &&
{error}
} -
- - setFormData({...formData, amountA: e.target.value})} - placeholder="Enter amount for token A" - required - disabled={isLoading} - /> -
-
- - setFormData({...formData, amountB: e.target.value})} - placeholder="Enter amount for token B" - required - disabled={isLoading} - /> -
- -
+
+

Deposit Liquidity

+ {error && ( +
+ {error} +
+ )} +
+
+ Amount Token A + setFormData({...formData, amountA: e.target.value})} + placeholder="Enter amount for token A" + required + disabled={isLoading} + /> +
+
+ Amount Token B + setFormData({...formData, amountB: e.target.value})} + placeholder="Enter amount for token B" + required + disabled={isLoading} + /> +
+ +
+
); }; \ No newline at end of file diff --git a/app/src/components/Guide.tsx b/app/src/components/Guide.tsx index a012abc..ca1a705 100644 --- a/app/src/components/Guide.tsx +++ b/app/src/components/Guide.tsx @@ -1,4 +1,5 @@ import { FC } from 'react'; +import { WrapSolForm } from './wrapSolForm'; import '../style/Guide.css'; export const Guide: FC = () => { @@ -23,14 +24,8 @@ export const Guide: FC = () => {

Setup Instructions

-

Convert SOL to WSOL

-
- spl-token wrap 1 - -
-

Note: This requires spl-token and solana-cli. Visit the Solana Docs for installation guide.

+

Convert SOL to WSOL for trading in Wsol-USDT Pool

+ {}} />
diff --git a/app/src/components/InitPoolForm.tsx b/app/src/components/InitPoolForm.tsx index 790b3c3..945b99e 100644 --- a/app/src/components/InitPoolForm.tsx +++ b/app/src/components/InitPoolForm.tsx @@ -52,18 +52,21 @@ export const InitPoolForm: FC = ({ pool, onSuccess }) => { }; return ( -
+
+

Initialize Pool

{error && ( -
+
{error}
)}
); diff --git a/app/src/components/LendForm.tsx b/app/src/components/LendForm.tsx index 944d68b..e6d1ee9 100644 --- a/app/src/components/LendForm.tsx +++ b/app/src/components/LendForm.tsx @@ -4,7 +4,8 @@ import { PublicKey } from '@solana/web3.js'; import { lend } from '../utils/lend'; import BN from 'bn.js'; import { PoolInfo } from '../utils/getPoolList'; -import '../style/LendForm.css'; +import '../style/Theme.css'; +import '../style/Typography.css'; interface LendFormProps { pool: PoolInfo; @@ -30,13 +31,11 @@ export const LendForm: FC = ({ pool, onSuccess }) => { } try { - // 验证金额 const lendAmount = parseFloat(amount); if (isNaN(lendAmount) || lendAmount <= 0) { throw new Error("Invalid amount"); } - // 从 lendingPool 对象获取必要的公钥 const poolPubkey = new PublicKey(pool.poolPk); const signature = await lend( wallet, @@ -46,7 +45,7 @@ export const LendForm: FC = ({ pool, onSuccess }) => { ); console.log(`Transaction URL: https://explorer.solana.com/tx/${signature.tx}`); - setAmount(""); // 重置表单 + setAmount(""); onSuccess(signature.tx); } catch (err) { console.error("Error lending:", err); @@ -56,19 +55,24 @@ export const LendForm: FC = ({ pool, onSuccess }) => { } }; - console.log(pool); return ( -
-

Lend TokenA

+
+

Lend Tokens

+ {error && ( -
+
{error}
)} +
- +
+ Amount to Lend (Token A): +
+ setAmount(e.target.value)} @@ -79,13 +83,22 @@ export const LendForm: FC = ({ pool, onSuccess }) => { disabled={isLoading} />
- + +
+ + Note: By lending tokens, you'll receive interest based on the pool's lending rate. + +
+ +
+ +
); diff --git a/app/src/components/LenderPoolItem.tsx b/app/src/components/LenderPoolItem.tsx index 427c331..76d7dd5 100644 --- a/app/src/components/LenderPoolItem.tsx +++ b/app/src/components/LenderPoolItem.tsx @@ -7,7 +7,8 @@ import { RedeemForm } from './RedeemForm'; import { PoolInfo } from '../utils/getPoolList'; import { PublicKey } from '@solana/web3.js'; import { InitPoolForm } from './InitPoolForm'; -import '../style/LenderPoolItem.css'; +import '../style/Theme.css'; +import '../style/Typography.css'; import { shortenAddress } from '../utils/string'; interface PoolItemProps { @@ -70,9 +71,10 @@ export const LenderPoolItem: FC = ({ pool, onTxSuccess }) => { if (!isCreditPool) { return ( -
-
-
+
+
+
Initialize Pool
+

This liquidity pool has not created a lending pool yet.

Click the button below to create one.

@@ -88,68 +90,66 @@ export const LenderPoolItem: FC = ({ pool, onTxSuccess }) => { } return ( -
-
-

Credit Pool Details

-
- -
-
- Pool Pubkey: - - {shortenAddress(pool.poolPk.toString())} - -
- -
- Token A mint addr: - - {shortenAddress(pool.mintA.toString())} - -
- -
- Credit Pool tokenA Amount: - {details?.lendingPoolInfo.tokenAAmount.toFixed(6)} +
+
+
Credit Pool Details
+
+
+ PoolPK + + {shortenAddress(pool.poolPk.toString())} + +
- -
- Token B mint addr: - - {shortenAddress(pool.mintB.toString())} - +
+
+ TokenA Addr + + {shortenAddress(pool.mintA.toString())} + +
+
+ TokenA Balance + {details?.lendingPoolInfo.tokenAAmount.toFixed(6)} +
- -
- Credit Pool tokenB Amount: - {details?.lendingPoolInfo.tokenBAmount.toFixed(6)} +
+
+ TokenB Addr + + {shortenAddress(pool.mintB.toString())} + +
+
+ TokenB Balance + {details?.lendingPoolInfo.tokenBAmount.toFixed(6)} +
-
-
-

Your Assets

+
+
Your Assets
{isLoadingDetails ? ( -
Loading credit pool details...
+
) : details ? ( - <> -
- tokenA Balance: - {details.userAssets.tokenAAmount} +
+
+ tokenA Balance + {details.userAssets.tokenAAmount}
-
- You have lent tokenA amount: - {details.userAssets.lendingReceiptAmount} +
+ You have lent tokenA + {details.userAssets.lendingReceiptAmount}
- +
) : ( -
Failed to load lending pool details
+
Failed to load credit pool details
)}
-
+
{isLoadingDetails ? ( -
Credit lending pool details...
+
) : details ? ( <> {Number(details.userAssets.lendingReceiptAmount) === 0 ? ( @@ -171,19 +171,9 @@ export const LenderPoolItem: FC = ({ pool, onTxSuccess }) => { )} ) : ( -
Failed to load credit pool details
+
Failed to load credit pool details
)}
- -
- -
); }; \ No newline at end of file diff --git a/app/src/components/LenderPoolList.tsx b/app/src/components/LenderPoolList.tsx index 8ded0b3..a41a4e7 100644 --- a/app/src/components/LenderPoolList.tsx +++ b/app/src/components/LenderPoolList.tsx @@ -3,7 +3,8 @@ import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react'; import { getPoolList } from '../utils/getPoolList'; import { PoolInfo } from '../utils/getPoolList'; import { LenderPoolItem } from './LenderPoolItem'; -import '../style/LenderPoolList.css'; +import '../style/Theme.css'; +import '../style/Typography.css'; export const LenderPoolList: FC = () => { const wallet = useAnchorWallet(); @@ -32,22 +33,21 @@ export const LenderPoolList: FC = () => { if (isLoading) { return ( -
+
-

Loading pools...

); } if (selectedPool) { return ( -
+
{ ); } - return ( -
-
-

Available Credit Pools

+
+
+
Available Credit Pools
- Trading Pair - Pool Address - Action + Trading Pair + Pool Address + Action
{pools.length === 0 ? ( -
- No pools available +
+ No pools available
) : (
{pools.map((pool) => (
setSelectedPool(pool)} >
-
- {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)} - / - {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)} +
+ + {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)} + / + {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)} +
-
- {pool.poolPk.toString().slice(0, 4)}...{pool.poolPk.toString().slice(-4)} +
+ + {pool.poolPk.toString().slice(0, 4)}...{pool.poolPk.toString().slice(-4)} +
-
diff --git a/app/src/components/LiquidateForm.tsx b/app/src/components/LiquidateForm.tsx index 297b091..ae0d62e 100644 --- a/app/src/components/LiquidateForm.tsx +++ b/app/src/components/LiquidateForm.tsx @@ -3,7 +3,8 @@ import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react'; import { PublicKey } from '@solana/web3.js'; import { liquidate } from '../utils/liquidate'; import { getPendingLiquidation } from '../utils/getPendingLiquidation'; -import '../styles/LiquidateForm.css'; +import '../style/Theme.css'; +import '../style/Typography.css'; export const LiquidateForm: FC = () => { const wallet = useAnchorWallet(); @@ -15,9 +16,7 @@ export const LiquidateForm: FC = () => { const [isLoadingList, setIsLoadingList] = useState(false); const [processingLiquidation, setProcessingLiquidation] = useState(null); - const handleSearch = async (e: React.FormEvent) => { - e.preventDefault(); if (!wallet || !poolAddress) { setError("Please connect wallet and enter pool address"); @@ -39,7 +38,6 @@ export const LiquidateForm: FC = () => { } }; - const handleLiquidatePosition = async (borrowerKey: PublicKey) => { setError(""); setProcessingLiquidation(borrowerKey.toString()); @@ -80,73 +78,94 @@ export const LiquidateForm: FC = () => { }; return ( -
- {error && ( -
- {error} -
- )} - {lastTxSignature && ( -
- Liquidation successful! - - View transaction - -
- )} +
+
+

Liquidate Positions

+ + {error && ( +
+ {error} +
+ )} + + {lastTxSignature && ( +
+ Liquidation successful! + + View transaction + +
+ )} -
- -
+
+ + Pool PublicKey (Could get from Lend page) + +
+ +
setPoolAddress(e.target.value)} placeholder="Enter pool address" - className="input-field" />
- -
-

Pending Bankrupts

+
{isLoadingList ? ( -
Loading pending Bankrupts...
+
+
+ + Loading pending Bankrupts... + +
) : pendingLiquidations.length === 0 ? ( -

No pending Bankrupts found

+
+ + No pending Bankrupts found + +
) : ( -
    +
    +

    Pending Bankrupts

    {pendingLiquidations.map((item, index) => ( -
  • -
    {item.toString()}
    - -
  • +
    +
    + {item.toString()} +
    +
    + +
    +
    ))} -
+
)}
- ); }; \ No newline at end of file diff --git a/app/src/components/PoolItem.tsx b/app/src/components/PoolItem.tsx index a18878d..6498c79 100644 --- a/app/src/components/PoolItem.tsx +++ b/app/src/components/PoolItem.tsx @@ -38,35 +38,37 @@ export const PoolItem: FC = ({ pool, onTxSuccess }) => { }, [pool, connection, walletPublicKey]); return ( -
-
- { - onTxSuccess(signature); - fetchDetails(); - }} - /> -
- -
+
+
{isLoadingDetails ? ( -
Loading prices...
+
Loading prices...
) : details ? ( <> -
- Price (A → B): - 1 A = {details.poolInfo.aToB.toFixed(6)} B +
+ Price (A → B) + 1 A = {details.poolInfo.aToB.toFixed(6)} B
-
- Price (B → A): - 1 B = {details.poolInfo.bToA.toFixed(6)} A +
+ Price (B → A) + 1 B = {details.poolInfo.bToA.toFixed(6)} A
) : ( -
Failed to load prices
+
+ Failed to load prices +
)}
+ +
+ { + onTxSuccess(signature); + fetchDetails(); + }} + /> +
); }; \ No newline at end of file diff --git a/app/src/components/PoolList.tsx b/app/src/components/PoolList.tsx index 80cd6b4..9ce5db6 100644 --- a/app/src/components/PoolList.tsx +++ b/app/src/components/PoolList.tsx @@ -1,9 +1,10 @@ import { FC, useState, useEffect } from 'react'; -import { useConnection } from '@solana/wallet-adapter-react'; +import { useConnection, useAnchorWallet } from '@solana/wallet-adapter-react'; import { PoolInfo, getPoolList } from '../utils/getPoolList'; import { PoolItem } from './PoolItem'; -import { useAnchorWallet } from '@solana/wallet-adapter-react'; -import '../style/PoolList.css'; +import { CreatePoolForm } from './CreatePoolForm'; +import '../style/Theme.css'; +import '../style/Typography.css'; export const PoolList: FC = () => { const wallet = useAnchorWallet(); @@ -11,12 +12,13 @@ export const PoolList: FC = () => { const [pools, setPools] = useState([]); const [isLoading, setIsLoading] = useState(true); const [selectedPool, setSelectedPool] = useState(null); + const [showCreateForm, setShowCreateForm] = useState(false); const [lastTxSignature, setLastTxSignature] = useState(''); const fetchPools = async () => { try { setIsLoading(true); - const poolList = await getPoolList( wallet, connection); + const poolList = await getPoolList(wallet, connection); setPools(poolList); } catch (error) { console.error('Error fetching pools:', error); @@ -27,26 +29,37 @@ export const PoolList: FC = () => { useEffect(() => { fetchPools(); - }, [connection, lastTxSignature]); + }, [connection, wallet, lastTxSignature]); if (isLoading) { return ( -
+
-

Loading pools...

); } + if (showCreateForm) { + return ( + { + setLastTxSignature(signature); + setShowCreateForm(false); + }} + /> + ); + } + if (selectedPool) { return ( -
+
{ } return ( -
-
-

Available Pools

+
+
+
+

Available Pools

+ +
+
- Trading Pair - Pool Address - Action + Trading Pair + Pool Address + Action
+ {pools.length === 0 ? ( -
- No pools available +
+ No pools available
) : (
{pools.map((pool) => (
setSelectedPool(pool)} >
-
- {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)} - / - {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)} +
+ + {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)} + / + {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)} +
-
- {pool.poolPk.toString().slice(0, 4)}...{pool.poolPk.toString().slice(-4)} +
+ + {pool.poolPk.toString().slice(0, 4)}...{pool.poolPk.toString().slice(-4)} +
-
diff --git a/app/src/components/RedeemForm.tsx b/app/src/components/RedeemForm.tsx index 5b4af76..0bb18c8 100644 --- a/app/src/components/RedeemForm.tsx +++ b/app/src/components/RedeemForm.tsx @@ -3,6 +3,8 @@ import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react'; import { PublicKey } from '@solana/web3.js'; import { redeem } from '../utils/redeem'; import { PoolInfo } from '../utils/getPoolList'; +import '../style/Theme.css'; +import '../style/Typography.css'; interface RedeemFormProps { pool: PoolInfo; @@ -27,9 +29,7 @@ export const RedeemForm: FC = ({ pool, onSuccess }) => { } try { - // 从 lendingPool 对象获取必要的公钥 const poolPubkey = new PublicKey(pool.poolPk); - const signature = await redeem( wallet, connection, @@ -47,24 +47,29 @@ export const RedeemForm: FC = ({ pool, onSuccess }) => { }; return ( -
-

Redeem Tokens

+
+

Redeem Tokens

+ {error && ( -
+
{error}
)} +
-
+
Note: This will redeem all your lending receipt and return your tokenA.
- + +
+ +
); diff --git a/app/src/components/RepayForm.tsx b/app/src/components/RepayForm.tsx index 741ab25..3d9b137 100644 --- a/app/src/components/RepayForm.tsx +++ b/app/src/components/RepayForm.tsx @@ -3,6 +3,8 @@ import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react'; import { PublicKey } from '@solana/web3.js'; import { repay } from '../utils/repay'; import { PoolInfo } from '../utils/getPoolList'; +import '../style/Theme.css'; +import '../style/Typography.css'; interface RepayFormProps { pool: PoolInfo; @@ -48,27 +50,29 @@ export const RepayForm: FC = ({ pool, onSuccess }) => { }; return ( -
-

Repay Loan

+
+

Repay Loan

+ {error && ( -
+
{error}
)} +
-
-
- Repaying your loan will return your collateral proportionally. -
+
+ Repaying your loan will return your collateral proportionally.
- +
+ +
); diff --git a/app/src/components/wrapSolForm.tsx b/app/src/components/wrapSolForm.tsx new file mode 100644 index 0000000..e5695b2 --- /dev/null +++ b/app/src/components/wrapSolForm.tsx @@ -0,0 +1,93 @@ +import { FC, useState } from 'react'; +import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react'; +import { wrapSol } from '../utils/wrapSol'; +import '../style/Theme.css'; +import '../style/Typography.css'; + +interface WrapSolFormProps { + onSuccess: (signature: string) => void; +} + +export const WrapSolForm: FC = ({ onSuccess }) => { + const wallet = useAnchorWallet(); + const { connection } = useConnection(); + const [error, setError] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const [amount, setAmount] = useState(""); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + setIsLoading(true); + + if (!wallet) { + setError("Please connect your wallet first"); + setIsLoading(false); + return; + } + + if (!amount || isNaN(Number(amount)) || Number(amount) <= 0) { + setError("Please enter a valid amount"); + setIsLoading(false); + return; + } + + try { + const signature = await wrapSol( + connection, + wallet, + Number(amount) + ); + console.log(`Transaction URL: https://explorer.solana.com/tx/${signature}`); + onSuccess(signature); + setAmount(""); // Reset form after success + } catch (err) { + console.error("Error wrapping SOL:", err); + setError(err instanceof Error ? err.message : "Failed to wrap SOL"); + } finally { + setIsLoading(false); + } + }; + + return ( +
+

Wrap SOL

+ + {error && ( +
+ {error} +
+ )} + +
+
+ setAmount(e.target.value)} + placeholder="Enter SOL amount" + step="any" + min="0" + className="input-field" + style={{ + width: '100%', + padding: '8px', + borderRadius: '4px', + border: '1px solid var(--border-color)' + }} + /> +
+ +
+ +
+
+
+ ); +}; diff --git a/app/src/style/App.css b/app/src/style/App.css index 9bc30bd..60d1877 100644 --- a/app/src/style/App.css +++ b/app/src/style/App.css @@ -8,80 +8,37 @@ } .app-nav { - width: 100%; - height: 72px; - background: rgba(255, 255, 255, 0.05); - backdrop-filter: blur(10px); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - position: fixed; - top: 0; - left: 0; - z-index: 100; -} - -.nav-content { - max-width: 1200px; - margin: 0 auto; - padding: 1rem 2rem; + position: fixed; + top: 0; + left: 0; + right: 0; + height: 72px; + padding: 0 2rem; + background: rgba(255, 255, 255, 0.05); + backdrop-filter: blur(10px); + border-bottom: 1px solid rgba(255, 255, 255, 0.1); display: flex; justify-content: space-between; align-items: center; + z-index: 100; } -.nav-left { +.brand { display: flex; align-items: center; + gap: 12px; } -.app-title { - font-size: 1.5rem; - font-weight: 600; - background: linear-gradient(45deg, #7A4FF3, #9B6FFF); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - margin: 0; -} - - -.network-select { - appearance: none; - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 12px; - color: white; - padding: 0.5rem 2rem 0.5rem 1rem; - font-size: 0.9rem; - cursor: pointer; - transition: all 0.2s ease; - min-width: 140px; - position: relative; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 0.7rem center; -} - -.network-select:hover { - background-color: rgba(255, 255, 255, 0.1); -} - -.network-select option { - background: #1C1C28; - color: white; - padding: 8px; -} - - -.wallet-button { - background: #7A4FF3 !important; - border-radius: 12px !important; - padding: 0.5rem 1.5rem !important; - height: auto !important; - transition: all 0.2s ease !important; +.controls { + display: flex; + align-items: center; + gap: 1rem; } -.wallet-button:hover { - background: #8A5FF3 !important; - opacity: 0.9; +.app-logo { + height: 64px; + width: auto; + display: block; } /* 主内容区域 */ @@ -151,17 +108,6 @@ } } -.app-logo { - height: 32px; /* 调整大小 */ - width: auto; - margin-right: 12px; -} - -.nav-left { - display: flex; - align-items: center; -} - .connect-logo { width: 64px; /* 调整大小 */ height: auto; @@ -177,7 +123,7 @@ .social-icon { color: #ffffff; - font-size: 1.2rem; + font-size: 1.5rem; transition: color 0.2s ease; } @@ -205,4 +151,9 @@ .social-icon:hover { color: #9945FF; +} + +.fall-title { + margin: 0; + line-height: 1; } \ No newline at end of file diff --git a/app/src/style/BorrowForm.css b/app/src/style/BorrowForm.css deleted file mode 100644 index f795388..0000000 --- a/app/src/style/BorrowForm.css +++ /dev/null @@ -1,124 +0,0 @@ -.form-wrapper { - background: rgba(30, 35, 45, 0.5); - border-radius: 12px; - padding: 24px; - margin-top: 16px; - width: 100%; - box-sizing: border-box; - } - - .form-wrapper h3 { - color: #fff; - margin: 0 0 20px 0; - font-size: 18px; - text-align: center; - } - - .form-group { - margin-bottom: 20px; - width: 100%; - } - - .form-group label { - display: flex; - justify-content: space-between; - align-items: center; - color: #a7a7a7; - margin-bottom: 8px; - font-size: 14px; - } - - .form-group input { - width: 100%; - box-sizing: border-box; /* 添加这行确保input不会溢出 */ - background: rgba(40, 45, 55, 0.5); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 8px; - padding: 12px; - color: #fff; - font-size: 16px; - outline: none; - transition: all 0.2s; - } - - .form-group input:focus { - border-color: rgb(111, 76, 255); - box-shadow: 0 0 0 2px rgba(111, 76, 255, 0.2); - } - - .form-group input:disabled { - opacity: 0.5; - cursor: not-allowed; - } - - .lending-pool-info-summary { - background: rgba(40, 45, 55, 0.3); - border-radius: 8px; - padding: 16px; - margin-bottom: 20px; - font-size: 14px; - color: #a7a7a7; - } - - .lending-pool-info-summary div { - margin-bottom: 8px; - } - - .lending-pool-info-summary div:last-child { - margin-bottom: 0; - } - - .warning-message { - background: rgba(255, 183, 77, 0.1); - border: 1px solid rgba(255, 183, 77, 0.3); - color: #ffb74d; - padding: 12px; - border-radius: 8px; - margin-bottom: 20px; - font-size: 14px; - } - - .error-message { - background: rgba(255, 76, 76, 0.1); - border: 1px solid rgba(255, 76, 76, 0.3); - color: #ff4c4c; - padding: 12px; - border-radius: 8px; - margin-bottom: 16px; - font-size: 14px; - } - - .action-button { - width: 100%; - background: rgb(111, 76, 255); - color: white; - border: none; - border-radius: 20px; - padding: 12px; - font-size: 16px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; - } - - .action-button:hover:not(:disabled) { - background: rgb(124, 93, 255); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(111, 76, 255, 0.2); - } - - .action-button:active:not(:disabled) { - transform: translateY(0); - box-shadow: none; - } - - .action-button:disabled { - background: rgba(111, 76, 255, 0.5); - cursor: not-allowed; - } - - .max-amount { - color: #6f4cff; - font-size: 14px; - font-family: monospace; - } \ No newline at end of file diff --git a/app/src/style/BorrowerPoolItem.css b/app/src/style/BorrowerPoolItem.css deleted file mode 100644 index 79a2cdb..0000000 --- a/app/src/style/BorrowerPoolItem.css +++ /dev/null @@ -1,156 +0,0 @@ -.pool-item { - background: rgba(30, 35, 45, 0.5); - border-radius: 12px; - padding: 24px; - margin-top: 24px; - width: 100%; - box-sizing: border-box; - max-width: 100%; - overflow: hidden; - } - - .pool-header { - margin-bottom: 24px; - } - - .pool-header h3 { - color: #fff; - margin: 0; - font-size: 20px; - text-align: center; - } - - .pool-info, - .pool-price-info, - .lending-pool-info, - .user-assets-details, - .user-lending-details { - margin-bottom: 24px; - width: 100%; - box-sizing: border-box; - overflow: hidden; - } - - .pool-info h4, - .pool-price-info h4, - .lending-pool-info h4, - .user-assets-details h4, - .user-lending-details h4 { - color: #fff; - margin: 0 0 16px 0; - font-size: 16px; - } - - .pool-details { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px; - background: rgba(40, 45, 55, 0.3); - border-radius: 8px; - margin-bottom: 8px; - width: 100%; - box-sizing: border-box; - overflow: hidden; - } - - .pool-label { - color: #a7a7a7; - font-size: 14px; - } - - .pool-value { - color: #fff; - font-family: monospace; - font-size: 14px; - word-break: break-all; - max-width: 60%; - overflow: hidden; - text-overflow: ellipsis; - } - - .loading-prices, - .loading-lending-pool-details, - .error-message { - text-align: center; - padding: 16px; - color: #a7a7a7; - background: rgba(40, 45, 55, 0.3); - border-radius: 8px; - } - - .error-message { - color: #ff4c4c; - background: rgba(255, 76, 76, 0.1); - border: 1px solid rgba(255, 76, 76, 0.3); - } - - .lending-pool-actions { - display: flex; - flex-direction: column; - gap: 16px; - align-items: flex-end; - } - - .lending-pool-action-buttons { - display: flex; - gap: 12px; - justify-content: flex-end; - flex-wrap: wrap; - width: 100%; - } - - .lending-pool-action-button { - background: rgb(111, 76, 255); - color: white; - border: none; - border-radius: 20px; - padding: 12px 24px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; - } - - .lending-pool-action-button:hover:not(:disabled) { - background: rgb(124, 93, 255); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(111, 76, 255, 0.2); - } - - .lending-pool-action-button:active:not(:disabled) { - transform: translateY(0); - box-shadow: none; - } - - .lending-pool-action-button:disabled { - background: rgba(111, 76, 255, 0.5); - cursor: not-allowed; - } - - .lending-pool-refresh-button { - background: rgba(40, 45, 55, 0.5); - color: #fff; - border: none; - border-radius: 20px; - padding: 8px 16px; - font-size: 14px; - cursor: pointer; - transition: all 0.2s; - } - - .lending-pool-refresh-button:hover:not(:disabled) { - background: rgba(50, 55, 65, 0.5); - } - - .lending-pool-refresh-button:disabled { - opacity: 0.5; - cursor: not-allowed; - } - - .form-container { - margin-top: 20px; - width: 100%; - box-sizing: border-box; - overflow: hidden; - } \ No newline at end of file diff --git a/app/src/style/BorrowerPoolList.css b/app/src/style/BorrowerPoolList.css deleted file mode 100644 index 24d6651..0000000 --- a/app/src/style/BorrowerPoolList.css +++ /dev/null @@ -1,210 +0,0 @@ -.pool-list-container { - padding: 20px; - background: #1a1b23; - border-radius: 12px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - max-width: 1200px; - margin: 0 auto; - width: 100%; - box-sizing: border-box; -} - -.pool-list-header { - margin-bottom: 20px; -} - -.pool-list-title { - font-size: 1.5rem; - font-weight: 600; - margin-bottom: 16px; - color: #ffffff; -} - -.pool-list-columns { - display: grid; - grid-template-columns: 2fr 2fr 1fr; - padding: 12px 20px; - background: #23242f; - border-radius: 8px; - margin-bottom: 8px; - font-size: 0.9rem; - color: #8f90a0; -} - -.pool-list { - display: flex; - flex-direction: column; - gap: 8px; -} - -.pool-list-item { - background: #23242f; - border-radius: 8px; - transition: all 0.2s ease; - cursor: pointer; -} - -.pool-list-item:hover { - background: #2a2b38; - border-left: 3px solid #ed3a7c; /* 为借款人列表添加特殊的边框颜色 */ -} - -.pool-item-content { - display: grid; - grid-template-columns: 2fr 2fr 1fr; - padding: 12px 20px; - align-items: center; - gap: 16px; - min-width: 0; -} - -.pool-pair { - display: flex; - align-items: center; - gap: 8px; - font-family: monospace; - color: #ffffff; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; -} - -.pool-pair .separator { - color: #8f90a0; -} - -.pool-address { - font-family: monospace; - color: #8f90a0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; -} - -.view-details-btn { - background: #7c3aed; - color: white; - border: none; - border-radius: 6px; - padding: 6px 12px; - font-size: 0.9rem; - cursor: pointer; - transition: background 0.2s ease; - justify-self: end; -} - -.view-details-btn:hover { - background: #d62d6b; /* 按钮悬停颜色 */ -} - -/* Loading state */ -.loading-spinner { - border: 3px solid #23242f; - border-top: 3px solid #ed3a7c; /* 使用借款人主题色 */ - border-radius: 50%; - width: 24px; - height: 24px; - animation: spin 1s linear infinite; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -.pool-list-loading { - display: flex; - flex-direction: column; - align-items: center; - gap: 12px; - padding: 40px; - color: #8f90a0; -} - -.no-pools-message { - text-align: center; - padding: 40px; - color: #8f90a0; -} - -/* Back button styles */ -.back-button { - width: 100%; - background: #7A4FF3; - color: white; - border: none; - border-radius: 12px; - padding: 1rem; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - margin-top: 1rem; - position: relative; - bottom: 0; - font-size: 14px; - font-family: inherit; - line-height: 1.5; -} - -.back-button:hover:not(:disabled) { - background: #6340C4; - transform: translateY(-2px); -} - -/* Pool detail view */ -.pool-detail-view { - padding: 20px; - background: #1a1b23; - border-radius: 12px; - max-width: 1200px; - margin: 0 auto; - width: 100%; - box-sizing: border-box; - display: flex; - flex-direction: column; - align-items: flex-start; -} - -.back-button-container { - width: 100%; - padding: 0 0 20px 0; - display: flex; - align-items: center; - justify-content: flex-start; -} - -.pool-detail-view .back-button-container { - width: 100%; - padding: 0 0 20px 0; - display: flex; - align-items: center; - justify-content: flex-start; -} - -.pool-detail-view .back-button-container .back-button { - width: auto; - min-width: 120px; - background: #7A4FF3 !important; - color: #FFFFFF !important; - border: none; - border-radius: 12px; - padding: 1rem 2rem; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - margin-top: 1rem; - position: relative; - bottom: 0; - font-size: 14px; - font-family: inherit; - line-height: 1.5; -} - -.pool-detail-view .back-button-container .back-button:hover:not(:disabled) { - background: #6340C4 !important; - transform: translateY(-2px); -} - - diff --git a/app/src/style/CreatePoolForm.css b/app/src/style/CreatePoolForm.css deleted file mode 100644 index c442ade..0000000 --- a/app/src/style/CreatePoolForm.css +++ /dev/null @@ -1,208 +0,0 @@ -.create-pool-button-wrapper { - position: absolute; - top: 2rem; - right: 2rem; - z-index: 10; -} - -.create-pool-button { - background: linear-gradient(135deg, #7A4FF3, #6340C4); - color: white; - border: none; - padding: 0.75rem 1.5rem; - border-radius: 12px; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - box-shadow: 0 4px 12px rgba(122, 79, 243, 0.2); - white-space: nowrap; -} - -.button-content { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.plus-icon { - font-size: 1.2rem; - font-weight: bold; - display: flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - background: rgba(255, 255, 255, 0.2); - border-radius: 50%; -} - -.button-text { - font-size: 0.95rem; - font-weight: 500; -} - -.create-pool-button:hover { - background: linear-gradient(135deg, #6340C4, #5233A3); - transform: translateY(-2px); - box-shadow: 0 6px 16px rgba(122, 79, 243, 0.3); -} - -.create-pool-button:active { - transform: translateY(0); - box-shadow: 0 2px 8px rgba(122, 79, 243, 0.2); -} - -.create-pool-form-container { - position: absolute; - inset: 0; - background: #1A1B23; - display: flex; - flex-direction: column; - align-items: center; - z-index: 50; - padding: 2rem; - border-radius: 20px; - overflow-y: auto; - height: auto; - min-height: 100%; -} - -.create-pool-form { - width: 100%; - max-width: 560px; - margin: 0 auto; - padding: 0 0 2rem 0; - background: transparent; - border: none; - box-shadow: none; - display: flex; - flex-direction: column; - gap: 1.5rem; -} - -.back-button { - background: transparent; - padding: 8px 16px; - border-radius: 6px; - cursor: pointer; - margin-bottom: 20px; - transition: all 0.2s ease; - border: 1px solid #7c3aed; - color: #7c3aed; - display: block; - text-align: left; - width: fit-content; -} - -.back-button:hover { - color: white; - background: rgba(255, 255, 255, 0.15); -} - -.form-title { - text-align: center; - color: white; - font-size: 1.5rem; - font-weight: 600; - margin: 1rem 0 2rem; -} - -.form-group { - margin-bottom: 0; -} - -.form-group label { - display: block; - color: rgba(255, 255, 255, 0.8); - margin-bottom: 0.5rem; - font-size: 0.9rem; -} - -.form-group input { - width: 100%; - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 12px; - padding: 0.75rem 1rem; - color: white; - font-size: 0.9rem; - transition: all 0.3s ease; -} - -.form-group input:focus { - outline: none; - border-color: #7A4FF3; - background: rgba(122, 79, 243, 0.1); -} - -.form-group input:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.submit-button { - width: 100%; - background: #7A4FF3; - color: white; - border: none; - border-radius: 12px; - padding: 1rem; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - margin-top: 1rem; - position: relative; - bottom: 0; -} - -.submit-button:hover:not(:disabled) { - background: #6340C4; - transform: translateY(-2px); -} - -.submit-button:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.error-message { - background: rgba(255, 59, 48, 0.1); - color: #FF3B30; - padding: 1rem; - border-radius: 12px; - margin-bottom: 1.5rem; - font-size: 0.9rem; -} - -.success-message { - background: rgba(52, 199, 89, 0.1); - color: #34C759; - padding: 1rem; - border-radius: 12px; - margin-bottom: 1.5rem; - font-size: 0.9rem; -} - -.success-message a { - color: #34C759; - text-decoration: underline; - margin-left: 0.5rem; -} - -.pool-list-title { - position: relative; - z-index: 20; - padding-left: 8rem; -} - -@media (max-width: 768px) { - .create-pool-form { - padding: 1.5rem; - margin: 1rem; - } - - .back-button { - top: 1rem; - left: 1rem; - } -} \ No newline at end of file diff --git a/app/src/style/Dashboard.css b/app/src/style/Dashboard.css deleted file mode 100644 index fa8d549..0000000 --- a/app/src/style/Dashboard.css +++ /dev/null @@ -1,235 +0,0 @@ -.dashboard-wrapper { - min-height: calc(100vh - 72px); - width: 100%; - background: #1C1C28; - overflow-y: auto; - position: relative; -} - -.dashboard-container { - max-width: 1200px; - margin: 0 auto; - padding: 2rem; -} - -.dashboard-content { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; -} - -/* 导航标签容器 */ -.navigation-tabs { - display: flex; - justify-content: center; - align-items: center; - gap: 0.75rem; - margin-bottom: 2rem; - padding: 0.75rem; - background: rgba(255, 255, 255, 0.03); - border-radius: 20px; - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.08); - box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15); - width: fit-content; -} - -/* 导航标签按钮 */ -.nav-tab { - padding: 12px 24px; - border-radius: 16px; - border: none; - background: transparent; - color: rgba(255, 255, 255, 0.6); - font-weight: 500; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - display: flex; - align-items: center; - justify-content: center; - gap: 0.75rem; - font-size: 0.95rem; - position: relative; - overflow: hidden; - min-width: 120px; -} - -/* 标签图标 */ -.tab-icon { - font-size: 1.2rem; - display: flex; - align-items: center; - justify-content: center; - transition: transform 0.3s ease; -} - -/* 标签文本 */ -.tab-label { - font-weight: 600; - letter-spacing: 0.3px; - white-space: nowrap; -} - -/* 悬停效果 */ -.nav-tab:hover { - color: rgba(255, 255, 255, 0.9); - background: rgba(255, 255, 255, 0.07); - transform: translateY(-1px); -} - -.nav-tab:hover .tab-icon { - transform: scale(1.1); -} - -/* 活跃状态 */ -.nav-tab.active { - background: linear-gradient(135deg, #7A4FF3, #9945FF); - color: white; - box-shadow: 0 4px 15px rgba(122, 79, 243, 0.3); - border: 1px solid rgba(255, 255, 255, 0.1); -} - -.nav-tab.active::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(45deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0)); - border-radius: inherit; -} - -/* 特定标签的颜色 */ -.nav-tab[data-tab="amm"] .tab-icon { - color: #FF9800; -} - -.nav-tab[data-tab="pool"] .tab-icon { - color: #2196F3; -} - -.nav-tab[data-tab="lenderPool"] .tab-icon { - color: #4CAF50; -} - -.nav-tab[data-tab="borrowerPool"] .tab-icon { - color: #E91E63; -} - -.nav-tab[data-tab="liquidate"] .tab-icon { - color: #FF5722; -} - -/* 活跃状态下图标颜色统一为白色 */ -.nav-tab.active .tab-icon { - color: white; -} - -/* 内容区域 */ -.tab-content { - background: rgba(255, 255, 255, 0.05); - border-radius: 24px; - padding: 2rem; - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); - width: 100%; - min-height: 400px; - box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); -} - -/* 加载状态 */ -.loading-state { - display: flex; - justify-content: center; - align-items: center; - min-height: 200px; - color: rgba(255, 255, 255, 0.6); -} - -/* 空状态 */ -.empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - min-height: 200px; - padding: 2rem; - text-align: center; - color: rgba(255, 255, 255, 0.6); -} - -.empty-state-icon { - font-size: 2.5rem; - margin-bottom: 1rem; - opacity: 0.5; -} - -.empty-state-text { - font-size: 1.1rem; - margin-bottom: 0.5rem; -} - -.empty-state-subtext { - font-size: 0.9rem; - opacity: 0.7; -} - -/* 响应式设计 */ -@media (max-width: 768px) { - .navigation-tabs { - padding: 0.5rem; - gap: 0.5rem; - flex-wrap: wrap; - width: 100%; - } - - .nav-tab { - flex: 1 1 calc(33.33% - 0.5rem); - padding: 10px 16px; - min-width: auto; - } - - .tab-content { - padding: 1.5rem; - } -} - -@media (max-width: 480px) { - .dashboard-container { - padding: 1rem; - } - - .nav-tab { - flex: 1 1 calc(50% - 0.5rem); - } - - .tab-icon { - font-size: 1.1rem; - } - - .tab-label { - font-size: 0.9rem; - } - - .tab-content { - padding: 1rem; - } -} - -/* 动画效果 */ -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.tab-content { - animation: fadeIn 0.3s ease-out; -} \ No newline at end of file diff --git a/app/src/style/LendForm.css b/app/src/style/LendForm.css deleted file mode 100644 index a1215c5..0000000 --- a/app/src/style/LendForm.css +++ /dev/null @@ -1,89 +0,0 @@ -.form-wrapper { - background: rgba(30, 35, 45, 0.5); - border-radius: 12px; - padding: 24px; - margin-top: 16px; -} - -.form-wrapper h3 { - color: #fff; - margin: 0 0 20px 0; - font-size: 18px; - text-align: center; -} - -.error-message { - background: rgba(255, 76, 76, 0.1); - border: 1px solid rgba(255, 76, 76, 0.3); - color: #ff4c4c; - padding: 12px; - border-radius: 8px; - margin-bottom: 16px; - font-size: 14px; -} - -.form-group { - margin-bottom: 20px; -} - -.form-group label { - display: block; - color: #a7a7a7; - margin-bottom: 8px; - font-size: 14px; -} - -.form-group input { - width: 100%; - background: rgba(40, 45, 55, 0.5); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 8px; - padding: 12px; - color: #fff; - font-size: 16px; - outline: none; - transition: all 0.2s; -} - -.form-group input:focus { - border-color: rgb(111, 76, 255); - box-shadow: 0 0 0 2px rgba(111, 76, 255, 0.2); -} - -.form-group input:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.form-group input::placeholder { - color: rgba(167, 167, 167, 0.5); -} - -button.action-button { - width: 100%; - background: rgb(111, 76, 255); - color: white; - border: none; - border-radius: 20px; - padding: 12px; - font-size: 16px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; -} - -button.action-button:hover:not(:disabled) { - background: rgb(124, 93, 255); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(111, 76, 255, 0.2); -} - -button.action-button:active:not(:disabled) { - transform: translateY(0); - box-shadow: none; -} - -button.action-button:disabled { - background: rgba(111, 76, 255, 0.5); - cursor: not-allowed; -} \ No newline at end of file diff --git a/app/src/style/LenderPoolItem.css b/app/src/style/LenderPoolItem.css deleted file mode 100644 index dc3cf5b..0000000 --- a/app/src/style/LenderPoolItem.css +++ /dev/null @@ -1,81 +0,0 @@ -.pool-item { - margin-top: 24px; /* 增加与返回按钮的距离 */ - background: rgba(30, 35, 45, 0.5); - border-radius: 12px; - padding: 20px; - width: 100%; /* 添加宽度控制 */ - box-sizing: border-box; /* 确保padding不会导致溢出 */ - max-width: 100%; /* 确保不会超过父容器 */ - overflow: hidden; /* 防止内容溢出 */ -} - -.pool-header { - margin-bottom: 20px; -} - -.pool-header h3 { - margin: 0; - font-size: 18px; - color: #fff; - text-align: center; -} - -.pool-info { - display: flex; - flex-direction: column; - gap: 12px; - margin-bottom: 24px; - width: 100%; - box-sizing: border-box; -} - -.pool-details { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 12px; - background: rgba(40, 45, 55, 0.3); - border-radius: 8px; - width: 100%; - box-sizing: border-box; - overflow: hidden; -} - -.pool-label { - color: #a7a7a7; - font-size: 14px; -} - -.pool-value { - color: #fff; - font-family: monospace; - font-size: 14px; -} - -.lending-pool-info, -.user-assets-details, -.user-lending-details { - margin-top: 24px; -} - -.lending-pool-actions { - margin-top: 24px; - display: flex; - justify-content: center; - gap: 12px; -} - -.lending-pool-action-button { - background: rgb(111, 76, 255); - color: white; - border: none; - border-radius: 20px; - padding: 12px 24px; - cursor: pointer; - transition: all 0.2s; -} - -.lending-pool-action-button:hover { - background: rgb(124, 93, 255); - transform: translateY(-1px); -} \ No newline at end of file diff --git a/app/src/style/LenderPoolList.css b/app/src/style/LenderPoolList.css deleted file mode 100644 index 6ac1a05..0000000 --- a/app/src/style/LenderPoolList.css +++ /dev/null @@ -1,173 +0,0 @@ -.pool-list-container { - padding: 20px; - background: #1a1b23; - border-radius: 12px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - max-width: 1200px; - margin: 0 auto; - width: 100%; - box-sizing: border-box; -} - -.pool-list-header { - margin-bottom: 20px; -} - -.pool-list-title { - font-size: 1.5rem; - font-weight: 600; - margin-bottom: 16px; - color: #ffffff; -} - -.pool-list-columns { - display: grid; - grid-template-columns: 2fr 2fr 1fr; - padding: 12px 20px; - background: #23242f; - border-radius: 8px; - margin-bottom: 8px; - font-size: 0.9rem; - color: #8f90a0; -} - -.pool-list { - display: flex; - flex-direction: column; - gap: 8px; -} - -.pool-list-item { - background: #23242f; - border-radius: 8px; - transition: all 0.2s ease; - cursor: pointer; -} - -.pool-list-item:hover { - background: #2a2b38; -} - -.pool-item-content { - display: grid; - grid-template-columns: 2fr 2fr 1fr; - padding: 12px 20px; - align-items: center; - gap: 16px; - min-width: 0; -} - -.pool-pair { - display: flex; - align-items: center; - gap: 8px; - font-family: monospace; - color: #ffffff; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; -} - -.pool-pair .separator { - color: #8f90a0; -} - -.pool-address { - font-family: monospace; - color: #8f90a0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; -} - -.view-details-btn { - background: #7c3aed; - color: white; - border: none; - border-radius: 6px; - padding: 6px 12px; - font-size: 0.9rem; - cursor: pointer; - transition: background 0.2s ease; - justify-self: end; -} - -.view-details-btn:hover { - background: #6d28d9; -} - -/* Loading state */ -.loading-spinner { - border: 3px solid #23242f; - border-top: 3px solid #7c3aed; - border-radius: 50%; - width: 24px; - height: 24px; - animation: spin 1s linear infinite; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -.pool-list-loading { - display: flex; - flex-direction: column; - align-items: center; - gap: 12px; - padding: 40px; - color: #8f90a0; -} - -.no-pools-message { - text-align: center; - padding: 40px; - color: #8f90a0; -} - -/* Back button styles */ -.pool-detail-view .back-button-container { - width: 100%; - padding: 0 0 20px 0; - display: flex; - align-items: center; - justify-content: flex-start; -} - -.pool-detail-view .back-button-container .back-button { - width: auto; - min-width: 120px; - background: #7A4FF3 !important; - color: #FFFFFF !important; - border: none; - border-radius: 12px; - padding: 1rem 2rem; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - margin-top: 1rem; - position: relative; - bottom: 0; - font-size: 14px; - font-family: inherit; - line-height: 1.5; -} - -.pool-detail-view .back-button-container .back-button:hover:not(:disabled) { - background: #6340C4 !important; - transform: translateY(-2px); -} - -/* Pool detail view */ -.pool-detail-view { - padding: 20px; - background: #1a1b23; - border-radius: 12px; - max-width: 1200px; - margin: 0 auto; - width: 100%; - box-sizing: border-box; -} \ No newline at end of file diff --git a/app/src/style/PoolList.css b/app/src/style/PoolList.css deleted file mode 100644 index 70e8478..0000000 --- a/app/src/style/PoolList.css +++ /dev/null @@ -1,232 +0,0 @@ -.dashboard-container { - width: 100%; - height: 100%; -} - -.pool-content { - width: 100%; - max-width: 900px; - margin: 0 auto; - padding: 2rem; - position: relative; -} - -.pool-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 2rem; - width: 100%; - background: transparent; - border: none; -} - -.pool-list-title { - font-size: 1.8rem; - font-weight: 600; - color: white; - margin: 0 auto; /* 修改这里 */ - text-align: center; - width: 100%; - padding: 1rem 0; /* 添加一些上下间距 */ -} - -.pool-list-container { - padding: 20px; - background: #1a1b23; - border-radius: 12px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - max-width: 1200px; /* 添加最大宽度 */ - margin: 0 auto; /* 居中容器 */ - width: 100%; /* 确保响应式 */ - box-sizing: border-box; /* 确保padding不会导致溢出 */ -} - -.pool-list-header { - margin-bottom: 20px; -} - -.pool-list-title { - font-size: 1.5rem; - font-weight: 600; - margin-bottom: 16px; - color: #ffffff; -} - -.pool-list-columns { - display: grid; - grid-template-columns: 2fr 2fr 1fr; - padding: 12px 20px; - background: #23242f; - border-radius: 8px; - margin-bottom: 8px; - font-size: 0.9rem; - color: #8f90a0; -} - -.pool-list { - display: flex; - flex-direction: column; - gap: 8px; -} - -.pool-list-item { - background: #23242f; - border-radius: 8px; - transition: all 0.2s ease; - cursor: pointer; -} - -.pool-list-item:hover { - background: #2a2b38; -} - -.pool-item-content { - display: grid; - grid-template-columns: 2fr 2fr 1fr; - padding: 12px 20px; - align-items: center; - gap: 16px; /* 添加列间距 */ - min-width: 0; /* 防止grid项目溢出 */ -} - -.pool-pair { - display: flex; - align-items: center; - gap: 8px; - font-family: monospace; - color: #ffffff; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; -} - -.pool-pair .separator { - color: #8f90a0; -} - -.pool-address { - font-family: monospace; - color: #8f90a0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; -} - -.view-details-btn { - background: #7c3aed; - color: white; - border: none; - border-radius: 6px; - padding: 6px 12px; - font-size: 0.9rem; - cursor: pointer; - transition: background 0.2s ease; - justify-self: end; -} - -.view-details-btn:hover { - background: #6d28d9; -} - -.loading-spinner { - border: 3px solid #23242f; - border-top: 3px solid #7c3aed; - border-radius: 50%; - width: 24px; - height: 24px; - animation: spin 1s linear infinite; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -.pool-list-loading { - display: flex; - flex-direction: column; - align-items: center; - gap: 12px; - padding: 40px; - color: #8f90a0; -} - -.no-pools-message { - text-align: center; - padding: 40px; - color: #8f90a0; -} - -/* 响应式设计 */ -@media (max-width: 768px) { - .pool-list-container { - padding: 0 1rem; - } - - .pool-list { - grid-template-columns: 1fr; - } - - .pool-list-title { - font-size: 1.5rem; - margin-bottom: 1.5rem; - } -} - -/* Pool Detail View 样式 */ -.pool-detail-view { - width: 100%; - max-width: 900px; - margin: 0 auto; - display: flex; - flex-direction: column; - align-items: center; -} - -/* 确保 PoolItem 容器也是居中的 */ -.pool-item { - width: 100%; -} - -@media (min-width: 768px) { - .pool-list { - grid-template-columns: minmax(300px, 600px); - } -} - - -/* Back button styles */ -.pool-detail-view .back-button-container { - width: 100%; - padding: 0 0 20px 0; - display: flex; - align-items: center; - justify-content: flex-start; -} - -.pool-detail-view .back-button-container .back-button { - width: auto; - min-width: 120px; - background: #7A4FF3 !important; - color: #FFFFFF !important; - border: none; - border-radius: 12px; - padding: 1rem 2rem; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - margin-top: 1rem; - position: relative; - bottom: 0; - font-size: 14px; - font-family: inherit; - line-height: 1.5; -} - -.pool-detail-view .back-button-container .back-button:hover:not(:disabled) { - background: #6340C4 !important; - transform: translateY(-2px); -} \ No newline at end of file diff --git a/app/src/style/Theme.css b/app/src/style/Theme.css new file mode 100644 index 0000000..5a68f04 --- /dev/null +++ b/app/src/style/Theme.css @@ -0,0 +1,322 @@ +:root { + /* 主题颜色 */ + --primary: #9945FF; + --primary-dark: #9945FF; + --secondary: #9945FF; + --secondary-dark: #7B35CC; + + /* 背景颜色 */ + --bg-primary: #1a1b23; + --bg-secondary: #23242f; + --bg-hover: #2a2b38; + + /* 文字颜色 */ + --text-primary: #ffffff; + --text-secondary: #8f90a0; + --text-muted: #888888; + + /* 特效 */ + --glass-bg: rgba(255, 255, 255, 0.05); + --glass-blur: blur(10px); + + /* 间距 */ + --spacing-xs: 0.5rem; + --spacing-sm: 1rem; + --spacing-md: 1.5rem; + --spacing-lg: 2rem; + --spacing-xl: 2.5rem; + + /* 圆角 */ + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 16px; + + /* 阴影 */ + --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1); + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.1); + + /* 错误颜色变量 */ + --error: #ff4d4f; +} + +/* 通用容器样式 */ +.tap-page { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + margin: 0 auto; /* 容器本身居中 */ + padding: var(--spacing-md); +} + +/* 通用按钮样式 */ +.btn { + display: inline-flex; + align-items: center; + gap: var(--spacing-xs); + border: none; + border-radius: var(--radius-sm); + font-size: 0.9rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +/* 主要按钮样式 */ +.btn.btn-primary { + background: var(--secondary); + color: var(--text-primary); + padding: 12px 16px; + text-align: center; + justify-content: center; + min-width: 120px; +} + +.btn.btn-primary:hover { + background: var(--secondary-dark); + transform: translateY(-1px); + box-shadow: var(--shadow-sm); +} + +.btn.btn-primary:active { + transform: translateY(0); +} + +.btn.btn-primary:disabled { + background: var(--bg-secondary); + color: var(--text-secondary); + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +/* 按钮内的图标和文本布局 */ +.btn .code-text { + display: flex; + align-items: center; + gap: var(--spacing-xs); +} + +/* 卡片基础样式 */ +.card { + width: 100%; + max-width: 1200px; /* 添加最大宽度 */ + margin: 0 auto; /* 容器本身居中 */ + padding: var(--spacing-md); + border-radius: var(--radius-sm); + line-height: 1.6; +} + +/* 渐变边框卡片 */ +.card.gradient-border { + position: relative; + background: linear-gradient(var(--bg-primary), var(--bg-primary)) padding-box, + linear-gradient(45deg, var(--primary), var(--secondary)) border-box; + border: 2px solid transparent; + transition: all 0.2s ease; + width: 100%; /* 占满容器宽度 */ + margin: 0 auto; /* 水平居中 */ +} + +.card.gradient-border:hover, +.card.gradient-border.compact:hover { + background: var(--bg-hover); + border: 2px solid transparent; + border-image: linear-gradient(45deg, var(--primary), var(--secondary)) 1; + border-left-color: var(--primary); +} + +/* 新增窄版卡片样式 */ +.card.gradient-border.compact { + max-width: 720px; /* 限制最大宽度 */ + width: 100%; /* 在最大宽度范围内占满宽度 */ + margin: 0 auto; /* 水平居中 */ +} + +/* 输入框容器样式 */ +.form-group { + display: flex; + flex-direction: column; + align-items: center; /* 水平居中 */ + gap: var(--spacing-xs); + margin-bottom: var(--spacing-md); + width: 100%; +} + +/* 输入框样式 */ +.input { + background: var(--bg-secondary); + border: 1px solid var(--text-secondary); + border-radius: var(--radius-sm); + padding: var(--spacing-sm); + color: var(--text-primary); + transition: all 0.2s ease; + width: 100%; + max-width: 240px; /* 限制最大宽度 */ + margin: 0 auto; /* 水平居中 */ + font-size: 0.9rem; /* 减小文字大小 */ +} + +.input:focus { + border-color: var(--primary); + outline: none; + box-shadow: 0 0 0 2px rgba(20, 241, 149, 0.2); +} + +/* 加载动画 */ +.loading-spinner { + border: 3px solid var(--bg-secondary); + border-top: 3px solid var(--primary); + border-radius: 50%; + width: 24px; + height: 24px; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* 列表布局 */ +.pool-list-columns { + display: grid; + grid-template-columns: 1fr 1fr 1fr; /* 三等分 */ + gap: var(--spacing-md); + padding: var(--spacing-md) var(--spacing-lg); /* 左右添加更多内边距 */ + color: var(--text-secondary); + font-size: 0.9rem; +} + +.pool-list { + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + width: 100%; + line-height: 1.6; + font-size: 1rem; +} + +.pool-item-content { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: var(--spacing-md); + padding: var(--spacing-xs) var(--spacing-lg); /* 减小上下内边距为 0.5rem */ + align-items: center; +} + +/* 表单组样式 */ +.form-group { + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + margin-bottom: var(--spacing-md); +} + +/* 右对齐容器 */ +.align-right { + display: flex; + justify-content: flex-end; + margin-bottom: var(--spacing-md); +} + +/* 居中对齐容器 */ +.align-center { + display: flex; + justify-content: center; + margin-bottom: var(--spacing-md); +} + +/* 左对齐容器 */ +.align-left { + display: flex; + justify-content: flex-start; + margin-bottom: var(--spacing-md); + width: 100%; /* 确保容器占满宽度 */ + max-width: 1200px; /* 与其他内容保持一致的最大宽度 */ + margin-left: auto; /* 水平居中容器本身 */ + margin-right: auto; /* 水平居中容器本身 */ + padding: 0 var(--spacing-md); /* 添加左右内边距 */ +} + + + +/* 标签导航样式 */ +.tab-nav { + position: fixed; + top: 72px; /* 导航栏高度 */ + left: 0; + right: 0; + z-index: 99; + display: flex; + gap: var(--spacing-sm); + justify-content: center; + padding: var(--spacing-md); + background: var(--bg-secondary); + border-radius: 0; + flex-wrap: wrap; +} + +/* 标签按钮样式 */ +.tab-button { + display: flex; + align-items: center; + gap: var(--spacing-xs); + padding: var(--spacing-sm) var(--spacing-md); + border: none; + border-radius: var(--radius-sm); + background: transparent; + color: var(--text-secondary); + cursor: pointer; + transition: all 0.2s ease; +} + +.tab-button:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.tab-button.active { + background: var(--primary); +} + +.tab-button.active .code-text, +.tab-button.active .sub-title, +.tab-button.active .section-title { + color: var(--bg-primary); +} + +/* 返回按钮容器样式 */ +.back-button-container { + display: flex; + justify-content: flex-start; + width: 100%; + max-width: 1200px; + margin: 0 auto var(--spacing-md) auto; + padding: 0 var(--spacing-md); /* 与 tab-content 保持一致的内边距 */ +} + +/* 如果需要在某些场景下隐藏返回按钮容器 */ +.back-button-container.hidden { + display: none; +} + +.address-pair { + display: flex; + align-items: center; + gap: var(--spacing-xs); + white-space: nowrap; +} + +/* 为了防止内容被固定的tab-nav遮挡,需要给内容添加上边距 */ +.app-main { + padding-top: 64px; /* 根据tab-nav的实际高度调整 */ +} + +/* 添加内容区域样式 */ +.dashboard-content { + margin-top: 140px; /* 72px (nav) + 约68px (tab-nav高度) */ + width: 100%; + padding: var(--spacing-md); +} diff --git a/app/src/style/Typography.css b/app/src/style/Typography.css new file mode 100644 index 0000000..b57bf6a --- /dev/null +++ b/app/src/style/Typography.css @@ -0,0 +1,100 @@ +/* 标题样式 */ +.gradient-title { + font-size: 2.5rem; + text-align: center; + background: linear-gradient(45deg, #9945FF, #14F195); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + margin-bottom: 1rem; + } + + /* 子标题样式 */ + .section-title { + font-size: 1.5rem; + color: #14F195; + margin-bottom: 0.75rem; + font-weight: 600; + } + + /* 小标题样式 */ + .sub-title { + font-size: 1.3rem; + color: #9945FF; + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.1; + } + + /* 正文样式 */ + .body-text { + color: #ffffff; + line-height: 1.2; + margin-bottom: 0.5rem; + font-weight: 600; + } + + /* 次要文字样式 */ + .secondary-text { + color: #8f90a0; + font-size: 1.2rem; + line-height: 1.2; + font-weight: 600; + } + + /* 链接样式 */ + .text-link { + color: #14F195; + text-decoration: none; + transition: color 0.2s ease; + } + + .text-link:hover { + color: #9945FF; + } + + /* 强调文字 */ + .highlight-text { + color: #9945FF; + font-weight: 500; + } + + /* 代码文字 */ + .code-text { + font-family: 'Courier New', Courier, monospace; + color: #14F195; + display: flex; + align-items: center; /* 改为居中对齐 */ + gap: var(--spacing-xs); + width: 100%; + line-height: 1.6; + font-size: 1rem; + font-weight: 500; + } + + /* 注释文字 */ + .note-text { + font-size: 0.9rem; + color: #888; + font-style: italic; + line-height: 1.2; + } + + .code-text.column { /* 添加新的类用于需要垂直布局的情况 */ + flex-direction: column; + text-align: center; + } + + /* 响应式设计 */ + @media (max-width: 768px) { + .gradient-title { + font-size: 2rem; + } + + .section-title { + font-size: 1.5rem; + } + + .sub-title { + font-size: 1.2rem; + } + } \ No newline at end of file diff --git a/app/src/utils/wrapSol.ts b/app/src/utils/wrapSol.ts new file mode 100644 index 0000000..f721c31 --- /dev/null +++ b/app/src/utils/wrapSol.ts @@ -0,0 +1,48 @@ +import { Connection, Transaction, SystemProgram, LAMPORTS_PER_SOL } from '@solana/web3.js'; +import { NATIVE_MINT, createSyncNativeInstruction, getAssociatedTokenAddress } from '@solana/spl-token'; + +export async function wrapSol( + connection: Connection, + wallet: any, + amountInSol: number +) { + try { + // 1. 获取用户的 wSOL 关联账户地址 + const associatedTokenAccount = await getAssociatedTokenAddress( + NATIVE_MINT, + wallet.publicKey + ); + + const transaction = new Transaction(); + + // 2. 转账SOL到关联账户 + transaction.add( + SystemProgram.transfer({ + fromPubkey: wallet.publicKey, + toPubkey: associatedTokenAccount, + lamports: amountInSol * LAMPORTS_PER_SOL, + }) + ); + + // 3. 同步原生SOL余额 + transaction.add(createSyncNativeInstruction(associatedTokenAccount)); + + // 4. 获取最新的 blockhash + const latestBlockhash = await connection.getLatestBlockhash(); + transaction.recentBlockhash = latestBlockhash.blockhash; + transaction.feePayer = wallet.publicKey; + + // 5. 发送并确认交易 + const signed = await wallet.signTransaction(transaction); + const signature = await connection.sendRawTransaction(signed.serialize()); + await connection.confirmTransaction({ + signature, + ...latestBlockhash + }); + + return signature; + } catch (error) { + console.error("Error wrapping SOL:", error); + throw error; + } +}