Add more test for the daily volumes and add button to set the UIFee Factor

This commit is contained in:
2025-11-14 18:04:58 +07:00
parent d27df5de51
commit 479fcca662
3 changed files with 453 additions and 1 deletions

View File

@@ -222,6 +222,106 @@ export const useClaimUiFees = () => {
}
}
// ABI for the setUiFeeFactor function
const SET_UI_FEE_FACTOR_ABI = parseAbi([
'function setUiFeeFactor(uint256 uiFeeFactor) external',
])
// UI Fee Receiver contract address on Arbitrum (from Arbiscan)
const UI_FEE_RECEIVER_ADDRESS = '0x602b805EedddBbD9ddff44A7dcBD46cb07849685'
/**
* Custom hook to update UI fee factor on GMX ExchangeRouter contract
*/
export const useUpdateUiFee = () => {
const { address, isConnected } = useAccount()
const chainId = useChainId()
const { switchChainAsync } = useSwitchChain()
const { writeContractAsync } = useWriteContract()
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [txHash, setTxHash] = useState<string | null>(null)
/**
* Updates the UI fee factor by calling the ExchangeRouter contract
* @param percentage The fee percentage (e.g., 0.1 for 0.1%)
*/
const updateUiFeeFactor = async (percentage: number) => {
if (!isConnected || !address) {
throw new Error('Wallet not connected')
}
if (percentage < 0 || percentage > 100) {
throw new Error('Fee percentage must be between 0 and 100')
}
setIsLoading(true)
setError(null)
setTxHash(null)
try {
// Switch to Arbitrum if not already on it
if (chainId !== arbitrum.id) {
await switchChainAsync({ chainId: arbitrum.id })
}
// Convert percentage to basis points (1% = 100 basis points)
// GMX typically uses factors with 30 decimal places (10^30 total precision)
const basisPoints = Math.round(percentage * 100) // Convert to basis points
const feeFactor = BigInt(basisPoints) * BigInt(10) ** BigInt(26) // Multiply by 10^26 to get the factor (10^30 - 10^4 for basis points)
console.log('Updating UI fee factor:')
console.log(`Percentage: ${percentage}%`)
console.log(`Basis points: ${basisPoints}`)
console.log(`Fee factor: ${feeFactor.toString()}`)
// Call the setUiFeeFactor function on the contract
const hash = await writeContractAsync({
address: UI_FEE_RECEIVER_ADDRESS,
abi: SET_UI_FEE_FACTOR_ABI,
functionName: 'setUiFeeFactor',
args: [feeFactor],
})
setTxHash(hash)
return hash
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred'
setError(errorMessage)
throw new Error(`Failed to update UI fee factor: ${errorMessage}`)
} finally {
setIsLoading(false)
}
}
return {
updateUiFeeFactor,
isLoading,
error,
txHash,
isConnected,
address,
}
}
/**
* Hook to monitor UI fee update transaction status
*/
export const useUpdateUiFeeTransaction = (txHash: string | null) => {
const { data, isLoading, isSuccess, isError } = useWaitForTransactionReceipt({
hash: txHash as `0x${string}` | undefined,
chainId: arbitrum.id,
})
return {
transactionData: data,
isConfirming: isLoading,
isConfirmed: isSuccess,
isError,
}
}
// Export the allowed tickers for use in other components
export { ALLOWED_TICKERS, type AllowedTicker }

View File

@@ -5,7 +5,13 @@ import LogIn from '../../components/mollecules/LogIn/LogIn'
import useCookie from '../../hooks/useCookie'
import {useEffect, useState} from 'react'
import {useAuthStore} from '../../app/store/accountStore'
import {ALLOWED_TICKERS, useClaimUiFees, useClaimUiFeesTransaction} from '../../hooks/useClaimUiFees'
import {
ALLOWED_TICKERS,
useClaimUiFees,
useClaimUiFeesTransaction,
useUpdateUiFee,
useUpdateUiFeeTransaction
} from '../../hooks/useClaimUiFees'
import Toast from '../../components/mollecules/Toast/Toast'
import useApiUrlStore from '../../app/store/apiStore'
@@ -85,11 +91,19 @@ export const Auth = ({ children }: any) => {
const [isLoading, setIsLoading] = useState(true)
const [claimAccountAddress, setClaimAccountAddress] = useState('')
const [showClaimSection, setShowClaimSection] = useState(false)
// Update UI Fee state
const [uiFeePercentage, setUiFeePercentage] = useState('')
const [showUpdateUiFeeSection, setShowUpdateUiFeeSection] = useState(false)
// Claim UI fees hook
const { claimUiFees, isLoading: isClaimingFees, txHash, error: claimError } = useClaimUiFees()
const { isConfirming, isConfirmed, isError: txError } = useClaimUiFeesTransaction(txHash)
// Update UI fee hook
const { updateUiFeeFactor, isLoading: isUpdatingFee, txHash: updateTxHash, error: updateFeeError } = useUpdateUiFee()
const { isConfirming: isUpdateConfirming, isConfirmed: isUpdateConfirmed, isError: updateTxError } = useUpdateUiFeeTransaction(updateTxHash)
useEffect(() => {
if (ready) {
const timeout = setTimeout(() => {
@@ -122,6 +136,23 @@ export const Auth = ({ children }: any) => {
toast.update('error', 'Transaction failed')
}
}, [txError])
// Handle successful UI fee update
useEffect(() => {
if (isUpdateConfirmed) {
const toast = new Toast('Success')
toast.update('success', 'UI fee factor updated successfully!')
setUiFeePercentage('') // Clear the input
}
}, [isUpdateConfirmed])
// Handle UI fee update transaction error
useEffect(() => {
if (updateTxError) {
const toast = new Toast('Error')
toast.update('error', 'UI fee update transaction failed')
}
}, [updateTxError])
const handleClaimUiFees = async () => {
if (!isConnected) {
@@ -149,6 +180,33 @@ export const Auth = ({ children }: any) => {
}
}
const handleUpdateUiFee = async () => {
if (!isConnected) {
const toast = new Toast('Error')
toast.update('error', 'Please connect your wallet first')
return
}
const percentage = parseFloat(uiFeePercentage)
if (isNaN(percentage) || percentage < 0 || percentage > 100) {
const toast = new Toast('Error')
toast.update('error', 'Please enter a valid percentage between 0 and 100')
return
}
const toast = new Toast('Updating UI fee factor...')
try {
toast.update('info', 'Submitting transaction to update UI fee factor...')
await updateUiFeeFactor(percentage)
toast.update('info', 'Transaction submitted, waiting for confirmation...')
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred'
toast.update('error', `Failed to update UI fee factor: ${errorMessage}`)
console.error('Error updating UI fee factor:', err)
}
}
if (!ready || isLoading) {
return <div>Loading...</div>;
}
@@ -288,6 +346,125 @@ export const Auth = ({ children }: any) => {
)}
</div>
)}
{/* Update UI Fee Section */}
<div style={{ textAlign: 'center', marginTop: '20px' }}>
<button
onClick={() => setShowUpdateUiFeeSection(!showUpdateUiFeeSection)}
style={{
padding: '10px 20px',
backgroundColor: showUpdateUiFeeSection ? '#6B7280' : '#8B5CF6',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '16px'
}}
>
{showUpdateUiFeeSection ? 'Hide' : 'Update UI Fee'}
</button>
</div>
{showUpdateUiFeeSection && (
<div style={{
padding: '20px',
backgroundColor: '#F3F4F6',
borderRadius: '8px',
maxWidth: '400px',
width: '100%',
marginTop: '20px'
}}>
<h3 style={{ margin: '0 0 15px 0', fontSize: '18px', color: '#111827' }}>
Update GMX UI Fee Factor
</h3>
<p style={{ margin: '0 0 10px 0', fontSize: '14px', color: '#6B7280' }}>
Set the UI fee factor percentage for GMX trading. This will be automatically formatted for the smart contract.
</p>
<details style={{ marginBottom: '15px', fontSize: '12px', color: '#6B7280' }}>
<summary style={{ cursor: 'pointer', fontWeight: '500' }}>
How it works
</summary>
<div style={{
marginTop: '8px',
padding: '8px',
backgroundColor: '#E5E7EB',
borderRadius: '4px',
lineHeight: '1.6'
}}>
Enter a percentage (e.g., 0.1 for 0.1%). This gets converted to basis points and formatted for the GMX contract.
The fee factor determines the percentage of trading fees that go to the UI/interface.
</div>
</details>
{isConnected && walletAddress && (
<div style={{ marginBottom: '15px', fontSize: '12px', color: '#059669' }}>
Wallet connected: {walletAddress.slice(0, 6)}...{walletAddress.slice(-4)}
</div>
)}
{!isConnected && (
<div style={{ marginBottom: '15px', fontSize: '12px', color: '#DC2626' }}>
Please connect your wallet first
</div>
)}
<input
type="number"
placeholder="Enter fee percentage (e.g., 0.1)"
value={uiFeePercentage}
onChange={(e) => setUiFeePercentage(e.target.value)}
min="0"
max="100"
step="0.01"
style={{
width: '100%',
padding: '10px',
marginBottom: '15px',
border: '1px solid #D1D5DB',
borderRadius: '4px',
fontSize: '14px',
boxSizing: 'border-box'
}}
/>
<button
onClick={handleUpdateUiFee}
disabled={!isConnected || isUpdatingFee || isUpdateConfirming || !uiFeePercentage.trim()}
style={{
width: '100%',
padding: '10px 20px',
backgroundColor: (!isConnected || isUpdatingFee || isUpdateConfirming || !uiFeePercentage.trim()) ? '#9CA3AF' : '#8B5CF6',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: (!isConnected || isUpdatingFee || isUpdateConfirming || !uiFeePercentage.trim()) ? 'not-allowed' : 'pointer',
fontSize: '16px'
}}
>
{isUpdatingFee ? 'Signing...' : isUpdateConfirming ? 'Confirming...' : isUpdateConfirmed ? 'Updated!' : 'Update UI Fee Factor'}
</button>
{(updateFeeError) && (
<div style={{ marginTop: '10px', fontSize: '12px', color: '#DC2626' }}>
{updateFeeError}
</div>
)}
{updateTxHash && (
<div style={{ marginTop: '10px', fontSize: '12px' }}>
<p style={{ color: '#059669', margin: '0 0 5px 0' }}>Transaction Hash:</p>
<a
href={`https://arbiscan.io/tx/${updateTxHash}`}
target="_blank"
rel="noopener noreferrer"
style={{ color: '#8B5CF6', wordBreak: 'break-all' }}
>
{updateTxHash}
</a>
</div>
)}
</div>
)}
</div>
)
} else if (!token) {