-
- {publicKey?.toBase58() == EXCLUDED_PUBLIC_KEY && (
-
- )}
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
- {activeTab === 'amm' && publicKey?.toBase58() == EXCLUDED_PUBLIC_KEY ?
- (
- )
- : activeTab === 'pool' ? (
-
-
-
+
+ {activeTab === 'amm' && publicKey?.toBase58() == EXCLUDED_PUBLIC_KEY ? (
+
+ ) : activeTab === 'pool' ? (
+
+ ) : activeTab === 'lenderPool' ? (
+
+
+
+ ) : activeTab === 'borrowerPool' ? (
+
+
+
+ ) : activeTab === 'liquidate' ? (
+
+
+
+ ) : (
+
+ )}
- )
- : activeTab === 'lenderPool' ? (
-
-
-
- )
- : activeTab === 'borrowerPool' ? (
-
-
-
- )
- : activeTab === 'liquidate' ? (
-
-
- )
- :(
-
- )}
-
+
+
-
);
};
-// 交易表单组件
const FutureForm: FC = () => {
return (
-
Coming Soon...
+ Coming Soon...
);
};
diff --git a/app/src/components/Icons.tsx b/app/src/components/Icons.tsx
new file mode 100644
index 0000000..b32882b
--- /dev/null
+++ b/app/src/components/Icons.tsx
@@ -0,0 +1,152 @@
+import { FC } from 'react';
+
+interface IconProps {
+ className?: string;
+ size?: number;
+ color?: string;
+}
+
+export const IconRefresh: FC
= ({
+ className = '',
+ size = 16,
+ color = 'currentColor'
+}) => (
+
+);
+
+export const IconSwap: FC = ({
+ className = '',
+ size = 16,
+ color = 'currentColor'
+}) => (
+
+);
+
+export const IconSettings: FC = ({
+ className = '',
+ size = 16,
+ color = 'currentColor'
+}) => (
+
+);
+
+export const IconClose: FC = ({
+ className = '',
+ size = 16,
+ color = 'currentColor'
+}) => (
+
+);
+
+export const IconChevronDown: FC = ({
+ className = '',
+ size = 16,
+ color = 'currentColor'
+}) => (
+
+);
+
+export const IconWallet: FC = ({
+ className = '',
+ size = 16,
+ color = 'currentColor'
+}) => (
+
+);
\ No newline at end of file
diff --git a/app/src/components/InitPoolForm.tsx b/app/src/components/InitPoolForm.tsx
index 1467687..6511065 100644
--- a/app/src/components/InitPoolForm.tsx
+++ b/app/src/components/InitPoolForm.tsx
@@ -1,7 +1,7 @@
import { FC, useState } from 'react';
import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react';
import { PublicKey } from '@solana/web3.js';
-import { initPool } from '../utils/initPool';
+import { createCreditPool } from '../utils/createCreditPool';
import { useAmm } from '../contexts/AmmContext';
import { PoolInfo } from '../utils/getPoolList';
@@ -38,7 +38,7 @@ export const InitPoolForm: FC = ({ pool, onSuccess }) => {
const mintAPubkey = new PublicKey(pool.mintA);
const mintBPubkey = new PublicKey(pool.mintB);
- const signature = await initPool(
+ const signature = await createCreditPool(
wallet,
connection,
poolPubkey,
diff --git a/app/src/components/LenderPoolList.tsx b/app/src/components/LenderPoolList.tsx
index e169819..e08000f 100644
--- a/app/src/components/LenderPoolList.tsx
+++ b/app/src/components/LenderPoolList.tsx
@@ -3,25 +3,24 @@ import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react';
import { getPoolList } from '../utils/getPoolList';
import { PoolInfo } from '../utils/getPoolList';
import { LenderPoolItem } from './LenderPoolItem';
+import '../style/PoolList.css';
export const LenderPoolList: FC = () => {
const wallet = useAnchorWallet();
const { connection } = useConnection();
const [pools, setPools] = useState([]);
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState("");
- const [lastTxSignature, setLastTxSignature] = useState("");
+ const [isLoading, setIsLoading] = useState(true);
+ const [selectedPool, setSelectedPool] = useState(null);
+ const [lastTxSignature, setLastTxSignature] = useState('');
const fetchPools = async () => {
- if (!wallet) return;
-
- setIsLoading(true);
try {
- const pools = await getPoolList(wallet, connection);
- setPools(pools);
- } catch (err) {
- console.error("Error fetching pools:", err);
- setError(err instanceof Error ? err.message : "Failed to fetch pools");
+ setIsLoading(true);
+ if (!wallet) return;
+ const poolList = await getPoolList(wallet, connection);
+ setPools(poolList);
+ } catch (error) {
+ console.error('Error fetching pools:', error);
} finally {
setIsLoading(false);
}
@@ -29,51 +28,74 @@ export const LenderPoolList: FC = () => {
useEffect(() => {
fetchPools();
- }, [wallet, connection]);
+ }, [connection, wallet, lastTxSignature]);
- const handleTxSuccess = (signature: string) => {
- setLastTxSignature(signature);
- fetchPools();
- };
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (selectedPool) {
+ return (
+
+
+ {
+ setLastTxSignature(signature);
+ fetchPools();
+ }}
+ />
+
+ );
+ }
return (
-
Liquidity Pools
- {error && (
-
- {error}
-
- )}
- {lastTxSignature && (
-
- )}
- {isLoading ? (
-
Loading pools...
- ) : pools.length === 0 ? (
-
-
No pools found
+
Available Lending Pools
+ {pools.length === 0 ? (
+
+ No pools available
) : (
{pools.map((pool) => (
-
+
setSelectedPool(pool)}
+ >
+
+
+ Token A:
+
+ {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)}
+
+
+
⟷
+
+ Token B:
+
+ {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)}
+
+
+
+
+ View Details →
+
+
))}
)}
-
);
-};
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/app/src/components/PoolItem.tsx b/app/src/components/PoolItem.tsx
index 86ca91f..fadcfc1 100644
--- a/app/src/components/PoolItem.tsx
+++ b/app/src/components/PoolItem.tsx
@@ -3,9 +3,11 @@ import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { getPoolDetail, PoolDetailInfo } from '../utils/getPoolDetail';
import { SwapForm } from './SwapForm';
import { DepositLiquidityForm } from './DepositLiquidityForm';
-import { InitPoolForm } from './InitPoolForm';
+import { CheckCreditPoolForm } from './CheckCreditPoolForm';
import { PoolInfo } from '../utils/getPoolList';
import { PublicKey } from '@solana/web3.js';
+import '../style/PoolItem.css';
+
interface PoolItemProps {
pool: PoolInfo;
onTxSuccess: (signature: string) => void;
@@ -14,7 +16,7 @@ interface PoolItemProps {
export const PoolItem: FC
= ({ pool, onTxSuccess }) => {
const { connection } = useConnection();
const { publicKey: walletPublicKey } = useWallet();
- const [activeForm, setActiveForm] = useState<'none' | 'initPool' | 'deposit' | 'swap'>('none');
+ const [activeForm, setActiveForm] = useState<'none' | 'deposit' | 'swap'>('none');
const [details, setDetails] = useState(null);
const [isLoadingDetails, setIsLoadingDetails] = useState(false);
@@ -45,6 +47,10 @@ export const PoolItem: FC = ({ pool, onTxSuccess }) => {
return (
+
+
Pool Details
@@ -63,59 +69,8 @@ export const PoolItem: FC
= ({ pool, onTxSuccess }) => {
{pool.pubkey.toString()}
-
-
- Token A mint addr:
-
- {pool.mintA.toString()}
-
-
-
-
- Token B mint addr:
-
- {pool.mintB.toString()}
-
-
-
-
- Fee:
-
- {pool.fee}
-
-
-
-
- Min Collateral Ratio:
-
- {pool.minCollateralRatio}
-
-
-
-
-
-
-
- {activeForm === 'initPool' && (
-
- {
- onTxSuccess(signature);
- setActiveForm('none');
- fetchDetails();
- }}
- />
-
- )}
-
Pool Liquidity & Prices
{isLoadingDetails ? (
@@ -184,7 +139,6 @@ export const PoolItem: FC
= ({ pool, onTxSuccess }) => {
/>
)}
-
);
};
\ No newline at end of file
diff --git a/app/src/components/PoolList.tsx b/app/src/components/PoolList.tsx
index 71fd169..79f1433 100644
--- a/app/src/components/PoolList.tsx
+++ b/app/src/components/PoolList.tsx
@@ -1,27 +1,25 @@
-import { FC, useEffect, useState } from 'react';
-import { useAnchorWallet, useConnection } from '@solana/wallet-adapter-react';
-import { getPoolList } from '../utils/getPoolList';
+import { FC, useState, useEffect } from 'react';
+import { useConnection } from '@solana/wallet-adapter-react';
+import { PoolInfo, getPoolList } from '../utils/getPoolList';
import { PoolItem } from './PoolItem';
-import { PoolInfo } from '../utils/getPoolList';
+import '../style/PoolList.css';
+import { useAnchorWallet } from '@solana/wallet-adapter-react';
export const PoolList: FC = () => {
const wallet = useAnchorWallet();
const { connection } = useConnection();
const [pools, setPools] = useState
([]);
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState("");
- const [lastTxSignature, setLastTxSignature] = useState("");
+ const [isLoading, setIsLoading] = useState(true);
+ const [selectedPool, setSelectedPool] = useState(null);
+ const [lastTxSignature, setLastTxSignature] = useState('');
const fetchPools = async () => {
- if (!wallet) return;
-
- setIsLoading(true);
try {
- const pools = await getPoolList(wallet, connection);
- setPools(pools);
- } catch (err) {
- console.error("Error fetching pools:", err);
- setError(err instanceof Error ? err.message : "Failed to fetch pools");
+ setIsLoading(true);
+ const poolList = await getPoolList( wallet, connection);
+ setPools(poolList);
+ } catch (error) {
+ console.error('Error fetching pools:', error);
} finally {
setIsLoading(false);
}
@@ -29,51 +27,74 @@ export const PoolList: FC = () => {
useEffect(() => {
fetchPools();
- }, [wallet, connection]);
+ }, [connection, lastTxSignature]);
- const handleTxSuccess = (signature: string) => {
- setLastTxSignature(signature);
- fetchPools();
- };
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (selectedPool) {
+ return (
+
+
+
{
+ setLastTxSignature(signature);
+ fetchPools();
+ }}
+ />
+
+ );
+ }
return (
-
Liquidity Pools
- {error && (
-
- {error}
-
- )}
- {lastTxSignature && (
-
- )}
- {isLoading ? (
-
Loading pools...
- ) : pools.length === 0 ? (
-
-
No pools found
+
Available Pools
+ {pools.length === 0 ? (
+
+ No pools available
) : (
{pools.map((pool) => (
-
+ className="pool-list-item"
+ onClick={() => setSelectedPool(pool)}
+ >
+
+
+ Token A:
+
+ {pool.mintA.toString().slice(0, 4)}...{pool.mintA.toString().slice(-4)}
+
+
+
⟷
+
+ Token B:
+
+ {pool.mintB.toString().slice(0, 4)}...{pool.mintB.toString().slice(-4)}
+
+
+
+
+ View Details →
+
+
))}
)}
-
);
-};
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/app/src/idl/fall.json b/app/src/idl/fall.json
index 491ad52..a4825b8 100644
--- a/app/src/idl/fall.json
+++ b/app/src/idl/fall.json
@@ -1,5 +1,5 @@
{
- "address": "BmzRwgDaqP6cSLoA2u2A7NysVF8nbbw8xRomRUc3DTt2",
+ "address": "C8sj5h7nejVHeyWvq6aCwrFWY5Q5QGuZDPqVwVHPxNLm",
"metadata": {
"name": "fall",
"version": "0.1.0",
diff --git a/app/src/index.css b/app/src/index.css
deleted file mode 100644
index 2c409f2..0000000
--- a/app/src/index.css
+++ /dev/null
@@ -1,857 +0,0 @@
-/* src/index.css */
-:root {
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-html, body {
- width: 100%;
- min-height: 100vh;
- background-color: #f5f5f5;
-}
-
-body {
- margin: 0;
- padding: 0;
- color: #213547;
- background-color: #f5f5f5;
-}
-
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- background-color: #512da8;
- color: white;
- cursor: pointer;
- transition: all 0.25s;
-}
-
-
-.error-message {
- color: #ff4444;
- background-color: #ffe0e0;
- padding: 10px;
- border-radius: 4px;
- margin-bottom: 16px;
-}
-
-
-.success-message {
- color: #4CAF50;
- background-color: #E8F5E9;
- padding: 10px;
- border-radius: 4px;
- margin-bottom: 16px;
-}
-
-.create-amm-container {
- max-width: 500px;
- margin: 0 auto;
- padding: 20px;
-}
-
-
-.amm-item {
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 16px;
- margin-bottom: 12px;
- background-color: white;
-}
-
-.amm-details {
- display: flex;
- justify-content: space-between;
- margin-bottom: 8px;
-}
-
-.amm-details:last-child {
- margin-bottom: 0;
-}
-
-.amm-label {
- font-weight: 500;
- color: #666;
-}
-
-.amm-value {
- word-break: break-all;
-}
-
-.pool-item {
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 16px;
- margin-bottom: 12px;
- background-color: white;
-}
-
-.pool-details {
- display: flex;
- justify-content: space-between;
- margin-bottom: 8px;
-}
-
-.pool-details:last-child {
- margin-bottom: 0;
-}
-
-.pool-label {
- font-weight: 500;
- color: #666;
-}
-
-.pool-value {
- word-break: break-all;
- max-width: 70%;
- text-align: right;
-}
-
-.swap-direction {
- display: flex;
- gap: 8px;
- margin-bottom: 16px;
-}
-
-.direction-button {
- flex: 1;
- padding: 8px;
- border: 1px solid #646cff;
- background: none;
- color: #646cff;
- border-radius: 4px;
- cursor: pointer;
-}
-
-.direction-button.active {
- background: #646cff;
- color: white;
-}
-
-.pool-actions {
- display: flex;
- gap: 8px;
-}
-
-.pool-list-container {
- max-width: 800px;
- margin: 0 auto;
- padding: 20px;
-}
-
-.pool-list {
- display: flex;
- flex-direction: column;
- gap: 16px;
-}
-
-.pool-item {
- background: white;
- border-radius: 8px;
- padding: 20px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
-}
-
-.pool-details {
- display: flex;
- justify-content: space-between;
- margin-bottom: 8px;
- font-size: 14px;
-}
-
-.pool-label {
- color: #666;
- font-weight: 500;
-}
-
-.pool-value {
- color: #333;
- word-break: break-all;
- max-width: 70%;
- text-align: right;
-}
-
-.pool-actions {
- display: flex;
- gap: 8px;
- margin-top: 16px;
-}
-
-.action-button {
- flex: 1;
- background-color: #646cff;
- color: white;
- border: none;
- padding: 8px 16px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 14px;
- transition: background-color 0.2s;
-}
-
-.action-button:hover {
- background-color: #747bff;
-}
-
-.action-button:disabled {
- background-color: #cccccc;
- cursor: not-allowed;
-}
-
-.form-container {
- margin-top: 16px;
- padding-top: 16px;
- border-top: 1px solid #eee;
-}
-
-.success-message {
- background-color: #e6ffe6;
- border: 1px solid #b3ffb3;
- color: #008000;
- padding: 12px;
- border-radius: 4px;
- margin-bottom: 16px;
-}
-
-.success-message a:hover {
- text-decoration: underline;
-}
-
-.error-message {
- background-color: #ffe6e6;
- border: 1px solid #ffb3b3;
- color: #cc0000;
- padding: 12px;
- border-radius: 4px;
- margin-bottom: 16px;
-}
-
-.loading {
- text-align: center;
- color: #666;
- padding: 20px;
-}
-
-.empty-state {
- text-align: center;
- color: #666;
- padding: 40px;
- background: #f9f9f9;
- border-radius: 8px;
-}
-
-
-
-
-
-.form-group input:focus {
- border-color: #646cff;
- outline: none;
-}
-
-
-.submit-button:disabled {
- background-color: #cccccc;
- cursor: not-allowed;
-}
-
-.pool-price-info {
- margin: 16px 0;
- padding: 16px;
- background-color: #f8f9fa;
- border-radius: 4px;
-}
-
-.pool-price-info h4 {
- margin: 0 0 12px 0;
- color: #333;
- font-size: 14px;
-}
-
-.loading-prices {
- color: #666;
- font-size: 14px;
- text-align: center;
- padding: 8px 0;
-}
-
-.pool-details .pool-value {
- font-family: monospace;
-}
-
-.lending-pool-item {
- background: #ffffff;
- border: 1px solid #e0e0e0;
- border-radius: 8px;
- padding: 20px;
- margin-bottom: 20px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
-}
-
-.pool-info-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
- gap: 20px;
- margin-bottom: 20px;
-}
-
-.info-section {
- background: #f8f9fa;
- border: 1px solid #e9ecef;
- border-radius: 6px;
- padding: 15px;
-}
-
-.info-section h4 {
- margin: 0 0 15px 0;
- color: #343a40;
- font-size: 1rem;
- font-weight: 600;
-}
-
-.info-row {
- margin-bottom: 10px;
-}
-
-.info-row:last-child {
- margin-bottom: 0;
-}
-
-.info-label {
- display: block;
- font-size: 0.875rem;
- color: #6c757d;
- margin-bottom: 4px;
-}
-
-.info-value {
- display: block;
- font-weight: 500;
- color: #212529;
-}
-
-.pool-actions {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-top: 15px;
- border-top: 1px solid #e9ecef;
-}
-
-.action-buttons {
- display: flex;
- gap: 10px;
-}
-
-.action-button {
- padding: 8px 16px;
- border: none;
- border-radius: 4px;
- background-color: #0d6efd;
- color: white;
- font-weight: 500;
- cursor: pointer;
- transition: background-color 0.2s;
-}
-
-.action-button:hover {
- background-color: #0b5ed7;
-}
-
-.refresh-button {
- padding: 6px 12px;
- border: 1px solid #dee2e6;
- border-radius: 4px;
- background-color: white;
- color: #212529;
- cursor: pointer;
- transition: all 0.2s;
-}
-
-.refresh-button:hover:not(:disabled) {
- background-color: #f8f9fa;
- border-color: #ced4da;
-}
-
-.refresh-button:disabled {
- opacity: 0.65;
- cursor: not-allowed;
-}
-
-.form-container {
- margin-top: 20px;
- padding-top: 20px;
- border-top: 1px solid #e9ecef;
-}
-
-.form-wrapper {
- background: #ffffff;
- border-radius: 8px;
- padding: 20px;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
-}
-
-.error-message {
- background-color: #ffe6e6;
- color: #d63031;
- padding: 10px;
- border-radius: 4px;
- margin-bottom: 15px;
-}
-
-.warning-message {
- background-color: #fff3cd;
- color: #856404;
- padding: 10px;
- border-radius: 4px;
- margin: 15px 0;
-}
-
-.lending-pool-info-summary {
- background: #f8f9fa;
- border: 1px solid #e9ecef;
- border-radius: 6px;
- padding: 15px;
- margin: 15px 0;
- font-size: 0.9rem;
-}
-
-.lending-pool-info-summary > div {
- margin: 5px 0;
- color: #495057;
-}
-
-.action-button {
- width: 100%;
- padding: 12px;
- background-color: #0d6efd;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- font-size: 1rem;
- transition: background-color 0.2s;
-}
-
-.action-button:hover:not(:disabled) {
- background-color: #0b5ed7;
-}
-
-.action-button:disabled {
- background-color: #6c757d;
- cursor: not-allowed;
-}
-/* Liquidate Form Styles */
-.liquidate-container {
- max-width: 500px;
- margin: 0 auto;
- padding: 20px;
-}
-
-.error-message {
- color: red;
- margin-bottom: 15px;
- padding: 10px;
- border: 1px solid red;
- border-radius: 4px;
-}
-
-.success-message a {
- color: green;
- margin-left: 5px;
- text-decoration: underline;
-}
-
-.input-field {
- padding: 8px;
- border: 1px solid #ccc;
- border-radius: 4px;
- font-size: 16px;
-}
-
-.input-field:focus {
- outline: none;
- border-color: #4CAF50;
- box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2);
-}
-
-
-.submit-button:disabled {
- background-color: #cccccc;
- cursor: not-allowed;
-}
-
-.submit-button:hover:not(:disabled) {
- background-color: #45a049;
-}
-
-/* src/App.css */
-
-/* 重置基础样式 */
-* {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
-}
-
-/* 容器样式 */
-.container {
- min-height: 100vh;
- width: 100%; /* 确保容器占满整个宽度 */
- background-color: #f5f5f5;
- display: flex;
- flex-direction: column;
-}
-
-/* 导航栏样式 */
-.navbar {
- width: 100%; /* 确保导航栏占满宽度 */
- background-color: white;
- padding: 1rem 0; /* 只设置上下内边距 */
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
-}
-
-.navbar-content {
- width: 100%;
- max-width: 1200px; /* 增加最大宽度 */
- margin: 0 auto;
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 0 2rem; /* 左右内边距 */
-}
-
-/* 主要内容区域 */
-.main-content {
- width: 100%;
- flex: 1;
- padding: 2rem;
- display: flex;
- flex-direction: column;
- align-items: center; /* 内容居中 */
-}
-
-/* DeFi Dashboard 样式 */
-.defi-dashboard {
- width: 100%;
- max-width: 1200px; /* 增加最大宽度 */
- background: white;
- border-radius: 8px;
- padding: 2rem;
- margin: 20px auto;
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
-}
-
-.tabs {
- display: flex;
- gap: 10px;
- margin-bottom: 20px;
- justify-content: center; /* 居中显示标签 */
-}
-
-.form {
- max-width: 400px;
- margin: 0 auto;
- padding: 0 20px; /* 添加左右内边距 */
- display: flex;
- flex-direction: column;
- gap: 15px;
-}
-
-/* 连接钱包提示居中 */
-.connect-prompt {
- text-align: center;
- margin-top: 5rem;
-}
-
-.title {
- font-size: 1.5rem;
- font-weight: bold;
- color: #333;
-}
-
-.form-group {
- margin-bottom: 20px;
- display: flex;
- flex-direction: column;
- gap: 5px;
-}
-
-.form-group label {
- display: block;
- margin-bottom: 8px;
- color: #666;
-}
-
-.form-group input {
- width: 100%;
- padding: 10px;
- border: 1px solid #ddd;
- border-radius: 4px;
- font-size: 16px;
-}
-
-.submit-button {
- width: 100%;
- padding: 12px;
- background: #512da8;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- transition: background-color 0.2s;
- font-size: 16px;
-}
-
-.submit-button:hover {
- background: #4527a0;
-}
-
-
-
-
- /* 修改 #root 的样式 */
-#root {
- width: 100%;
- min-height: 100vh;
- display: flex;
- flex-direction: column; /* 改为列方向排列 */
- min-height: 100vh;
-}
-
-
-
-.solana-network-select {
- appearance: none;
- background-color: #1C1C1C;
- border: 1px solid #383838;
- border-radius: 12px;
- padding: 8px 36px 8px 12px;
- font-size: 14px;
- font-weight: 500;
- color: #FFFFFF;
- cursor: pointer;
- transition: all 0.2s ease;
- min-width: 160px;
- height: 40px;
-}
-
-.solana-network-select:hover {
- border-color: #4E44CE;
- background-color: #232323;
-}
-
-.solana-network-select:focus {
- outline: none;
- border-color: #9945FF;
- box-shadow: 0 0 0 2px rgba(153, 69, 255, 0.1);
-}
-
-
-
-.solana-network-status {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px 16px;
- background-color: #1C1C1C;
- border-radius: 12px;
- border: 1px solid #383838;
- margin-bottom: 16px;
-}
-
-.solana-network-label {
- font-size: 14px;
- font-weight: 500;
- color: #777777;
-}
-
-.solana-network-value {
- font-size: 14px;
- font-weight: 600;
- color: #FFFFFF;
- padding: 4px 12px;
- background-color: #232323;
- border-radius: 8px;
- border: 1px solid #383838;
-}
-
-/* 为不同网络添加不同的标识色 */
-.solana-network-value[data-network="mainnet-beta"] {
- color: #14F195;
-}
-
-.solana-network-value[data-network="devnet"] {
- color: #9945FF;
-}
-
-.solana-network-value[data-network="testnet"] {
- color: #00C2FF;
-}
-
-.solana-network-value[data-network="localnet"] {
- color: #F6C343;
-}
-
-.solana-content {
- max-width: 1200px;
- margin: 0 auto;
- padding: 24px;
-}
-
-/* 为 WalletMultiButton 添加一些自定义样式 */
-.wallet-adapter-button {
- height: 40px !important;
- padding: 0 16px !important;
- border-radius: 12px !important;
- background-color: #4E44CE !important;
- transition: all 0.2s ease !important;
-}
-
-.wallet-adapter-button:hover {
- background-color: #5a50d8 !important;
-}
-.navbar-content {
- width: 100%;
- max-width: 1200px;
- margin: 0 auto;
- padding: 0 2rem;
- display: flex;
- justify-content: space-between;
- align-items: center;
- height: 64px;
-}
-
-.solana-network-select-container {
- align-items: center;
- position: relative;
- display: inline-block;
-}
-
-
-.solana-network-select-container::after {
- content: '';
- position: absolute;
- right: 12px;
- top: 50%;
- transform: translateY(-50%);
- width: 0;
- height: 0;
- border-left: 5px solid transparent;
- border-right: 5px solid transparent;
- border-top: 5px solid #FFFFFF;
- pointer-events: none;
-}
-
-
-
-/* 悬停效果 */
-.action-button:hover,
-.lending-pool-action-button:hover,
-.lending-pool-refresh-button:hover {
- background-color: #45a049;
- transform: translateY(-2px);
- box-shadow: 0 2px 5px rgba(0,0,0,0.2);
- border-color: transparent; /* 覆盖之前的 border-color */
-}
-
-/* 按钮容器样式 */
-.pool-actions,
-.lending-pool-actions {
- display: flex;
- gap: 10px;
- margin: 15px 0;
- flex-wrap: wrap;
- justify-content: center;
-}
-
-.lending-pool-action-buttons {
- display: flex;
- gap: 10px;
- flex-wrap: wrap;
- justify-content: center;
- width: 100%;
-}
-
-/* 不同操作按钮的颜色区分 */
-.action-button[onClick*="initPool"] {
- background-color: #9C27B0;
-}
-
-.action-button[onClick*="deposit"] {
- background-color: #2196F3;
-}
-
-.action-button[onClick*="swap"] {
- background-color: #FF9800;
-}
-
-.lending-pool-action-button[onClick*="lend"] {
- background-color: #514caf;
-}
-
-.lending-pool-action-button[onClick*="borrow"] {
- background-color: #F44336;
-}
-
-.lending-pool-action-button[onClick*="repay"] {
- background-color: #795548;
-}
-
-.lending-pool-action-button[onClick*="depositCollateral"] {
- background-color: #607D8B;
-}
-
-/* 刷新按钮特殊样式 */
-.lending-pool-refresh-button {
- background-color: #6e21f3;
-}
-
-.lending-pool-refresh-button:hover {
- background-color: #7919d2;
-}
-
-/* 基础按钮样式 */
-.action-button,
-.lending-pool-action-button,
-.lending-pool-refresh-button {
- background-color: #512da8;
- color: white;
- padding: 10px 20px;
- border: none;
- border-radius: 5px;
- cursor: pointer;
- font-size: 14px;
- margin: 5px;
- transition: all 0.3s ease;
- width: auto; /* 覆盖之前的 width: 100% */
-}
-
-/* 禁用状态统一样式 */
-.action-button:disabled,
-.lending-pool-action-button:disabled,
-.lending-pool-refresh-button:disabled {
- background-color: #cccccc;
- cursor: not-allowed;
- transform: none;
- box-shadow: none;
- opacity: 0.7;
-}
-
-/* 表单容器样式 */
-.form-container {
- background-color: #f5f5f5;
- padding: 15px;
- border-radius: 8px;
- margin: 10px 0;
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
-}
diff --git a/app/src/main.tsx b/app/src/main.tsx
index 7cb4252..c4ae20f 100644
--- a/app/src/main.tsx
+++ b/app/src/main.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { WalletContextProvider } from './WalletContextProvider';
-import './index.css';
+import './style/index.css';
import { Buffer } from 'buffer';
import 'stream-browserify';
diff --git a/app/src/style/App.css b/app/src/style/App.css
new file mode 100644
index 0000000..fe038eb
--- /dev/null
+++ b/app/src/style/App.css
@@ -0,0 +1,152 @@
+.app-container {
+ min-height: 100vh;
+ background: #1C1C28;
+ color: white;
+ position: relative; /* 添加这行 */
+ overflow-x: hidden; /* 防止水平滚动 */
+ padding-top: 72px; /* 添加这行,为固定导航栏留出空间 */
+}
+
+.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;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.nav-left {
+ display: flex;
+ align-items: center;
+}
+
+.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;
+}
+
+.wallet-button:hover {
+ background: #8A5FF3 !important;
+ opacity: 0.9;
+}
+
+/* 主内容区域 */
+.app-main {
+ padding-top: 72px;
+ min-height: calc(100vh - 72px);
+ width: 100%;
+}
+
+/* 连接钱包提示 */
+.connect-wallet {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: calc(100vh - 72px);
+}
+
+.connect-card {
+ background: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 24px;
+ padding: 3rem;
+ text-align: center;
+ max-width: 400px;
+ width: 100%;
+}
+
+.connect-icon {
+ font-size: 3rem;
+ margin-bottom: 1rem;
+ display: block;
+}
+
+.connect-card h2 {
+ margin: 1rem 0;
+ font-size: 1.5rem;
+ color: white;
+}
+
+.connect-card p {
+ color: rgba(255, 255, 255, 0.6);
+ margin: 0;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ .nav-content {
+ padding: 1rem;
+ }
+
+ .nav-right {
+ gap: 0.5rem;
+ }
+
+ .network-select {
+ padding: 0.5rem 1.5rem 0.5rem 0.75rem;
+ }
+
+ .wallet-button {
+ padding: 0.5rem 1rem !important;
+ }
+
+ .connect-card {
+ padding: 2rem 1rem;
+ margin: 1rem;
+ }
+}
\ No newline at end of file
diff --git a/app/src/style/CreatePoolForm.css b/app/src/style/CreatePoolForm.css
new file mode 100644
index 0000000..316846b
--- /dev/null
+++ b/app/src/style/CreatePoolForm.css
@@ -0,0 +1,212 @@
+.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 {
+ position: absolute;
+ top: 2rem;
+ left: 2rem;
+ background: rgba(255, 255, 255, 0.1);
+ border: none;
+ color: rgba(255, 255, 255, 0.6);
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ font-size: 0.9rem;
+ padding: 0.75rem 1rem;
+ border-radius: 12px;
+ transition: all 0.3s ease;
+ z-index: 10;
+}
+
+.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
new file mode 100644
index 0000000..fa8d549
--- /dev/null
+++ b/app/src/style/Dashboard.css
@@ -0,0 +1,235 @@
+.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/PoolItem.css b/app/src/style/PoolItem.css
new file mode 100644
index 0000000..9b4547a
--- /dev/null
+++ b/app/src/style/PoolItem.css
@@ -0,0 +1,171 @@
+.pool-item {
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 24px;
+ padding: 2rem;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ margin-bottom: 2rem;
+ backdrop-filter: blur(10px);
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);
+}
+
+/* 头部样式 */
+.pool-header {
+ margin-bottom: 1.5rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.pool-header h3 {
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: white;
+ margin: 0;
+}
+
+/* 详情样式 */
+.pool-info {
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 16px;
+ padding: 1.5rem;
+ margin-bottom: 1.5rem;
+}
+
+.pool-details {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.75rem 0;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.pool-details:last-child {
+ border-bottom: none;
+}
+
+.pool-label {
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 0.9rem;
+ font-weight: 500;
+}
+
+.pool-value {
+ color: white;
+ font-size: 0.9rem;
+ font-family: monospace;
+ max-width: 60%;
+ text-align: right;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* 价格信息区域 */
+.pool-price-info {
+ background: rgba(122, 79, 243, 0.1);
+ border-radius: 16px;
+ padding: 1.5rem;
+ margin: 1.5rem 0;
+ border: 1px solid rgba(122, 79, 243, 0.2);
+}
+
+.pool-price-info h4 {
+ color: white;
+ margin: 0 0 1rem 0;
+ font-size: 1.1rem;
+ font-weight: 600;
+}
+
+.loading-prices {
+ color: rgba(255, 255, 255, 0.6);
+ text-align: center;
+ padding: 1rem 0;
+}
+
+/* 操作按钮区域 */
+.pool-actions {
+ display: flex;
+ gap: 1rem;
+ margin: 1.5rem 0;
+}
+
+.action-button {
+ flex: 1;
+ background: linear-gradient(135deg, #7A4FF3, #9945FF);
+ color: white;
+ border: none;
+ border-radius: 12px;
+ padding: 0.75rem 1.5rem;
+ font-size: 0.95rem;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+}
+
+.action-button:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(122, 79, 243, 0.3);
+}
+
+.action-button:active {
+ transform: translateY(0);
+}
+
+/* 表单容器 */
+.form-container {
+ background: rgba(255, 255, 255, 0.03);
+ border-radius: 16px;
+ padding: 1.5rem;
+ margin-top: 1rem;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ .pool-item {
+ padding: 1.5rem;
+ }
+
+ .pool-info,
+ .pool-price-info {
+ padding: 1rem;
+ }
+
+ .pool-details {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ }
+
+ .pool-value {
+ max-width: 100%;
+ text-align: left;
+ }
+
+ .pool-actions {
+ flex-direction: column;
+ }
+
+ .action-button {
+ width: 100%;
+ }
+}
+
+/* 动画效果 */
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.form-container {
+ animation: fadeIn 0.3s ease-out;
+}
\ No newline at end of file
diff --git a/app/src/style/PoolList.css b/app/src/style/PoolList.css
new file mode 100644
index 0000000..c019ce9
--- /dev/null
+++ b/app/src/style/PoolList.css
@@ -0,0 +1,212 @@
+.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;
+}
+
+.pool-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ width: 100%;
+ padding: 1rem 0;
+ box-sizing: border-box;
+}
+
+.pool-list-item {
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 16px;
+ padding: 1.5rem;
+ width: 100%;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ cursor: pointer;
+ transition: all 0.3s ease;
+ box-sizing: border-box;
+ margin: 0;
+ max-width: 100%;
+ overflow: hidden;
+}
+
+.pool-tokens {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ width: 100%;
+}
+
+.token-info {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ flex: 1;
+}
+
+.token-label {
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 0.9rem;
+}
+
+.token-address {
+ color: white;
+ font-size: 1rem;
+ word-break: break-all;
+}
+
+.token-separator {
+ color: rgba(255, 255, 255, 0.3);
+ margin: 0 1rem;
+}
+
+.pool-list-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 2rem;
+ width: 100%;
+}
+
+.pool-list-item:hover {
+ transform: translateY(-2px);
+ background: rgba(255, 255, 255, 0.08);
+ border-color: rgba(122, 79, 243, 0.5);
+ box-shadow: 0 4px 20px rgba(122, 79, 243, 0.15);
+}
+
+.pool-view-details {
+ position: absolute;
+ bottom: 1rem;
+ right: 1.5rem;
+ color: #7A4FF3;
+ font-weight: 500;
+ font-size: 0.9rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ opacity: 0;
+ transition: all 0.3s ease;
+}
+
+.pool-list-item:hover .pool-view-details {
+ opacity: 1;
+ transform: translateX(-5px);
+}
+
+/* 返回按钮样式 */
+.back-button {
+ align-self: flex-start;
+ background: transparent;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: white;
+ padding: 0.75rem 1.5rem;
+ border-radius: 12px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ margin-bottom: 2rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ font-weight: 500;
+}
+
+.back-button:hover {
+ background: rgba(255, 255, 255, 0.05);
+ transform: translateX(-2px);
+}
+
+/* 加载状态样式 */
+.pool-list-loading {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 200px;
+ color: rgba(255, 255, 255, 0.6);
+ gap: 1rem;
+}
+
+.loading-spinner {
+ width: 40px;
+ height: 40px;
+ border: 3px solid rgba(255, 255, 255, 0.1);
+ border-radius: 50%;
+ border-top-color: #7A4FF3;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* 无数据状态样式 */
+.no-pools-message {
+ text-align: center;
+ color: rgba(255, 255, 255, 0.6);
+ padding: 3rem;
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 16px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+/* 响应式设计 */
+@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);
+ }
+}
\ No newline at end of file
diff --git a/app/src/style/index.css b/app/src/style/index.css
new file mode 100644
index 0000000..5aa34a0
--- /dev/null
+++ b/app/src/style/index.css
@@ -0,0 +1,198 @@
+:root {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+html, body {
+ width: 100%;
+ min-height: 100vh;
+ background-color: #1C1C28;
+ color: white;
+ margin: 0;
+ padding: 0;
+}
+
+/* 2. 布局容器 */
+.container {
+ min-height: 100vh;
+ width: 100%;
+}
+
+.main-content {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 24px;
+}
+
+/* 3. 导航栏样式 */
+.navbar {
+ width: 100%;
+ background-color: rgba(255, 255, 255, 0.05);
+ backdrop-filter: blur(10px);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ position: fixed;
+ top: 0;
+ z-index: 100;
+}
+
+.navbar-content {
+ width: 100%;
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 2rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 64px;
+}
+
+/* 4. 网络选择器样式 */
+.solana-network-select-container {
+ position: relative;
+ display: inline-block;
+}
+
+.solana-network-select {
+ appearance: none;
+ background-color: #1C1C1C;
+ border: 1px solid #383838;
+ border-radius: 12px;
+ padding: 8px 36px 8px 12px;
+ font-size: 14px;
+ font-weight: 500;
+ color: #FFFFFF;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ min-width: 160px;
+ height: 40px;
+}
+
+.solana-network-select:hover {
+ border-color: #4E44CE;
+ background-color: #232323;
+}
+
+.solana-network-select:focus {
+ outline: none;
+ border-color: #9945FF;
+ box-shadow: 0 0 0 2px rgba(153, 69, 255, 0.1);
+}
+
+.solana-network-select-container::after {
+ content: '';
+ position: absolute;
+ right: 12px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 0;
+ height: 0;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-top: 5px solid #FFFFFF;
+ pointer-events: none;
+}
+
+/* 7. 按钮样式 */
+.action-button,
+.lending-pool-action-button,
+.lending-pool-refresh-button {
+ background-color: #512da8;
+ color: white;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 14px;
+ margin: 5px;
+ transition: all 0.3s ease;
+ width: auto;
+}
+
+.action-button:hover,
+.lending-pool-action-button:hover,
+.lending-pool-refresh-button:hover {
+ background-color: #45a049;
+ transform: translateY(-2px);
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
+}
+
+.action-button:disabled,
+.lending-pool-action-button:disabled,
+.lending-pool-refresh-button:disabled {
+ background-color: #cccccc;
+ cursor: not-allowed;
+ transform: none;
+ box-shadow: none;
+ opacity: 0.7;
+}
+
+/* 8. 消息提示样式 */
+.error-message {
+ background-color: rgba(255, 0, 0, 0.1);
+ border: 1px solid rgba(255, 0, 0, 0.2);
+ color: #ff4444;
+ padding: 12px;
+ border-radius: 4px;
+ margin-bottom: 16px;
+}
+
+.success-message {
+ background-color: rgba(0, 255, 0, 0.1);
+ border: 1px solid rgba(0, 255, 0, 0.2);
+ color: #4CAF50;
+ padding: 12px;
+ border-radius: 4px;
+ margin-bottom: 16px;
+}
+
+/* 9. 加载和空状态 */
+.loading {
+ text-align: center;
+ color: rgba(255, 255, 255, 0.6);
+ padding: 20px;
+}
+
+.empty-state {
+ text-align: center;
+ color: rgba(255, 255, 255, 0.6);
+ padding: 40px;
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 8px;
+}
+
+/* 10. 钱包按钮样式 */
+.wallet-adapter-button {
+ height: 40px !important;
+ padding: 0 16px !important;
+ border-radius: 12px !important;
+ background-color: #4E44CE !important;
+ transition: all 0.2s ease !important;
+}
+
+.wallet-adapter-button:hover {
+ background-color: #5a50d8 !important;
+}
+
+/* 11. 响应式设计 */
+@media (max-width: 768px) {
+ .navbar-content {
+ padding: 0 1rem;
+ }
+
+ .navigation-tabs {
+ flex-wrap: wrap;
+ }
+
+ .nav-tab {
+ flex: 1 1 calc(50% - 0.5rem);
+ }
+
+ .pool-list-container {
+ padding: 10px;
+ }
+} */
\ No newline at end of file
diff --git a/app/src/utils/checkCreditPool.ts b/app/src/utils/checkCreditPool.ts
new file mode 100644
index 0000000..6303979
--- /dev/null
+++ b/app/src/utils/checkCreditPool.ts
@@ -0,0 +1,85 @@
+import * as anchor from '@coral-xyz/anchor';
+import { Connection, PublicKey } from '@solana/web3.js';
+import fallIdl from '../idl/fall.json';
+import {
+ LENDING_AUTHORITY_SEED,
+ COLLATERAL_TOKEN_SEED,
+ LENDER_LENDING_BLOCK_HEIGHT_TOKEN_SEED,
+} from './constants';
+import { Idl } from '@coral-xyz/anchor';
+import { AnchorWallet } from '@solana/wallet-adapter-react';
+
+export interface CheckCreditPoolResult {
+ isCreditPoolInitialized: boolean;
+}
+
+export async function checkCreditPool(
+ wallet: AnchorWallet,
+ connection: Connection,
+ poolPda: PublicKey,
+ mintA: PublicKey,
+): Promise {
+ try {
+ const provider = new anchor.AnchorProvider(
+ connection,
+ wallet,
+ {
+ commitment: "confirmed",
+ preflightCommitment: "confirmed"
+ }
+ );
+
+ const program = new anchor.Program(
+ (fallIdl as any) as Idl,
+ provider
+ ) as any;
+
+ const [lendingPoolAuthorityPda] = PublicKey.findProgramAddressSync(
+ [poolPda.toBuffer(), Buffer.from(LENDING_AUTHORITY_SEED)],
+ program.programId
+ );
+ const [collateralReceiptTokenMint] = PublicKey.findProgramAddressSync(
+ [poolPda.toBuffer(), Buffer.from(COLLATERAL_TOKEN_SEED)],
+ program.programId
+ );
+ const lendingPoolAccountA = await anchor.utils.token.associatedAddress({
+ mint: mintA,
+ owner: lendingPoolAuthorityPda
+ });
+ const [lenderLendingBlockHeightMint] = PublicKey.findProgramAddressSync(
+ [poolPda.toBuffer(), Buffer.from(LENDER_LENDING_BLOCK_HEIGHT_TOKEN_SEED)],
+ program.programId
+ );
+
+ console.log('Step 1: Initializing lending pool...');
+ if (!await accountExists(connection, lendingPoolAccountA)) {
+ return {
+ isCreditPoolInitialized: false
+ }
+ }
+ console.log('Step 2: Initializing lending pool...');
+ if (!await accountExists(connection, collateralReceiptTokenMint)) {
+ return {
+ isCreditPoolInitialized: false
+ }
+ }
+ console.log('Step 3: Initializing lending pool...');
+ if (!await accountExists(connection, lenderLendingBlockHeightMint)) {
+ return {
+ isCreditPoolInitialized: false
+ }
+ }
+ return {
+ isCreditPoolInitialized: true
+ };
+ } catch (error) {
+ console.error('Error', error);
+ }
+ return {
+ isCreditPoolInitialized: false
+ };
+}
+async function accountExists(connection: Connection, publicKey: PublicKey): Promise {
+ const account = await connection.getAccountInfo(publicKey);
+ return account !== null;
+}
\ No newline at end of file
diff --git a/app/src/utils/constants.ts b/app/src/utils/constants.ts
index b9b24c5..bbf9a16 100644
--- a/app/src/utils/constants.ts
+++ b/app/src/utils/constants.ts
@@ -17,4 +17,6 @@ export const BORROWER_BORROW_BLOCK_HEIGHT_TOKEN_SEED= "j"; // borrow_height_toke
// Constants
export const MINIMUM_LIQUIDITY = 100;
-export const PRICE_SCALE = 1_000_000_000;
\ No newline at end of file
+export const PRICE_SCALE = 1_000_000_000;
+
+export const EXCLUDED_PUBLIC_KEY = 'GUXNPX5ci1Qj76MZe2aRJ33zK48VmT6gXVyR86CsF4T5';
diff --git a/app/src/utils/initPool.ts b/app/src/utils/createCreditPool.ts
similarity index 99%
rename from app/src/utils/initPool.ts
rename to app/src/utils/createCreditPool.ts
index 8b53513..d168771 100644
--- a/app/src/utils/initPool.ts
+++ b/app/src/utils/createCreditPool.ts
@@ -20,7 +20,7 @@ export interface InitPoolResult {
initTx3: string;
}
-export async function initPool(
+export async function createCreditPool(
wallet: AnchorWallet,
connection: Connection,
poolPda: PublicKey,
diff --git a/app/src/utils/withdraw.ts b/app/src/utils/withdraw.ts
new file mode 100644
index 0000000..fb49ae2
--- /dev/null
+++ b/app/src/utils/withdraw.ts
@@ -0,0 +1,117 @@
+import * as anchor from '@coral-xyz/anchor';
+import { Connection, PublicKey, SystemProgram } from '@solana/web3.js';
+import {
+ TOKEN_PROGRAM_ID,
+ ASSOCIATED_TOKEN_PROGRAM_ID,
+ getAssociatedTokenAddress
+} from '@solana/spl-token';
+import { Idl } from '@coral-xyz/anchor';
+import { BN } from 'bn.js';
+import fallIdl from '../idl/fall.json';
+import { AUTHORITY_SEED, LIQUIDITY_SEED } from './constants';
+
+export async function withdrawLiquidity(
+ wallet: any,
+ connection: Connection,
+ pool: PublicKey,
+ amm: PublicKey,
+ mintA: PublicKey,
+ mintB: PublicKey,
+ amountA: number,
+ amountB: number,
+) {
+ try {
+ const provider = new anchor.AnchorProvider(
+ connection,
+ wallet,
+ { preflightCommitment: "confirmed" }
+ );
+
+ const program = new anchor.Program(
+ (fallIdl as any) as Idl,
+ provider
+ ) as any;
+
+ const [poolAuthority] = PublicKey.findProgramAddressSync(
+ [
+ amm.toBuffer(),
+ mintA.toBuffer(),
+ mintB.toBuffer(),
+ Buffer.from(AUTHORITY_SEED)
+ ],
+ program.programId
+ );
+
+ const [mintLiquidity] = PublicKey.findProgramAddressSync(
+ [
+ pool.toBuffer(),
+ Buffer.from(LIQUIDITY_SEED)
+ ],
+ program.programId
+ );
+
+ const poolAccountA = await getAssociatedTokenAddress(
+ mintA,
+ poolAuthority,
+ true
+ );
+
+ const poolAccountB = await getAssociatedTokenAddress(
+ mintB,
+ poolAuthority,
+ true
+ );
+
+ const depositorAccountA = await getAssociatedTokenAddress(
+ mintA,
+ wallet.publicKey,
+ true
+ );
+
+ const depositorAccountB = await getAssociatedTokenAddress(
+ mintB,
+ wallet.publicKey,
+ true
+ );
+
+ const depositorAccountLiquidity = await getAssociatedTokenAddress(
+ mintLiquidity,
+ wallet.publicKey,
+ true
+ );
+
+ const tx = await program.methods
+ .depositLiquidity(
+ new BN(amountA),
+ new BN(amountB)
+ )
+ .accounts({
+ pool,
+ poolAuthority,
+ mintA,
+ mintB,
+ depositor: wallet.publicKey,
+ mintLiquidity,
+ poolAccountA,
+ poolAccountB,
+ depositorAccountLiquidity,
+ depositorAccountA,
+ depositorAccountB,
+ payer: wallet.publicKey,
+ tokenProgram: TOKEN_PROGRAM_ID,
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
+ systemProgram: SystemProgram.programId,
+ })
+ .rpc(
+ {
+ skipPreflight: false,
+ maxRetries: 3,
+ commitment: 'confirmed',
+ }
+ );
+ return tx;
+ } catch (error) {
+ console.error('Error in depositLiquidity:', error);
+ throw error;
+ }
+}
\ No newline at end of file
diff --git a/programs/fall/src/lib.rs b/programs/fall/src/lib.rs
index 032a942..57075b5 100644
--- a/programs/fall/src/lib.rs
+++ b/programs/fall/src/lib.rs
@@ -5,7 +5,7 @@ mod constants;
mod instructions;
mod state;
-declare_id!("BmzRwgDaqP6cSLoA2u2A7NysVF8nbbw8xRomRUc3DTt2");
+declare_id!("C8sj5h7nejVHeyWvq6aCwrFWY5Q5QGuZDPqVwVHPxNLm");
#[program]