Users were seeing "Transaction Successful" messages during offramp transactions, but their tokens were not actually being deducted from their Crossmint wallets. This created a confusing user experience where the system appeared to work but no actual token transfer occurred.
The issue was in the Crossmint external wallet signer implementation. When using external wallet signers, Crossmint API works in a two-step process:
- Create Transaction: POST to
/transfersendpoint returnsstatus: "awaiting-approval"with a message to sign - Submit Approval: Sign the message and POST to
/approvalsendpoint to complete the transaction
Our implementation was only performing step 1 and treating the "awaiting-approval" status as an error, but still returning success to the user because the transaction was created successfully.
Updated CrossmintService.executeTransferWithIdempotency() to automatically handle the approval process:
- Detect when transaction status is "awaiting-approval"
- Automatically sign the approval message using the appropriate private key
- Submit the approval to complete the transaction
- Return success only after the transaction is fully approved
Implemented support for both EVM and Solana chains with different signing methods:
EVM Chains (BSC, Base, Arbitrum, etc.):
- Uses
viemlibrary for message signing - Messages are in hex format
- Private key format:
0x...(hex)
Solana Chain:
- Uses
@solana/web3.js+tweetnaclfor message signing - Messages are in base64 format
- Private key format: base58 encoded
Added new environment variables for private keys:
# EVM private key (hex format) - corresponds to CROSSMINT_ADMIN_EVM_ADDRESS
CROSSMINT_ADMIN_EVM_PRIVATE_KEY=0x1234567890abcdef...
# Solana private key (base58 format) - corresponds to CROSSMINT_ADMIN_SOLANA_ADDRESS
CROSSMINT_ADMIN_SOLANA_PRIVATE_KEY=5J1F7GHaLrWmEqhrdcGjy3QSuK2w1QGdCdqMQ3CqW2mBvXcRtgHvW8...- Proper validation of private key configuration
- Address verification to ensure private keys match signer addresses
- Detailed logging for debugging transaction approval process
- Graceful fallback with clear error messages
- Updated
executeTransferWithIdempotency()to handle auto-approval - Added
submitTransactionApproval()method - Added
signEvmMessage()andsignSolanaMessage()methods - Added
isSolanaAddress()helper method - Added private key getters
- Added
viem: ^2.21.53for EVM message signing - Added
@solana/web3.js: ^1.95.4for Solana keypair management - Added
tweetnacl: ^1.0.3for Solana message signing - Added
bs58: ^1.3.3for base58 encoding/decoding
- Added
CROSSMINT_ADMIN_EVM_PRIVATE_KEYconfiguration - Added
CROSSMINT_ADMIN_SOLANA_PRIVATE_KEYconfiguration - Updated documentation for private key requirements
- User submits offramp transaction with PIN
- System validates PIN and wallet balances
crossmintService.transferTokens()is called- Crossmint API creates transaction with
status: "awaiting-approval" - NEW: System automatically signs the approval message
- NEW: System submits the approval to complete the transaction
- Transaction is fully executed and tokens are deducted
- User sees "Transaction Successful" only after actual completion
- Background process handles DexPay quote and bank transfer
- Configure valid private keys for both EVM and Solana admin addresses
- Ensure private keys correspond to the configured admin addresses
- Test with both staging and production Crossmint environments
- EVM Token Transfer: Test USDC/USDT transfers on BSC, Base, Arbitrum
- Solana Token Transfer: Test USDC/USDT transfers on Solana
- Error Handling: Test with invalid private keys, mismatched addresses
- Balance Verification: Confirm tokens are actually deducted after success message
- Monitor transaction approval success rates
- Track any approval failures in logs
- Verify blockchain confirmations for completed transfers
- Private keys must be securely stored and never logged
- Use environment variables or secure key management systems
- Rotate keys periodically following security best practices
- System validates that private keys match configured admin addresses
- Prevents accidental use of wrong private keys
- Logs address mismatches for security monitoring
- Idempotency keys prevent duplicate transactions
- Amount and recipient validation before signing
- Comprehensive error logging without exposing sensitive data
- Install new dependencies:
npm install viem @solana/web3.js tweetnacl bs58 - Configure
CROSSMINT_ADMIN_EVM_PRIVATE_KEYenvironment variable - Configure
CROSSMINT_ADMIN_SOLANA_PRIVATE_KEYenvironment variable - Verify private keys match admin addresses
- Test token transfers on staging environment
- Monitor transaction approval success rates
- Verify actual token deductions in user wallets
If issues occur, the system can be rolled back by:
- Reverting to previous CrossmintService implementation
- Removing auto-approval logic (transactions will fail but won't show false success)
- Implementing manual approval process as temporary measure
- Investigating and fixing any configuration issues
- Webhook Integration: Listen for Crossmint transaction status updates
- Retry Logic: Implement retry mechanism for failed approvals
- Multi-Signature Support: Support for multi-sig admin wallets
- Transaction Monitoring: Real-time monitoring of transaction status
- Key Rotation: Automated private key rotation system