An example Anchor program demonstrating how to perform a CPI (Cross-Program Invocation) into Titan's swap_route_v2 instruction.
Disclaimer: This code is provided purely for illustration and educational purposes. It is not production-ready and should not be used as-is. Use at your own risk. See LICENSE for full terms.
The program uses a protocol_authority PDA to custody tokens in associated token account vaults and route swaps through Titan on behalf of users.
- Caller provides raw
swap_route_v2instruction data (obtained from Titan's API) - Program validates the discriminator,
amount, andminimum_amount_outmatch the provided parameters - Program CPIs into Titan's
swap_route_v2with theprotocol_authorityPDA as the signer/payer
To execute a swap via CPI, you need the serialized instruction data and the remaining accounts for the route. Both are obtained from the Titan Swap API.
The Titan Swap API is also available through the following providers:
The Titan API uses WebSocket with MessagePack encoding. Install the SDK:
npm install @titanexchange/sdk-ts bs58Connect and open a swap quote stream. Set userPublicKey to your protocolAuthority PDA since it will be the payer/signer for the Titan CPI.
import { V1Client } from "@titanexchange/sdk-ts";
import bs58 from "bs58";
import { PublicKey, VersionedTransaction } from "@solana/web3.js";
const TITAN_PROGRAM_ID = new PublicKey(
"T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT"
);
const client = await V1Client.connect(
`${process.env.WS_URL}?auth=${process.env.AUTH_TOKEN}`
);
const { stream } = await client.newSwapQuoteStream({
swap: {
inputMint: bs58.decode(inputMint.toBase58()),
outputMint: bs58.decode(outputMint.toBase58()),
amount: BigInt(amount),
slippageBps: 50,
},
transaction: {
userPublicKey: bs58.decode(protocolAuthority.toBase58()),
},
update: {
intervalMs: 1000,
numQuotes: 3,
},
});Each stream update contains quotes keyed by provider. Pick the best one by outAmount:
for await (const update of stream) {
const quotes = update.quotes;
if (!Object.keys(quotes).length) continue;
const bestRoute = Object.values(quotes).reduce((best, route) =>
route.outAmount > best.outAmount ? route : best
);
// Use bestRoute (see next step)
break;
}Each SwapRoute includes a transaction (serialized VersionedTransaction). Deserialize it to extract the Titan instruction data and remaining accounts:
const tx = VersionedTransaction.deserialize(bestRoute.transaction);
// Find the Titan swap instruction
const titanIx = tx.message.compiledInstructions.find(
(ix) =>
tx.message.staticAccountKeys[ix.programIdIndex].equals(TITAN_PROGRAM_ID)
);
// The first 8 fixed accounts (payer, atlas, inputMint, inputTokenAccount,
// outputMint, outputTokenAccount, inputTokenProgram, outputTokenProgram)
// are provided as named accounts in the Swap struct.
// The rest are passed as remainingAccounts.
const accountKeys = tx.message.getAccountKeys();
const remainingAccounts = titanIx.accountKeyIndexes.slice(8).map((idx) => ({
pubkey: accountKeys.get(idx),
isSigner: false,
isWritable: tx.message.isAccountWritable(idx),
}));
// Call your program
await program.methods
.swap(
Buffer.from(titanIx.data),
new BN(bestRoute.inAmount.toString()),
new BN(
(bestRoute.outAmount * BigInt(10000 - bestRoute.slippageBps)) /
BigInt(10000)
)
)
.accounts({
payer: payer.publicKey,
inputMint,
outputMint,
atlas,
})
.remainingAccounts(remainingAccounts)
.rpc();
await client.close();For full API details, see the Titan Swap API documentation. Also see the TypeScript SDK and Claude skill examples.
programs/titan-v2-cpi-example/src/
lib.rs -- Program entrypoint, swap instruction handler
constant.rs -- Titan program ID, swap_route_v2 discriminator
swap.rs -- Swap accounts struct, validation, and CPI logic
tests/
titan-v2-cpi-example.ts -- Validation tests
titan_swap.json -- Titan aggregator IDL (reference)
| Account | Description |
|---|---|
payer |
Signer, pays for output vault creation if needed |
protocolAuthority |
PDA (seeds = ["protocol_authority"]), owns the vaults, signs the Titan CPI |
inputMint |
SPL mint of the input token |
inputVault |
ATA of protocolAuthority for inputMint |
outputMint |
SPL mint of the output token |
outputVault |
ATA of protocolAuthority for outputMint (created via init_if_needed) |
atlas |
Titan's swap authority PDA |
titanProgram |
Titan aggregator program (T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT) |
Additional accounts required by the specific swap route are passed via remainingAccounts.
anchor buildanchor testThe tests verify on-chain validation logic (discriminator check, amount/slippage matching). The full CPI swap cannot be tested on localnet since the Titan program is only deployed on mainnet.
- Anchor 0.32.1
- Solana / Rust 1.89.0