@vaulverin/orca-sdk
Version:
Typescript SDK for the Orca protocol.
272 lines (271 loc) • 19.2 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OrcaPoolImpl = void 0;
const spl_token_1 = require("@solana/spl-token");
const orca_defaults_1 = require("../../../constants/orca-defaults");
const public_1 = require("../../../public");
const pool_instructions_1 = require("../../../public/utils/web3/instructions/pool-instructions");
const key_utils_1 = require("../../../public/utils/web3/key-utils");
const quote_builder_1 = require("../../quote/quote-builder");
class OrcaPoolImpl {
constructor(connection, network, config) {
this.connection = connection;
this.poolParams = config;
this.orcaTokenSwapId =
network === public_1.Network.MAINNET ? public_1.ORCA_TOKEN_SWAP_ID : public_1.ORCA_TOKEN_SWAP_ID_DEVNET;
}
getTokenA() {
const tokenId = this.poolParams.tokenIds[0];
return this.poolParams.tokens[tokenId];
}
getTokenB() {
const tokenId = this.poolParams.tokenIds[1];
return this.poolParams.tokens[tokenId];
}
getPoolTokenMint() {
return this.poolParams.poolTokenMint;
}
getLPBalance(owner) {
return __awaiter(this, void 0, void 0, function* () {
const address = yield (0, public_1.deriveAssociatedTokenAddress)(owner, this.poolParams.poolTokenMint);
const accountInfo = yield this.connection.getAccountInfo(address);
// User does not have a balance for this account
if (accountInfo == undefined) {
return public_1.OrcaU64.fromNumber(0, this.poolParams.poolTokenDecimals);
}
const result = (0, public_1.deserializeAccount)(accountInfo === null || accountInfo === void 0 ? void 0 : accountInfo.data);
if (result == undefined) {
throw new Error("Failed to parse user account for LP token.");
}
return public_1.OrcaU64.fromU64(result.amount, this.poolParams.poolTokenDecimals);
});
}
getLPSupply() {
return __awaiter(this, void 0, void 0, function* () {
const context = yield this.connection.getTokenSupply(this.poolParams.poolTokenMint);
const amt = new spl_token_1.u64(context.value.amount);
return public_1.OrcaU64.fromU64(amt, this.poolParams.poolTokenDecimals);
});
}
getQuote(inputToken, inputAmount, slippage) {
return __awaiter(this, void 0, void 0, function* () {
const { inputPoolToken, outputPoolToken } = (0, public_1.getTokens)(this.poolParams, inputToken.mint.toString());
const { inputTokenCount, outputTokenCount } = yield (0, public_1.getTokenCount)(this.connection, this.poolParams, inputPoolToken, outputPoolToken);
return this.getQuoteWithPoolAmounts(inputToken, inputAmount, inputTokenCount, outputTokenCount, slippage);
});
}
getQuoteWithPoolAmounts(inputToken, inputAmount, inputTokenPoolAmount, outputTokenPoolAmount, slippage) {
return __awaiter(this, void 0, void 0, function* () {
const slippageTolerance = slippage === undefined ? orca_defaults_1.defaultSlippagePercentage : public_1.Percentage.fromDecimal(slippage);
const feeStructure = this.poolParams.feeStructure;
const { inputPoolToken, outputPoolToken } = (0, public_1.getTokens)(this.poolParams, inputToken.mint.toString());
const inputAmountU64 = public_1.U64Utils.toTokenU64(inputAmount, inputPoolToken, "inputAmount");
const { value: { feeCalculator }, } = yield this.connection.getRecentBlockhashAndContext("singleGossip");
const quoteParams = {
inputToken: inputPoolToken,
outputToken: outputPoolToken,
inputTokenCount: inputTokenPoolAmount,
outputTokenCount: outputTokenPoolAmount,
feeStructure: feeStructure,
slippageTolerance: slippageTolerance,
lamportsPerSignature: feeCalculator.lamportsPerSignature,
amp: this.poolParams.amp !== undefined ? new spl_token_1.u64(this.poolParams.amp) : undefined,
};
const quoteBuilder = quote_builder_1.QuoteBuilderFactory.getBuilder(this.poolParams.curveType);
const quote = quoteBuilder === null || quoteBuilder === void 0 ? void 0 : quoteBuilder.buildQuote(quoteParams, inputAmountU64);
if (quote == undefined) {
throw new Error("Failed to get quote!");
}
return quote;
});
}
swap(owner, inputToken, amountIn, minimumAmountOut) {
return __awaiter(this, void 0, void 0, function* () {
const _owner = new key_utils_1.Owner(owner);
const ownerAddress = _owner.publicKey;
const { inputPoolToken, outputPoolToken } = (0, public_1.getTokens)(this.poolParams, inputToken.mint.toString());
const amountInU64 = public_1.U64Utils.toTokenU64(amountIn, inputPoolToken, "amountIn");
const minimumAmountOutU64 = public_1.U64Utils.toTokenU64(minimumAmountOut, outputPoolToken, "minimumAmountOut");
const _a = yield (0, public_1.resolveOrCreateAssociatedTokenAddress)(this.connection, _owner, inputPoolToken.mint, amountInU64), { address: inputPoolTokenUserAddress } = _a, resolveInputAddrInstructions = __rest(_a, ["address"]);
const _b = yield (0, public_1.resolveOrCreateAssociatedTokenAddress)(this.connection, _owner, outputPoolToken.mint), { address: outputPoolTokenUserAddress } = _b, resolveOutputAddrInstructions = __rest(_b, ["address"]);
if (inputPoolTokenUserAddress === undefined || outputPoolTokenUserAddress === undefined) {
throw new Error("Unable to derive input / output token associated address.");
}
const _c = (0, pool_instructions_1.createApprovalInstruction)(ownerAddress, amountInU64, inputPoolTokenUserAddress), { userTransferAuthority } = _c, approvalInstruction = __rest(_c, ["userTransferAuthority"]);
const swapInstruction = yield (0, pool_instructions_1.createSwapInstruction)(this.poolParams, _owner, inputPoolToken, inputPoolTokenUserAddress, outputPoolToken, outputPoolTokenUserAddress, amountInU64, minimumAmountOutU64, userTransferAuthority.publicKey, this.orcaTokenSwapId);
return yield new public_1.TransactionBuilder(this.connection, ownerAddress, _owner)
.addInstruction(resolveInputAddrInstructions)
.addInstruction(resolveOutputAddrInstructions)
.addInstruction(approvalInstruction)
.addInstruction(swapInstruction)
.build();
});
}
getDepositQuote(maxTokenAIn, maxTokenBIn, slippage) {
return __awaiter(this, void 0, void 0, function* () {
const slippageTolerance = slippage === undefined ? orca_defaults_1.defaultSlippagePercentage : public_1.Percentage.fromDecimal(slippage);
const maxTokenAIn_U64 = public_1.U64Utils.toTokenU64(maxTokenAIn, this.getTokenA(), "maxTokenAIn");
const maxTokenBIn_U64 = public_1.U64Utils.toTokenU64(maxTokenBIn, this.getTokenB(), "maxTokenBIn");
const { inputTokenCount: tokenAAmount, outputTokenCount: tokenBAmount } = yield (0, public_1.getTokenCount)(this.connection, this.poolParams, this.getTokenA(), this.getTokenB());
const lpSupply = yield this.getLPSupply();
if (tokenAAmount.eq(public_1.ZERO) || tokenBAmount.eq(public_1.ZERO)) {
return {
minPoolTokenAmountOut: public_1.OrcaU64.fromU64(public_1.ZERO, lpSupply.scale),
maxTokenAIn: public_1.OrcaU64.fromU64(maxTokenAIn_U64, this.getTokenA().scale),
maxTokenBIn: public_1.OrcaU64.fromU64(maxTokenBIn_U64, this.getTokenB().scale),
};
}
const poolTokenAmountWithA = maxTokenAIn_U64
.mul(slippageTolerance.denominator)
.mul(lpSupply.toU64())
.div(tokenAAmount)
.div(slippageTolerance.numerator.add(slippageTolerance.denominator));
const poolTokenAmountWithB = maxTokenBIn_U64
.mul(slippageTolerance.denominator)
.mul(lpSupply.toU64())
.div(tokenBAmount)
.div(slippageTolerance.numerator.add(slippageTolerance.denominator));
// Pick the smaller value of the two to calculate the minimum poolTokenAmount out
const minPoolTokenAmountOut_U64 = poolTokenAmountWithA.gt(poolTokenAmountWithB)
? poolTokenAmountWithB
: poolTokenAmountWithA;
return {
minPoolTokenAmountOut: public_1.OrcaU64.fromU64(minPoolTokenAmountOut_U64, lpSupply.scale),
maxTokenAIn: public_1.OrcaU64.fromU64(maxTokenAIn_U64, this.getTokenA().scale),
maxTokenBIn: public_1.OrcaU64.fromU64(maxTokenBIn_U64, this.getTokenB().scale),
};
});
}
deposit(owner, maxTokenAIn, maxTokenBIn, minPoolTokenAmountOut) {
return __awaiter(this, void 0, void 0, function* () {
const _owner = new key_utils_1.Owner(owner);
const ownerAddress = _owner.publicKey;
const tokenA = this.getTokenA();
const tokenB = this.getTokenB();
const maxTokenAIn_U64 = public_1.U64Utils.toTokenU64(maxTokenAIn, tokenA, "maxTokenAIn");
const maxTokenBIn_U64 = public_1.U64Utils.toTokenU64(maxTokenBIn, tokenB, "maxTokenBIn");
const minPoolTokenAmountOut_U64 = public_1.U64Utils.toPoolU64(minPoolTokenAmountOut, this.poolParams, "poolTokenAmount");
// If tokenA is SOL, this will create a new wSOL account with maxTokenAIn_U64
// Otherwise, get tokenA's associated token account
const _a = yield (0, public_1.resolveOrCreateAssociatedTokenAddress)(this.connection, _owner, tokenA.mint, maxTokenAIn_U64), { address: userTokenAPublicKey } = _a, resolveTokenAInstrucitons = __rest(_a, ["address"]);
// If tokenB is SOL, this will create a new wSOL account with maxTokenBIn_U64
// Otherwise, get tokenB's associated token account
const _b = yield (0, public_1.resolveOrCreateAssociatedTokenAddress)(this.connection, _owner, tokenB.mint, maxTokenBIn_U64), { address: userTokenBPublicKey } = _b, resolveTokenBInstrucitons = __rest(_b, ["address"]);
// If the user lacks the pool token account, create it
const _c = yield (0, public_1.resolveOrCreateAssociatedTokenAddress)(this.connection, _owner, this.poolParams.poolTokenMint), { address: userPoolTokenPublicKey } = _c, resolvePoolTokenInstructions = __rest(_c, ["address"]);
// Approve transfer of the tokens being deposited
const _d = (0, pool_instructions_1.createApprovalInstruction)(ownerAddress, maxTokenAIn_U64, userTokenAPublicKey), { userTransferAuthority } = _d, transferTokenAInstruction = __rest(_d, ["userTransferAuthority"]);
const transferTokenBInstruction = __rest((0, pool_instructions_1.createApprovalInstruction)(ownerAddress, maxTokenBIn_U64, userTokenBPublicKey, userTransferAuthority), []);
// Create the deposit instruction
const depositInstruction = yield (0, pool_instructions_1.createDepositInstruction)(this.poolParams, userTransferAuthority.publicKey, userTokenAPublicKey, userTokenBPublicKey, userPoolTokenPublicKey, minPoolTokenAmountOut_U64, maxTokenAIn_U64, maxTokenBIn_U64, tokenA.addr, tokenB.addr, this.orcaTokenSwapId, _owner);
return yield new public_1.TransactionBuilder(this.connection, ownerAddress, _owner)
.addInstruction(resolveTokenAInstrucitons)
.addInstruction(resolveTokenBInstrucitons)
.addInstruction(resolvePoolTokenInstructions)
.addInstruction(transferTokenAInstruction)
.addInstruction(transferTokenBInstruction)
.addInstruction(depositInstruction)
.build();
});
}
getWithdrawQuote(withdrawTokenAmount, withdrawTokenMint, slippage) {
return __awaiter(this, void 0, void 0, function* () {
const slippageTolerance = slippage === undefined ? orca_defaults_1.defaultSlippagePercentage : public_1.Percentage.fromDecimal(slippage);
const lpSupply = yield this.getLPSupply();
const { inputTokenCount: tokenAAmount, outputTokenCount: tokenBAmount } = yield (0, public_1.getTokenCount)(this.connection, this.poolParams, this.getTokenA(), this.getTokenB());
// withdrawTokenAmount needs represent amounts for one of the following: poolTokenAmount, tokenAAmount, or tokenBAmount
// determine which token this amount represents, then calculate poolTokenIn_U64
let poolTokenIn_U64 = public_1.ZERO;
if (withdrawTokenMint.equals(this.getPoolTokenMint())) {
poolTokenIn_U64 = public_1.U64Utils.toPoolU64(withdrawTokenAmount, this.poolParams, "withdrawTokenAmount");
}
else if (withdrawTokenMint.equals(this.getTokenA().mint) ||
withdrawTokenMint.equals(this.getTokenB().mint)) {
const token = withdrawTokenMint.equals(this.getTokenA().mint)
? this.getTokenA()
: this.getTokenB();
const totalAmount = token.mint.equals(this.getTokenA().mint) ? tokenAAmount : tokenBAmount;
const numerator = withdrawTokenAmount instanceof public_1.OrcaU64
? withdrawTokenAmount.toDecimal()
: withdrawTokenAmount;
const denominator = public_1.DecimalUtil.fromU64(totalAmount, token.scale);
const poolTokenIn = lpSupply.toDecimal().div(denominator).mul(numerator);
poolTokenIn_U64 = public_1.U64Utils.toPoolU64(poolTokenIn, this.poolParams, "poolTokenIn");
}
else {
throw new Error(`Unable to get withdraw quote with an invalid withdrawTokenMint ${withdrawTokenMint}`);
}
if (poolTokenIn_U64.eq(public_1.ZERO)) {
return {
maxPoolTokenAmountIn: public_1.OrcaU64.fromU64(public_1.ZERO, lpSupply.scale),
minTokenAOut: public_1.OrcaU64.fromU64(public_1.ZERO, this.getTokenA().scale),
minTokenBOut: public_1.OrcaU64.fromU64(public_1.ZERO, this.getTokenB().scale),
};
}
const minTokenAOut = new public_1.OrcaU64(poolTokenIn_U64
.mul(slippageTolerance.denominator)
.mul(tokenAAmount)
.div(lpSupply.toU64())
.div(slippageTolerance.numerator.add(slippageTolerance.denominator)), this.getTokenA().scale);
const minTokenBOut = new public_1.OrcaU64(poolTokenIn_U64
.mul(slippageTolerance.denominator)
.mul(tokenBAmount)
.div(lpSupply.toU64())
.div(slippageTolerance.numerator.add(slippageTolerance.denominator)), this.getTokenB().scale);
return {
maxPoolTokenAmountIn: public_1.OrcaU64.fromU64(poolTokenIn_U64, lpSupply.scale),
minTokenAOut,
minTokenBOut,
};
});
}
withdraw(owner, poolTokenAmountIn, minTokenAOut, minTokenBOut) {
return __awaiter(this, void 0, void 0, function* () {
const _owner = new key_utils_1.Owner(owner);
const ownerAddress = _owner.publicKey;
const tokenA = this.getTokenA();
const tokenB = this.getTokenB();
const minTokenAOut_U64 = public_1.U64Utils.toTokenU64(minTokenAOut, tokenA, "minTokenAOut");
const minTokenBOut_U64 = public_1.U64Utils.toTokenU64(minTokenBOut, tokenB, "minTokenBOut");
const poolTokenAmountIn_U64 = public_1.U64Utils.toPoolU64(poolTokenAmountIn, this.poolParams, "poolTokenAmountIn");
// Create a token account for tokenA, if necessary
const _a = yield (0, public_1.resolveOrCreateAssociatedTokenAddress)(this.connection, _owner, tokenA.mint), { address: userTokenAPublicKey } = _a, resolveTokenAInstrucitons = __rest(_a, ["address"]);
// Create a token account for tokenB, if necessary
const _b = yield (0, public_1.resolveOrCreateAssociatedTokenAddress)(this.connection, _owner, tokenB.mint), { address: userTokenBPublicKey } = _b, resolveTokenBInstrucitons = __rest(_b, ["address"]);
// Get user's poolToken token account
const _c = yield (0, public_1.resolveOrCreateAssociatedTokenAddress)(this.connection, _owner, this.poolParams.poolTokenMint), { address: userPoolTokenPublicKey } = _c, resolvePoolTokenInstructions = __rest(_c, ["address"]);
// Approve transfer of pool token
const _d = (0, pool_instructions_1.createApprovalInstruction)(ownerAddress, poolTokenAmountIn_U64, userPoolTokenPublicKey), { userTransferAuthority } = _d, transferPoolTokenInstruction = __rest(_d, ["userTransferAuthority"]);
// Create the withdraw instruction
const withdrawInstruction = yield (0, pool_instructions_1.createWithdrawInstruction)(this.poolParams, userTransferAuthority.publicKey, userTokenAPublicKey, userTokenBPublicKey, userPoolTokenPublicKey, poolTokenAmountIn_U64, minTokenAOut_U64, minTokenBOut_U64, tokenA.addr, tokenB.addr, this.orcaTokenSwapId, _owner);
return yield new public_1.TransactionBuilder(this.connection, ownerAddress, _owner)
.addInstruction(resolveTokenAInstrucitons)
.addInstruction(resolveTokenBInstrucitons)
.addInstruction(resolvePoolTokenInstructions)
.addInstruction(transferPoolTokenInstruction)
.addInstruction(withdrawInstruction)
.build();
});
}
}
exports.OrcaPoolImpl = OrcaPoolImpl;