import * as anchor from "@coral-xyz/anchor";
import { Program, AnchorProvider } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";
import {
TOKEN_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID,
getAssociatedTokenAddress,
getAccount,
createAssociatedTokenAccountInstruction,
} from "@solana/spl-token";
import idl from "./custom_stablecoins.json";
// Devnet addresses - see Key Addresses page for details
const PROGRAM_ID = new PublicKey("9vDwZVJXw5nxymWmUcgmNpemDH5EBcJwLNhtsznrgJDH");
const USDC_MINT = new PublicKey("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU");
const CUSTOM_TOKEN_MINT = new PublicKey("5P6MkoaCd9byPxH4X99kgKtS6SiuCQ67ZPCJzpXGkpCe"); // CBTUSD testnet token
// Initialize provider
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
// @ts-ignore - IDL type compatibility
const program = new Program(idl, provider);
async function swapUsdcForCustomToken() {
// Swap 0.1 USDC for custom token
const swapAmount = new anchor.BN(0.1 * 10 ** 6);
const minAmountOut = new anchor.BN(0.09 * 10 ** 6);
// Derive program addresses
const [pool] = PublicKey.findProgramAddressSync(
[Buffer.from("liquidity_pool")],
PROGRAM_ID
);
const [usdcVault] = PublicKey.findProgramAddressSync(
[Buffer.from("token_vault"), pool.toBuffer(), USDC_MINT.toBuffer()],
PROGRAM_ID
);
const [customTokenVault] = PublicKey.findProgramAddressSync(
[Buffer.from("token_vault"), pool.toBuffer(), CUSTOM_TOKEN_MINT.toBuffer()],
PROGRAM_ID
);
const [usdcVaultTokenAccount] = PublicKey.findProgramAddressSync(
[Buffer.from("vault_token_account"), usdcVault.toBuffer()],
PROGRAM_ID
);
const [customTokenVaultTokenAccount] = PublicKey.findProgramAddressSync(
[Buffer.from("vault_token_account"), customTokenVault.toBuffer()],
PROGRAM_ID
);
const [whitelist] = PublicKey.findProgramAddressSync(
[Buffer.from("address_whitelist")],
PROGRAM_ID
);
// Get user token accounts
const userUsdcAccount = await getAssociatedTokenAddress(
USDC_MINT,
provider.wallet.publicKey
);
const userCustomTokenAccount = await getAssociatedTokenAddress(
CUSTOM_TOKEN_MINT,
provider.wallet.publicKey
);
// Fetch pool to get fee recipient
const poolAccount = await (program.account as any).liquidityPool.fetch(pool);
const feeRecipientUsdcAccount = await getAssociatedTokenAddress(
USDC_MINT,
poolAccount.feeRecipient
);
// Check if destination account needs to be created
let needsAccountCreation = false;
try {
await getAccount(provider.connection, userCustomTokenAccount);
} catch {
needsAccountCreation = true;
}
// Build swap instruction
const swapIx = await program.methods
.swap(swapAmount, minAmountOut)
.accounts({
pool,
inVault: usdcVault,
outVault: customTokenVault,
inVaultTokenAccount: usdcVaultTokenAccount,
outVaultTokenAccount: customTokenVaultTokenAccount,
userFromTokenAccount: userUsdcAccount,
toTokenAccount: userCustomTokenAccount,
feeRecipientTokenAccount: feeRecipientUsdcAccount,
feeRecipient: poolAccount.feeRecipient,
fromMint: USDC_MINT,
toMint: CUSTOM_TOKEN_MINT,
user: provider.wallet.publicKey,
whitelist,
tokenProgram: TOKEN_PROGRAM_ID,
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
systemProgram: anchor.web3.SystemProgram.programId,
} as any)
.instruction();
// Build transaction
const transaction = new anchor.web3.Transaction();
if (needsAccountCreation) {
transaction.add(
createAssociatedTokenAccountInstruction(
provider.wallet.publicKey,
userCustomTokenAccount,
provider.wallet.publicKey,
CUSTOM_TOKEN_MINT
)
);
}
transaction.add(swapIx);
// Send transaction
try {
const tx = await provider.sendAndConfirm(transaction);
console.log("Swap successful!");
console.log("Transaction signature:", tx);
} catch (error: any) {
console.error("Swap failed:", error.message);
throw error;
}
}
swapUsdcForCustomToken()
.then(() => process.exit(0))
.catch((err) => { console.error(err); process.exit(1); });