@marinade.finance/kamino-sdk
Version:
219 lines • 10.7 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RaydiumService = void 0;
const web3_js_1 = require("@solana/web3.js");
const raydium_client_1 = require("../raydium_client");
const decimal_js_1 = __importDefault(require("decimal.js"));
const raydium_sdk_1 = require("@raydium-io/raydium-sdk");
const utils_1 = require("../utils");
const axios_1 = __importDefault(require("axios"));
const CreationParameters_1 = require("../utils/CreationParameters");
const programId_1 = require("../raydium_client/programId");
const raydium_1 = require("../utils/raydium");
class RaydiumService {
_connection;
_cluster;
constructor(connection, cluster) {
this._connection = connection;
this._cluster = cluster;
}
async getRaydiumWhirlpools() {
return (await axios_1.default.get(`https://api.hubbleprotocol.io/raydium/ammPools`)).data;
}
async getRaydiumPoolLiquidityDistribution(pool, keepOrder = true, lowestTick, highestTick) {
let raydiumLiqDistribution = (await axios_1.default.get(`https://api.hubbleprotocol.io/raydium/positionLine/${pool.toString()}`)).data;
const poolState = await raydium_client_1.PoolState.fetch(this._connection, pool);
if (!poolState) {
throw Error(`Raydium pool state ${pool} does not exist`);
}
let poolPrice = raydium_sdk_1.SqrtPriceMath.sqrtPriceX64ToPrice(poolState.sqrtPriceX64, poolState.mintDecimals0, poolState.mintDecimals1);
let liqDistribution = {
currentPrice: poolPrice,
currentTickIndex: poolState.tickCurrent,
distribution: [],
};
raydiumLiqDistribution.data.forEach((entry) => {
let tickIndex = (0, raydium_1.priceToTickIndexWithRounding)(entry.price);
if ((lowestTick && tickIndex < lowestTick) || (highestTick && tickIndex > highestTick)) {
return;
}
// if the prevoious entry has the same tick index, add to it
if (liqDistribution.distribution.length > 0 &&
liqDistribution.distribution[liqDistribution.distribution.length - 1].tickIndex === tickIndex) {
liqDistribution.distribution[liqDistribution.distribution.length - 1].liquidity = liqDistribution.distribution[liqDistribution.distribution.length - 1].liquidity.add(new decimal_js_1.default(entry.liquidity));
}
else {
let priceWithOrder = new decimal_js_1.default(entry.price);
if (!keepOrder) {
priceWithOrder = new decimal_js_1.default(1).div(priceWithOrder);
}
const liq = {
price: new decimal_js_1.default(priceWithOrder),
liquidity: new decimal_js_1.default(entry.liquidity),
tickIndex,
};
liqDistribution.distribution.push(liq);
}
});
return liqDistribution;
}
getStrategyWhirlpoolPoolAprApy = async (strategy, pools) => {
const position = await raydium_client_1.PersonalPositionState.fetch(this._connection, strategy.position);
if (!position) {
throw Error(`Position ${strategy.position} does not exist`);
}
const poolState = await raydium_client_1.PoolState.fetch(this._connection, strategy.pool);
if (!poolState) {
throw Error(`Raydium pool state ${strategy.pool} does not exist`);
}
if (!pools) {
({ data: pools } = await this.getRaydiumWhirlpools());
}
if (!pools || pools.length === 0) {
throw Error(`Could not get Raydium amm pools from Raydium API`);
}
const raydiumPool = pools.filter((d) => d.id === position.poolId.toString()).shift();
if (!raydiumPool) {
throw Error(`Could not get find Raydium amm pool ${strategy.pool} from Raydium API`);
}
const poolInfo = (await raydium_sdk_1.AmmV3.fetchMultiplePoolInfos({
connection: this._connection,
// @ts-ignore
poolKeys: [raydiumPool],
batchRequest: true,
chainTime: new Date().getTime() / 1000,
}))[strategy.pool.toString()].state;
const priceRange = (0, utils_1.getStrategyPriceRangeRaydium)(position.tickLowerIndex, position.tickUpperIndex, Number(poolState.tickCurrent.toString()), Number(strategy.tokenAMintDecimals.toString()), Number(strategy.tokenBMintDecimals.toString()));
if (priceRange.strategyOutOfRange) {
return {
...priceRange,
rewardsApy: [],
rewardsApr: [],
feeApy: utils_1.ZERO,
feeApr: utils_1.ZERO,
totalApy: utils_1.ZERO,
totalApr: utils_1.ZERO,
};
}
const params = {
poolInfo,
aprType: 'day',
positionTickLowerIndex: position.tickLowerIndex,
positionTickUpperIndex: position.tickUpperIndex,
};
const { apr, feeApr, rewardsApr } = raydium_sdk_1.AmmV3.estimateAprsForPriceRangeMultiplier(params);
const totalApr = new decimal_js_1.default(apr).div(100);
const fee = new decimal_js_1.default(feeApr).div(100);
const rewards = rewardsApr.map((reward) => new decimal_js_1.default(reward).div(100));
return {
totalApr,
totalApy: (0, utils_1.aprToApy)(totalApr, 365),
feeApr: fee,
feeApy: (0, utils_1.aprToApy)(fee, 365),
rewardsApr: rewards,
rewardsApy: rewards.map((x) => (0, utils_1.aprToApy)(x, 365)),
...priceRange,
};
};
getRaydiumPositionAprApy = async (poolPubkey, priceLower, priceUpper, pools) => {
const poolState = await raydium_client_1.PoolState.fetch(this._connection, poolPubkey);
if (!poolState) {
throw Error(`Raydium pool state ${poolPubkey} does not exist`);
}
if (!pools) {
({ data: pools } = await this.getRaydiumWhirlpools());
}
if (!pools || pools.length === 0) {
throw Error(`Could not get Raydium amm pools from Raydium API`);
}
const raydiumPool = pools.filter((d) => d.id === poolPubkey.toString()).shift();
if (!raydiumPool) {
throw Error(`Could not get find Raydium amm pool ${poolPubkey.toString()} from Raydium API`);
}
const poolInfo = (await raydium_sdk_1.AmmV3.fetchMultiplePoolInfos({
connection: this._connection,
// @ts-ignore
poolKeys: [raydiumPool],
batchRequest: true,
chainTime: new Date().getTime() / 1000,
}))[poolPubkey.toString()].state;
let tickLowerIndex = raydium_sdk_1.TickMath.getTickWithPriceAndTickspacing(priceLower, poolState.tickSpacing, poolState.mintDecimals0, poolState.mintDecimals1);
let tickUpperIndex = raydium_sdk_1.TickMath.getTickWithPriceAndTickspacing(priceUpper, poolState.tickSpacing, poolState.mintDecimals0, poolState.mintDecimals1);
const priceRange = (0, utils_1.getStrategyPriceRangeRaydium)(tickLowerIndex, tickUpperIndex, Number(poolState.tickCurrent.toString()), raydiumPool.mintDecimalsA, raydiumPool.mintDecimalsB);
if (priceRange.strategyOutOfRange) {
return {
...priceRange,
rewardsApy: [],
rewardsApr: [],
feeApy: utils_1.ZERO,
feeApr: utils_1.ZERO,
totalApy: utils_1.ZERO,
totalApr: utils_1.ZERO,
};
}
const params = {
poolInfo,
aprType: 'day',
positionTickLowerIndex: tickLowerIndex,
positionTickUpperIndex: tickUpperIndex,
};
const { apr, feeApr, rewardsApr } = raydium_sdk_1.AmmV3.estimateAprsForPriceRangeMultiplier(params);
const totalApr = new decimal_js_1.default(apr).div(100);
const fee = new decimal_js_1.default(feeApr).div(100);
const rewards = rewardsApr.map((reward) => new decimal_js_1.default(reward).div(100));
return {
totalApr,
totalApy: (0, utils_1.aprToApy)(totalApr, 365),
feeApr: fee,
feeApy: (0, utils_1.aprToApy)(fee, 365),
rewardsApr: rewards,
rewardsApy: rewards.map((x) => (0, utils_1.aprToApy)(x, 365)),
...priceRange,
};
};
async getGenericPoolInfo(poolPubkey, pools) {
const poolState = await raydium_client_1.PoolState.fetch(this._connection, poolPubkey);
if (!poolState) {
throw Error(`Raydium pool state ${poolPubkey} does not exist`);
}
if (!pools) {
({ data: pools } = await this.getRaydiumWhirlpools());
}
if (!pools || pools.length === 0) {
throw Error(`Could not get Raydium amm pools from Raydium API`);
}
const raydiumPool = pools.filter((d) => d.id === poolPubkey.toString()).shift();
if (!raydiumPool) {
throw Error(`Could not get find Raydium amm pool ${poolPubkey.toString()} from Raydium API`);
}
let poolInfo = {
dex: 'RAYDIUM',
address: new web3_js_1.PublicKey(poolPubkey),
tokenMintA: poolState.tokenMint0,
tokenMintB: poolState.tokenMint1,
price: new decimal_js_1.default(raydiumPool.price),
feeRate: new decimal_js_1.default(raydiumPool.ammConfig.tradeFeeRate).div(new decimal_js_1.default(CreationParameters_1.FullPercentage)),
volumeOnLast7d: new decimal_js_1.default(raydiumPool.week.volume),
tvl: new decimal_js_1.default(raydiumPool.tvl),
tickSpacing: new decimal_js_1.default(raydiumPool.ammConfig.tickSpacing),
// todo(Silviu): get real amount of positions
positions: new decimal_js_1.default(0),
};
return poolInfo;
}
async getPositionsCountByPool(pool) {
const positions = await this._connection.getProgramAccounts(programId_1.PROGRAM_ID, {
commitment: 'confirmed',
filters: [
{ dataSize: raydium_sdk_1.PositionInfoLayout.span },
{ memcmp: { bytes: pool.toBase58(), offset: raydium_sdk_1.PositionInfoLayout.offsetOf('poolId') } },
],
});
return positions.length;
}
}
exports.RaydiumService = RaydiumService;
//# sourceMappingURL=RaydiumService.js.map