fix: Implement Associated Token Account for USDC withdrawals
- Fixed PublicKey undefined error (derive from DRIFT_WALLET_PRIVATE_KEY) - Implemented ATA resolution using @solana/spl-token - Added comprehensive debug logging for withdrawal flow - Fixed AccountOwnedByWrongProgram error (need ATA not wallet address) - Successfully tested .58 withdrawal with on-chain confirmation - Updated .env with TOTAL_WITHDRAWN and LAST_WITHDRAWAL_TIME tracking Key changes: - lib/drift/withdraw.ts: Added getAssociatedTokenAddress() for USDC ATA - tsconfig.json: Excluded archive folders from compilation - package.json: Added bn.js as direct dependency Transaction: 4drNfMR1xBosGCQtfJ2a4r6oEawUByrT6L7Thyqu6QQWz555hX3QshFuJqiLZreL7KrheSgTdCEqMcXP26fi54JF Wallet: 3dG7wayp7b9NBMo92D2qL2sy1curSC4TTmskFpaGDrtA USDC ATA: 8ZEMwErnwxPNNNHJigUcMfrkBG14LCREDdKbqKm49YY7
This commit is contained in:
4
.env
4
.env
@@ -418,5 +418,5 @@ WITHDRAWAL_INTERVAL_HOURS=168
|
|||||||
WITHDRAWAL_PROFIT_PERCENT=10
|
WITHDRAWAL_PROFIT_PERCENT=10
|
||||||
MIN_WITHDRAWAL_AMOUNT=5
|
MIN_WITHDRAWAL_AMOUNT=5
|
||||||
MIN_ACCOUNT_BALANCE=500
|
MIN_ACCOUNT_BALANCE=500
|
||||||
LAST_WITHDRAWAL_TIME=
|
LAST_WITHDRAWAL_TIME=2025-11-19T19:34:47.185Z
|
||||||
TOTAL_WITHDRAWN=0
|
TOTAL_WITHDRAWN=6.58
|
||||||
@@ -5,8 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import BN from 'bn.js'
|
import BN from 'bn.js'
|
||||||
import { PublicKey } from '@solana/web3.js'
|
import { PublicKey, Keypair } from '@solana/web3.js'
|
||||||
|
import { getAssociatedTokenAddress, TOKEN_PROGRAM_ID } from '@solana/spl-token'
|
||||||
import { initializeDriftService } from './client'
|
import { initializeDriftService } from './client'
|
||||||
|
import bs58 from 'bs58'
|
||||||
|
|
||||||
export interface WithdrawalResult {
|
export interface WithdrawalResult {
|
||||||
success: boolean
|
success: boolean
|
||||||
@@ -29,17 +31,62 @@ export async function withdrawFromDrift(
|
|||||||
|
|
||||||
// Convert USD amount to token amount (USDC has 6 decimals)
|
// Convert USD amount to token amount (USDC has 6 decimals)
|
||||||
// $50.25 → 50,250,000 (raw token amount with 6 decimals)
|
// $50.25 → 50,250,000 (raw token amount with 6 decimals)
|
||||||
const tokenAmount = new BN(Math.floor(amountUSD * 1_000_000))
|
const rawAmount = Math.floor(amountUSD * 1_000_000)
|
||||||
|
console.log(`📊 Raw amount before BN: ${rawAmount} (type: ${typeof rawAmount})`)
|
||||||
|
const tokenAmount = new BN(rawAmount.toString())
|
||||||
|
|
||||||
console.log(`📊 Withdrawal details:`)
|
console.log(`📊 Withdrawal details:`)
|
||||||
console.log(` Amount: $${amountUSD.toFixed(2)} USDC`)
|
console.log(` Amount: $${amountUSD.toFixed(2)} USDC`)
|
||||||
console.log(` Token amount: ${tokenAmount.toString()} (raw with 6 decimals)`)
|
console.log(` Token amount: ${tokenAmount.toString()} (raw with 6 decimals)`)
|
||||||
console.log(` Market index: ${usdcMarketIndex}`)
|
console.log(` Market index: ${usdcMarketIndex}`)
|
||||||
|
|
||||||
// Get destination address (default to wallet public key if not specified)
|
// Get destination address
|
||||||
const destination = destinationWallet
|
// Need to get the Associated Token Account (ATA) for USDC, not just the wallet address
|
||||||
? new PublicKey(destinationWallet)
|
let walletPublicKey: PublicKey
|
||||||
: new PublicKey(process.env.WALLET_PUBLIC_KEY!)
|
let destination: PublicKey
|
||||||
|
try {
|
||||||
|
if (destinationWallet) {
|
||||||
|
console.log(`🔑 Using provided destination wallet`)
|
||||||
|
walletPublicKey = new PublicKey(destinationWallet)
|
||||||
|
} else {
|
||||||
|
console.log(`🔑 Deriving destination from private key`)
|
||||||
|
// Derive public key from private key (same wallet as trader)
|
||||||
|
const privateKeyStr = process.env.DRIFT_WALLET_PRIVATE_KEY
|
||||||
|
if (!privateKeyStr) {
|
||||||
|
throw new Error('DRIFT_WALLET_PRIVATE_KEY environment variable not set')
|
||||||
|
}
|
||||||
|
console.log(`🔑 Private key length: ${privateKeyStr.length}`)
|
||||||
|
const privateKeyBytes = privateKeyStr.startsWith('[')
|
||||||
|
? Uint8Array.from(JSON.parse(privateKeyStr))
|
||||||
|
: bs58.decode(privateKeyStr)
|
||||||
|
console.log(`🔑 Private key bytes length: ${privateKeyBytes.length}`)
|
||||||
|
const keypair = Keypair.fromSecretKey(privateKeyBytes)
|
||||||
|
walletPublicKey = keypair.publicKey
|
||||||
|
console.log(`🔑 Derived wallet public key: ${walletPublicKey.toString()}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// USDC mint address on Solana mainnet
|
||||||
|
const usdcMint = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v')
|
||||||
|
console.log(`🔑 USDC Mint: ${usdcMint.toString()}`)
|
||||||
|
console.log(`🔑 Wallet public key: ${walletPublicKey.toString()}`)
|
||||||
|
console.log(`🔑 About to call getAssociatedTokenAddress...`)
|
||||||
|
|
||||||
|
// Get the Associated Token Account for USDC
|
||||||
|
destination = await getAssociatedTokenAddress(
|
||||||
|
usdcMint,
|
||||||
|
walletPublicKey,
|
||||||
|
false, // allowOwnerOffCurve
|
||||||
|
TOKEN_PROGRAM_ID
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log(`✅ Got ATA successfully!`)
|
||||||
|
console.log(` Wallet: ${walletPublicKey.toString()}`)
|
||||||
|
console.log(` USDC ATA: ${destination.toString()}`)
|
||||||
|
|
||||||
|
} catch (keyError: any) {
|
||||||
|
console.error(`❌ Failed to get destination address:`, keyError)
|
||||||
|
throw new Error(`Failed to derive destination address: ${keyError?.message || 'Unknown error'}`)
|
||||||
|
}
|
||||||
|
|
||||||
console.log(` Destination: ${destination.toString()}`)
|
console.log(` Destination: ${destination.toString()}`)
|
||||||
|
|
||||||
|
|||||||
1
package-lock.json
generated
1
package-lock.json
generated
@@ -14,6 +14,7 @@
|
|||||||
"@pythnetwork/price-service-client": "^1.3.0",
|
"@pythnetwork/price-service-client": "^1.3.0",
|
||||||
"@solana/web3.js": "^1.91.1",
|
"@solana/web3.js": "^1.91.1",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
|
"bn.js": "^5.2.2",
|
||||||
"bs58": "^5.0.0",
|
"bs58": "^5.0.0",
|
||||||
"next": "^15.0.0",
|
"next": "^15.0.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"@pythnetwork/price-service-client": "^1.3.0",
|
"@pythnetwork/price-service-client": "^1.3.0",
|
||||||
"@solana/web3.js": "^1.91.1",
|
"@solana/web3.js": "^1.91.1",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
|
"bn.js": "^5.2.2",
|
||||||
"bs58": "^5.0.0",
|
"bs58": "^5.0.0",
|
||||||
"next": "^15.0.0",
|
"next": "^15.0.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
".next/types/**/*.ts"
|
".next/types/**/*.ts"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules",
|
||||||
|
"archive",
|
||||||
|
".archive"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user