@saberhq/stableswap-sdk
Version:
Solana SDK for Saber's StableSwap program.
409 lines (384 loc) • 11.1 kB
text/typescript
import type { u64 } from "@saberhq/token-utils";
import type { PublicKey, TransactionInstruction } from "@solana/web3.js";
import type { Fees, RawFees } from "../state/index.js";
import { encodeFees, ZERO_FEES } from "../state/index.js";
import type { StableSwapConfig } from "./common.js";
import { buildInstruction } from "./common.js";
import {
DepositIXLayout,
InitializeSwapIXLayout,
SwapIXLayout,
WithdrawIXLayout,
WithdrawOneIXLayout,
} from "./layouts.js";
/**
* Instruction enum.
*/
export enum StableSwapInstruction {
INITIALIZE = 0,
SWAP = 1,
DEPOSIT = 2,
WITHDRAW = 3,
WITHDRAW_ONE = 4,
}
/**
* Info about the tokens to swap.
*/
export interface SwapTokenInfo {
/**
* The account that admin fees go to.
*/
adminFeeAccount: PublicKey;
/**
* Mint of the token.
*/
mint: PublicKey;
/**
* This swap's token reserves.
*/
reserve: PublicKey;
}
export interface InitializeSwapInstruction {
config: StableSwapConfig;
/**
* Account that can manage the swap.
*/
adminAccount: PublicKey;
tokenA: SwapTokenInfo;
tokenB: SwapTokenInfo;
poolTokenMint: PublicKey;
/**
* Destination account for the initial LP tokens.
*/
destinationPoolTokenAccount: PublicKey;
nonce: number;
ampFactor: u64;
fees?: Fees;
isPaused?: boolean;
}
export interface SwapInstruction {
config: StableSwapConfig;
/**
* User source authority
*/
userAuthority: PublicKey;
/**
* User source token account
*/
userSource: PublicKey;
/**
* Swap source token account
*/
poolSource: PublicKey;
/**
* Swap destination token account
*/
poolDestination: PublicKey;
/**
* User destination token account
*/
userDestination: PublicKey;
adminDestination: PublicKey;
amountIn: u64;
minimumAmountOut: u64;
}
export interface DepositInstruction {
config: StableSwapConfig;
/**
* Authority for user account
*/
userAuthority: PublicKey;
/**
* Depositor account for token A
*/
sourceA: PublicKey;
/**
* Depositor account for token B
*/
sourceB: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
poolTokenMint: PublicKey;
poolTokenAccount: PublicKey;
tokenAmountA: u64;
tokenAmountB: u64;
minimumPoolTokenAmount: u64;
}
export interface WithdrawInstruction {
config: StableSwapConfig;
/**
* User source authority
*/
userAuthority: PublicKey;
poolMint: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
adminFeeAccountA: PublicKey;
adminFeeAccountB: PublicKey;
/**
* Account which is the source of the pool tokens
* that is; the user's pool token account
*/
sourceAccount: PublicKey;
userAccountA: PublicKey;
userAccountB: PublicKey;
poolTokenAmount: u64;
minimumTokenA: u64;
minimumTokenB: u64;
}
export interface WithdrawOneInstruction {
config: StableSwapConfig;
/**
* User source authority
*/
userAuthority: PublicKey;
poolMint: PublicKey;
/**
* User account that holds the LP tokens
*/
sourceAccount: PublicKey;
/**
* Pool account that holds the tokens to withdraw
*/
baseTokenAccount: PublicKey;
/**
* Pool account that holds the other token
*/
quoteTokenAccount: PublicKey;
/**
* User base token account to withdraw to
*/
destinationAccount: PublicKey;
/**
* Admin base token account to send fees to
*/
adminDestinationAccount: PublicKey;
/**
* Amount of pool tokens to burn. User receives an output of token a
* or b based on the percentage of the pool tokens that are returned.
*/
poolTokenAmount: u64;
/**
* Minimum amount of base tokens to receive, prevents excessive slippage
*/
minimumTokenAmount: u64;
}
export const initializeSwapInstructionRaw = ({
config,
adminAccount,
tokenA: {
adminFeeAccount: adminFeeAccountA,
mint: tokenMintA,
reserve: tokenAccountA,
},
tokenB: {
adminFeeAccount: adminFeeAccountB,
mint: tokenMintB,
reserve: tokenAccountB,
},
poolTokenMint,
destinationPoolTokenAccount,
nonce,
ampFactor,
fees,
}: Omit<InitializeSwapInstruction, "fees"> & {
fees: RawFees;
}): TransactionInstruction => {
const keys = [
{ pubkey: config.swapAccount, isSigner: false, isWritable: false },
{ pubkey: config.authority, isSigner: false, isWritable: false },
{ pubkey: adminAccount, isSigner: false, isWritable: false },
{ pubkey: adminFeeAccountA, isSigner: false, isWritable: false },
{ pubkey: adminFeeAccountB, isSigner: false, isWritable: false },
{ pubkey: tokenMintA, isSigner: false, isWritable: false },
{ pubkey: tokenAccountA, isSigner: false, isWritable: false },
{ pubkey: tokenMintB, isSigner: false, isWritable: false },
{ pubkey: tokenAccountB, isSigner: false, isWritable: false },
{ pubkey: poolTokenMint, isSigner: false, isWritable: true },
{ pubkey: destinationPoolTokenAccount, isSigner: false, isWritable: true },
{ pubkey: config.tokenProgramID, isSigner: false, isWritable: false },
];
const data = Buffer.alloc(InitializeSwapIXLayout.span);
InitializeSwapIXLayout.encode(
{
instruction: StableSwapInstruction.INITIALIZE, // InitializeSwap instruction
nonce,
ampFactor: ampFactor.toBuffer(),
fees,
},
data,
);
return buildInstruction({
config,
keys,
data,
});
};
export const initializeSwapInstruction = ({
fees = ZERO_FEES,
...args
}: InitializeSwapInstruction): TransactionInstruction => {
return initializeSwapInstructionRaw({ ...args, fees: encodeFees(fees) });
};
export const swapInstruction = ({
config,
userAuthority,
userSource,
poolSource,
poolDestination,
userDestination,
adminDestination,
amountIn,
minimumAmountOut,
}: SwapInstruction): TransactionInstruction => {
const data = Buffer.alloc(SwapIXLayout.span);
SwapIXLayout.encode(
{
instruction: StableSwapInstruction.SWAP, // Swap instruction
amountIn: amountIn.toBuffer(),
minimumAmountOut: minimumAmountOut.toBuffer(),
},
data,
);
const keys = [
{ pubkey: config.swapAccount, isSigner: false, isWritable: false },
{ pubkey: config.authority, isSigner: false, isWritable: false },
{ pubkey: userAuthority, isSigner: true, isWritable: false },
{ pubkey: userSource, isSigner: false, isWritable: true },
{ pubkey: poolSource, isSigner: false, isWritable: true },
{ pubkey: poolDestination, isSigner: false, isWritable: true },
{ pubkey: userDestination, isSigner: false, isWritable: true },
{ pubkey: adminDestination, isSigner: false, isWritable: true },
{ pubkey: config.tokenProgramID, isSigner: false, isWritable: false },
];
return buildInstruction({
config,
keys,
data,
});
};
export const depositInstruction = ({
config,
userAuthority,
sourceA,
sourceB,
tokenAccountA,
tokenAccountB,
poolTokenMint,
poolTokenAccount,
tokenAmountA,
tokenAmountB,
minimumPoolTokenAmount,
}: DepositInstruction): TransactionInstruction => {
const data = Buffer.alloc(DepositIXLayout.span);
DepositIXLayout.encode(
{
instruction: StableSwapInstruction.DEPOSIT, // Deposit instruction
tokenAmountA: tokenAmountA.toBuffer(),
tokenAmountB: tokenAmountB.toBuffer(),
minimumPoolTokenAmount: minimumPoolTokenAmount.toBuffer(),
},
data,
);
const keys = [
{ pubkey: config.swapAccount, isSigner: false, isWritable: false },
{ pubkey: config.authority, isSigner: false, isWritable: false },
{ pubkey: userAuthority, isSigner: true, isWritable: false },
{ pubkey: sourceA, isSigner: false, isWritable: true },
{ pubkey: sourceB, isSigner: false, isWritable: true },
{ pubkey: tokenAccountA, isSigner: false, isWritable: true },
{ pubkey: tokenAccountB, isSigner: false, isWritable: true },
{ pubkey: poolTokenMint, isSigner: false, isWritable: true },
{ pubkey: poolTokenAccount, isSigner: false, isWritable: true },
{ pubkey: config.tokenProgramID, isSigner: false, isWritable: false },
];
return buildInstruction({
config,
keys,
data,
});
};
export const withdrawInstruction = ({
config,
userAuthority,
poolMint,
sourceAccount,
tokenAccountA,
tokenAccountB,
userAccountA,
userAccountB,
adminFeeAccountA,
adminFeeAccountB,
poolTokenAmount,
minimumTokenA,
minimumTokenB,
}: WithdrawInstruction): TransactionInstruction => {
const data = Buffer.alloc(WithdrawIXLayout.span);
WithdrawIXLayout.encode(
{
instruction: StableSwapInstruction.WITHDRAW, // Withdraw instruction
poolTokenAmount: poolTokenAmount.toBuffer(),
minimumTokenA: minimumTokenA.toBuffer(),
minimumTokenB: minimumTokenB.toBuffer(),
},
data,
);
const keys = [
{ pubkey: config.swapAccount, isSigner: false, isWritable: false },
{ pubkey: config.authority, isSigner: false, isWritable: false },
{ pubkey: userAuthority, isSigner: true, isWritable: false },
{ pubkey: poolMint, isSigner: false, isWritable: true },
{ pubkey: sourceAccount, isSigner: false, isWritable: true },
{ pubkey: tokenAccountA, isSigner: false, isWritable: true },
{ pubkey: tokenAccountB, isSigner: false, isWritable: true },
{ pubkey: userAccountA, isSigner: false, isWritable: true },
{ pubkey: userAccountB, isSigner: false, isWritable: true },
{ pubkey: adminFeeAccountA, isSigner: false, isWritable: true },
{ pubkey: adminFeeAccountB, isSigner: false, isWritable: true },
{ pubkey: config.tokenProgramID, isSigner: false, isWritable: false },
];
return buildInstruction({
config,
keys,
data,
});
};
export const withdrawOneInstruction = ({
config,
userAuthority,
poolMint,
sourceAccount,
baseTokenAccount,
quoteTokenAccount,
destinationAccount,
adminDestinationAccount,
poolTokenAmount,
minimumTokenAmount,
}: WithdrawOneInstruction): TransactionInstruction => {
const data = Buffer.alloc(WithdrawOneIXLayout.span);
WithdrawOneIXLayout.encode(
{
instruction: StableSwapInstruction.WITHDRAW_ONE, // Withdraw instruction
poolTokenAmount: poolTokenAmount.toBuffer(),
minimumTokenAmount: minimumTokenAmount.toBuffer(),
},
data,
);
const keys = [
{ pubkey: config.swapAccount, isSigner: false, isWritable: false },
{ pubkey: config.authority, isSigner: false, isWritable: false },
{ pubkey: userAuthority, isSigner: true, isWritable: false },
{ pubkey: poolMint, isSigner: false, isWritable: true },
{ pubkey: sourceAccount, isSigner: false, isWritable: true },
{ pubkey: baseTokenAccount, isSigner: false, isWritable: true },
{ pubkey: quoteTokenAccount, isSigner: false, isWritable: true },
{ pubkey: destinationAccount, isSigner: false, isWritable: true },
{ pubkey: adminDestinationAccount, isSigner: false, isWritable: true },
{ pubkey: config.tokenProgramID, isSigner: false, isWritable: false },
];
return buildInstruction({
config,
keys,
data,
});
};