0

I am working on a Solana Token Swap program, and I keep encountering the following error during the swap operation:

Program SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw invoke [1]
Program log: Instruction: Swap
Program log: Error: The provided token program does not match the token program 
expected by the swap
Program SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw consumed 5913 of 200000 compute 
units
Program SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw failed: custom program error: 0x18

I suspect this error is related to how I reference the token program IDs in the swap instructions. Here is a snippet of my code:

const swapInstruction = TokenSwap.swapInstruction(
tokenSwapStateAccount.publicKey,  // The public key of the token swap state account, which holds the swap configuration.
swapAuthority,                    // The swap authority derived from the swap state account, which controls the swap.
alice.publicKey,                  // The public key of the user (in this case, Alice)who is initiating the swap.
aliceTokenAATA,                   // Alice's associated token account (ATA) for Token A.
tokenATokenAccount,               // The pool's token A account.
tokenBTokenAccount,               // The pool's token B account.
aliceTokenBATA,                   // Alice's associated token account (ATA) for Token B.
poolTokenMint,                    // The mint address for the pool token.
tokenFeeAccountAddress,           // The token account for fees.
null,                             // No host fee account provided.
tokenAMint,                       // The mint address of Token A.
tokenBMint,                       // The mint address of Token B.
TOKEN_SWAP_PROGRAM_ID,            // Token Swap program ID.
token.TOKEN_PROGRAM_ID,           // The SPL Token Program ID for Token A (I'm using token.TOKEN_PROGRAM_ID here).
token.TOKEN_PROGRAM_ID,           // The SPL Token Program ID for Token B.
token.TOKEN_PROGRAM_ID,           // The SPL Token Program ID for the pool tokens.
1000000n,                         // The amount of Token A for the swap.
0n                                // The minimum amount of Token B Alice expects to receive.
);

I suspect there might be an issue with the token.TOKEN_PROGRAM_ID I am passing in for the token program, which is likely causing the mismatch. I am using the standard SPL Token Program ID (token.TOKEN_PROGRAM_ID), and I have also seen references to token.TOKEN_2022_PROGRAM_ID in other examples.

Here are the questions I need help with:

Is the token program ID I am using (token.TOKEN_PROGRAM_ID) incorrect for this context?

Should I be using a different program ID like token.TOKEN_2022_PROGRAM_ID when performing a swap, especially if I am using SPL Token 2022 features?

How do I ensure that the token program ID matches what the swap expects?

Here is the full code for context:

 import { Transaction, Keypair, SystemProgram, Connection, PublicKey, 
 TransactionInstruction } from '@solana/web3.js';
 import { TokenSwap, TOKEN_SWAP_PROGRAM_ID, TokenSwapLayout, CurveType } from 
 "@solana/spl-token-swap";
 import * as token from "@solana/spl-token";
 const fs = require("fs");

// Function to load a keypair from a JSON file
function loadKeypair(filename: string): Keypair {
const secret = JSON.parse(fs.readFileSync(filename).toString()) as number[];
const secretKey = Uint8Array.from(secret);
return Keypair.fromSecretKey(secretKey);
}

 // Function to create a token account creation instruction
 async function getTokenAccountCreationInstruction(mint: PublicKey, owner: PublicKey, 
payer: PublicKey): Promise<[PublicKey, TransactionInstruction]> {
let tokenAccountAddress = await token.getAssociatedTokenAddress(
    mint,    // mint
    owner,    // owner
    true    // allow owner off curve
);

const tokenAccountInstruction = await token.createAssociatedTokenAccountInstruction(
    payer,    // payer
    tokenAccountAddress,    // ata
    owner,    // owner
    mint    // mint
);

return [tokenAccountAddress, tokenAccountInstruction];
};

async function main() {
const connection = new Connection('https://api.devnet.solana.com');
const wallet = loadKeypair("BobvKCJ5vrThAjcwonS73Dhcv9pD8punaGwMkdXg3rp3.json");

// Transaction to initialize Token Swap state account
let transaction = new Transaction();
const tokenSwapStateAccount = 
loadKeypair("TSKrjj1kfu9sCmdEBVysHWgn6mdkeUpTruc7tMMzAwe.json");
const rent = await TokenSwap.getMinBalanceRentForExemptTokenSwap(connection);
const tokenSwapStateAccountCreationInstruction = await SystemProgram.createAccount({
    newAccountPubkey: tokenSwapStateAccount.publicKey,
    fromPubkey: wallet.publicKey,
    lamports: rent,
    space: TokenSwapLayout.span,
    programId: TOKEN_SWAP_PROGRAM_ID
});
transaction.add(tokenSwapStateAccountCreationInstruction);

// Generate swap authority
const [swapAuthority, bump] = await PublicKey.findProgramAddress(
    [tokenSwapStateAccount.publicKey.toBuffer()],
    TOKEN_SWAP_PROGRAM_ID,
);
console.log("swap authority: " + swapAuthority.toBase58());

// Token Mints for Token A and Token B
const tokenAMint = new PublicKey("AToi4sm8XMY49wNbsttKDTzJpEFAPLLiT79SdG5Yb49w");
const tokenBMint = new PublicKey("BTonyXJU4aAVq22XyKEzXH2WrBcs6eeTyBVnGJdzEDLQ");

// Create token accounts for Token A and Token B
const [tokenATokenAccount, taci] = await getTokenAccountCreationInstruction(tokenAMint, 
swapAuthority, wallet.publicKey);
const [tokenBTokenAccount, tbci] = await getTokenAccountCreationInstruction(tokenBMint, 
swapAuthority, wallet.publicKey);
transaction.add(taci, tbci);

console.log("token A account: " + tokenATokenAccount.toBase58());
console.log("token B account: " + tokenBTokenAccount.toBase58());

// Pool Token Mint and associated accounts
transaction = new Transaction();
const poolTokenMint = new PublicKey("LPTBed1dBW88U78HXxhPSpLMr5emcSuoZ31fwUTiR8x");
const tokenAccountPool = Keypair.generate();
const poolAccountRent = await token.getMinimumBalanceForRentExemptAccount(connection);
const createTokenAccountPoolInstruction = SystemProgram.createAccount({
    fromPubkey: wallet.publicKey,
    newAccountPubkey: tokenAccountPool.publicKey,
    space: token.ACCOUNT_SIZE,
    lamports: poolAccountRent,
    programId: token.TOKEN_PROGRAM_ID,
});

const initializeTokenAccountPoolInstruction = token.createInitializeAccountInstruction(
    tokenAccountPool.publicKey,
    poolTokenMint,
    wallet.publicKey
);

transaction.add(createTokenAccountPoolInstruction, 
initializeTokenAccountPoolInstruction);

// Fee account for the swap
const feeOwner = new PublicKey('HfoTxFR1Tm6kGmWgYWD6J7YHVy1UwqSULUGVLXkJqaKN');
const [tokenFeeAccountAddress, tfaci] = await getTokenAccountCreationInstruction(
    poolTokenMint,
    feeOwner,
    wallet.publicKey
);
transaction.add(tfaci);

// Initialize swap instruction
const tokenSwapInitSwapInstruction = TokenSwap.createInitSwapInstruction(
    tokenSwapStateAccount,
    swapAuthority,
    tokenATokenAccount,
    tokenBTokenAccount,
    poolTokenMint,
    tokenFeeAccountAddress,
    tokenAccountPool.publicKey,
    token.TOKEN_PROGRAM_ID,     // Token Program ID
    TOKEN_SWAP_PROGRAM_ID,      // Token Swap Program ID
    0n,                          // Trade fee numerator
    10000n,                      // Trade fee denominator
    5n,                          // Owner trade fee numerator
    10000n,                      // Owner trade fee denominator
    0n,                          // Owner withdraw fee numerator
    0n,                          // Owner withdraw fee denominator
    20n,                         // Host fee numerator
    100n,                        // Host fee denominator
    CurveType.ConstantProduct   // Curve type
);
transaction.add(tokenSwapInitSwapInstruction);

// Swap operation by Alice
const alice = loadKeypair("ALicWpLcgsR2aNFbHHWL9i7ibfJXX3dFWTMvvYodUHL1.json");
const aliceTokenAATA = new PublicKey("F5NGfhNZy6LreZhcWd5osZbtRkTmrABBPPQXcnVGnRku");
const aliceTokenBATA = new PublicKey("BcdzVEkVEZ1jJ2XKt1mBVnb8qECcHpE7kzZZ355u3LY3");

// Swap instruction to perform the token swap operation
const swapInstruction = TokenSwap.swapInstruction(
    tokenSwapStateAccount.publicKey,  // Token Swap state account
    swapAuthority,                    // Swap authority
    alice.publicKey,                  // Alice's public key
    aliceTokenAATA,                   // Alice's token account for Token A
    tokenATokenAccount,               // Pool token A account
    tokenBTokenAccount,               // Pool token B account
    aliceTokenBATA,                   // Alice's token account for Token B
    poolTokenMint,                    // Pool token mint
    tokenFeeAccountAddress,           // Fee account for swap
    null,                             // Host fee account (optional)
    tokenAMint,                       // Mint for Token A
    tokenBMint,                       // Mint for Token B
    TOKEN_SWAP_PROGRAM_ID,            // Token Swap program ID
    token.TOKEN_PROGRAM_ID,           // Source token program ID
    token.TOKEN_PROGRAM_ID,           // Destination token program ID
    token.TOKEN_PROGRAM_ID,           // Pool token program ID
    1000000n,                         // Amount of Token A Alice provides
    0n                                // Minimum amount of Token B Alice expects
);

// Create transaction for the swap and send it
const ta = new Transaction();
ta.add(swapInstruction);
const sx = await connection.sendTransaction(ta, [alice]);
console.log(sx);
}

main();

Any help is greatly appreciated :)

1
  • 1
    This all looks correct, it might be that the deployment at SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw is just too old -- if you want to test, you're probably better off building / deploying / testing your own token-swap program.
    – Jon C
    Commented Sep 11 at 11:34

1 Answer 1

1

Hi any luck debugging this? I'm working with the Token Swap Program too and running into errors, so reading through your code and discussing would be helpful.

A few places in your place that I wanted to ask:

  1. I don't seem to find the transaction to initialize the tokenSwap object to be sent and confirmed. Are you sure the tokenSwap object was successfully created? I've gotten an error similar to yours on initSwap

  2. My understanding is that a PDA (Program Derived Address) must be found AND initialized. This is the Swap Authority https://solanacookbook.com/references/accounts.html#create-a-pda This is actually the part I'm having issues with. If you look on Solana Explorer, the PDA address should say that it's owned by the Token Swap program.

  3. Did you make sure the Pool Token owner is set to the Swap Authority? https://spl.solana.com/token-swap#creating-a-new-token-swap-pool I see you have this: const poolTokenMint = new PublicKey("LPTBed1dBW88U78HXxhPSpLMr5emcSuoZ31fwUTiR8x"); Maybe you created this somewhere else

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.