Overview
Confirms and submits a signed fee claim transaction to the Solana blockchain. This endpoint validates the transaction structure, adds the LP owner’s signature, and broadcasts it to the network. The transaction claims fees from a Meteora DAMM v2 pool and transfers 70% to the configured destination address.
Pool locking mechanism : This endpoint uses a mutex lock system to prevent concurrent fee claims for the same pool, ensuring transaction integrity.
curl -X POST https://api.zcombinator.io/fee-claim/confirm \
-H "Content-Type: application/json" \
-d '{
"signedTransaction": "4MzR7dxJNJRVP1Q6k7Y3j8X...",
"requestId": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}'
Request Parameters
Base58 encoded signed transaction from the user (must match the transaction from /fee-claim/claim)
Unique identifier returned from the /fee-claim/claim endpoint
Response
Indicates if the transaction was submitted successfully
Transaction signature (transaction ID) on Solana
Address of the Meteora DAMM v2 pool
Mint address of Token A in the pool
Mint address of Token B in the pool
Address that received 70% of the claimed fees
Number of positions claimed from (always 1)
Object containing the claimed fee amounts:
tokenA (string): Total Token A fees claimed
tokenB (string): Total Token B fees claimed
Success Response
{
"success" : true ,
"signature" : "5J8Z9xKqH4nL2pR7vT3mB1cW6dF8yG4aS9jK2xM5nP8hQ3rV7wE1" ,
"poolAddress" : "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C" ,
"tokenAMint" : "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" ,
"tokenBMint" : "So11111111111111111111111111111111111111112" ,
"destinationAddress" : "FeeDestinationWalletAddress123456789" ,
"positionsCount" : 1 ,
"estimatedFees" : {
"tokenA" : "1000000" ,
"tokenB" : "500000000"
},
"message" : "Fee claim transaction submitted successfully"
}
Error Responses
{
"error" : "Missing required fields: signedTransaction and requestId"
}
{
"error" : "Fee claim request not found or expired. Please call /fee-claim/claim first."
}
The requestId is invalid, expired, or already used.
{
"error" : "Fee claim request expired. Please create a new request."
}
More than 10 minutes have passed since the claim request was created.
400 - Transaction Deserialization Failed
{
"error" : "Failed to deserialize transaction: Invalid transaction format"
}
The provided signedTransaction cannot be decoded.
{
"error" : "Invalid transaction: missing blockhash"
}
{
"error" : "Invalid transaction: blockhash is expired. Please create a new transaction."
}
The transaction’s blockhash is older than 150 slots (~60 seconds).
{
"error" : "Transaction fee payer mismatch"
}
The transaction’s fee payer doesn’t match the expected payer from the claim request.
400 - LP Owner Not Required
{
"error" : "Transaction verification failed: LP owner signature not required"
}
Security validation failed - LP owner must be a required signer.
400 - Fee Payer Not Signed
{
"error" : "Transaction verification failed: Fee payer has not signed"
}
The fee payer’s signature is missing from the transaction.
400 - Invalid Fee Payer Signature
{
"error" : "Transaction verification failed: Invalid fee payer signature"
}
The fee payer’s signature is invalid or doesn’t match.
400 - Unauthorized Program
{
"error" : "Invalid transaction: unauthorized program instruction detected" ,
"details" : "Instruction 3 uses unauthorized program: 11111111111111111111111111111112"
}
The transaction contains instructions from programs that aren’t allowed.
400 - Unauthorized Token Instruction
{
"error" : "Invalid transaction: unauthorized token instruction detected" ,
"details" : "Instruction 2 has invalid opcode: 7. Only Transfer (3) and TransferChecked (12) allowed."
}
Token program instructions must be Transfer or TransferChecked only.
400 - Invalid Transfer Authority
{
"error" : "Invalid transaction: transfer authority must be LP owner" ,
"details" : "Instruction 5 authority does not match LP owner"
}
Transfers must be signed by the LP owner.
400 - Unauthorized Destination
{
"error" : "Invalid transaction: transfer destination not authorized" ,
"details" : "Instruction 6 destination is not in allowed list"
}
Transfers can only go to the configured destination address or LP owner’s token accounts.
400 - Amount Exceeds Expected
{
"error" : "Invalid transaction: Token A transfer amount exceeds expected fees" ,
"details" : "Instruction 7 amount 2000000 exceeds Token A fees 1000000"
}
Transfer amounts cannot exceed the calculated claimable fees.
500 - Configuration Error
{
"error" : "Server configuration incomplete"
}
Required environment variables are not configured.
500 - Transaction Submission Failed
{
"error" : "Failed to confirm fee claim"
}
Process Flow
This endpoint performs extensive validation before submitting the transaction:
Validate Parameters
Ensures signedTransaction and requestId are provided
Retrieve Request Data
Looks up the fee claim request data using the requestId
Acquire Pool Lock
Obtains a mutex lock for the pool to prevent concurrent claims
Check Request Expiry
Verifies the request is not older than 10 minutes
Deserialize Transaction
Decodes the Base58 encoded transaction
Validate Blockhash
Checks that the transaction’s blockhash is recent and valid (within last 150 slots)
Verify Fee Payer
Ensures the fee payer matches the expected payer and has signed
Verify Fee Payer Signature
Cryptographically validates the fee payer’s signature using nacl
Validate Transaction Structure
Comprehensive security validation (see Security Validations section below)
Add LP Owner Signature
Protocol signs the transaction with the LP owner keypair
Submit Transaction
Broadcasts the fully-signed transaction to Solana with preflight checks
Wait for Confirmation
Attempts to wait for transaction confirmation (continues if timeout)
Cleanup
Removes the request data from memory and releases the pool lock
Security Validations
This endpoint implements comprehensive transaction validation to prevent attacks:
Prevents race conditions by ensuring only one fee claim can be processed per pool at a time using mutex locks.
Prevents replay attacks by verifying the blockhash is recent (within last 150 slots / ~60 seconds).
Only permits instructions from:
Token Program (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA)
Associated Token Program (ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL)
Compute Budget Program
Lighthouse Program (for optimization)
Meteora CP-AMM Program (CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C)
System Program (for native SOL transfers)
4. Token Instruction Validation
Only Transfer (opcode 3) and TransferChecked (opcode 12) are allowed
All transfers must be signed by the LP owner
Transfer amounts cannot exceed calculated fees
5. Destination Validation
Transfers can ONLY go to:
Configured destination address token accounts (70% recipient)
LP owner token accounts (30% remainder)
Any other destination is rejected.
6. System Transfer Validation
For native SOL transfers (when Token B is wSOL):
Only SystemProgram.transfer (instruction type 2) allowed
Must be from LP owner to configured destination address
Amount cannot exceed Token B fees
7. ATA Instruction Validation
Only CreateAssociatedTokenAccountIdempotent (opcode 1) is allowed for ATA program.
All transfer amounts are compared against stored expected fees to prevent over-claiming.
No authorization required : Since fee destinations are hardcoded and validated, anyone can trigger fee claims. The fee payer only covers transaction costs and cannot redirect funds.
Rate Limiting
This endpoint is subject to rate limiting:
10 requests per 5-minute window per IP
Returns HTTP 429 when limit exceeded
Transaction Confirmation
The API attempts to wait for transaction confirmation but continues even if confirmation times out:
Confirmation Commitment : confirmed
Timeout Handling : Logs warning but returns success
User Responsibility : Check transaction status on Solana explorer
You can view the transaction on Solscan: https://solscan.io/tx/{signature}
Pool Lock System
Mutex locks prevent concurrent processing :When a fee claim is being processed for a pool, subsequent requests for the same pool will wait for the lock to be released. This prevents:
Double-claiming fees
Transaction conflicts
Race conditions
The lock is automatically released when processing completes (success or error).
Request Cleanup
After successful submission:
Request data is immediately removed from memory
Pool lock is released
requestId cannot be reused
Logging
The endpoint logs comprehensive information for monitoring:
Pool address
Token mints and fee amounts
Destination address
Transaction signature
Solscan link for easy verification
Best Practices
Check Blockhash Age
Submit the signed transaction quickly after receiving it from /fee-claim/claim. Blockhashes expire after ~60 seconds.
Handle Expiry
If more than 10 minutes pass, create a new claim request instead of retrying with the old requestId.
Monitor Transaction
Use the returned signature to check transaction status on Solana explorers or via RPC.
Wait for Lock
If you receive a timeout or slow response, the pool may be locked by another concurrent request. Wait and retry.
Environment Variables
Required server configuration:
RPC_URL: Solana RPC endpoint
DAMM_POOL_ADDRESS: Meteora DAMM v2 pool address
PROTOCOL_PRIVATE_KEY: LP owner’s private key (Base58)
FEE_DESTINATION_ADDRESS: Address to receive 70% of claimed fees