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:
mindesbunister
2025-11-19 20:35:32 +01:00
parent 8d53c4bbad
commit c37a9a37d3
5 changed files with 60 additions and 9 deletions

4
.env
View File

@@ -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

View File

@@ -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
View File

@@ -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",

View File

@@ -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",

View File

@@ -30,6 +30,8 @@
".next/types/**/*.ts" ".next/types/**/*.ts"
], ],
"exclude": [ "exclude": [
"node_modules" "node_modules",
"archive",
".archive"
] ]
} }