@marinade.finance/kamino-sdk
Version:
379 lines (341 loc) • 12.1 kB
text/typescript
import Decimal from 'decimal.js';
import { PositionRange, RebalanceFieldInfo, RebalanceFieldsDict } from '../utils/types';
import {
DefaultLowerPercentageBPSDecimal,
DefaultUpperPercentageBPSDecimal,
FullBPS,
FullBPSDecimal,
} from '../utils/CreationParameters';
import { getManualRebalanceFieldInfos } from './manualRebalance';
import { RebalanceRaw } from '../kamino-client/types';
import { RebalanceTypeLabelName } from './consts';
import { Dex, readBigUint128LE } from '../utils';
import { sqrtPriceX64ToPrice } from '@orca-so/whirlpool-sdk';
import BN from 'bn.js';
import { upsertManyRebalanceFieldInfos } from './utils';
import { getPriceRangeFromPriceAndDiffBPS, getResetRangeFromPriceAndDiffBPS } from './math_utils';
export const DefaultMaxNumberOfExpansions = new Decimal(10);
export const DefaultExpansionSizeBPS = new Decimal(100);
export const DefaultSwapUnevenAllowed = new Decimal(1);
export const ExpanderRebalanceTypeName = 'expander';
export function getExpanderRebalanceFieldInfos(
price: Decimal,
lowerPercentageBPS: Decimal,
upperPercentageBPS: Decimal,
resetLowerPercentageBPS: Decimal,
resetUpperPercentageBPS: Decimal,
expansionBPS: Decimal,
maxNumberOfExpansions: Decimal,
swapUnevenAllowed: Decimal,
enabled: boolean = true
): RebalanceFieldInfo[] {
let rebalanceType: RebalanceFieldInfo = {
label: RebalanceTypeLabelName,
type: 'string',
value: ExpanderRebalanceTypeName,
enabled,
};
let lowerBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'lowerRangeBps',
type: 'number',
value: lowerPercentageBPS,
enabled,
};
let upperBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'upperRangeBps',
type: 'number',
value: upperPercentageBPS,
enabled,
};
let resetLowerBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'resetLowerRangeBps',
type: 'number',
value: resetLowerPercentageBPS,
enabled,
};
let resetUpperBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'resetUpperRangeBps',
type: 'number',
value: resetUpperPercentageBPS,
enabled,
};
let expansionBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'expansionBps',
type: 'number',
value: expansionBPS,
enabled,
};
let maxNumberOfExpansionsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'maxNumberOfExpansions',
type: 'number',
value: maxNumberOfExpansions,
enabled,
};
let swapUnevenAllowedFieldInfo: RebalanceFieldInfo = {
label: 'swapUnevenAllowed',
type: 'number',
value: swapUnevenAllowed,
enabled,
};
let { lowerPrice, upperPrice } = getPositionRangeFromExpanderParams(price, lowerPercentageBPS, upperPercentageBPS);
let lowerRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceLower',
type: 'number',
value: lowerPrice,
enabled: false,
};
let upperRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceUpper',
type: 'number',
value: upperPrice,
enabled: false,
};
let { lowerPrice: resetLowerPrice, upperPrice: resetUpperPrice } = getPositionResetRangeFromExpanderParams(
price,
lowerPercentageBPS,
upperPercentageBPS,
resetLowerPercentageBPS,
resetUpperPercentageBPS
);
let resetLowerRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'resetPriceLower',
type: 'number',
value: resetLowerPrice,
enabled: false,
};
let resetUpperRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'resetPriceUpper',
type: 'number',
value: resetUpperPrice,
enabled: false,
};
return [
rebalanceType,
lowerBpsRebalanceFieldInfo,
upperBpsRebalanceFieldInfo,
resetLowerBpsRebalanceFieldInfo,
resetUpperBpsRebalanceFieldInfo,
expansionBpsRebalanceFieldInfo,
maxNumberOfExpansionsRebalanceFieldInfo,
swapUnevenAllowedFieldInfo,
lowerRangeRebalanceFieldInfo,
upperRangeRebalanceFieldInfo,
resetLowerRangeRebalanceFieldInfo,
resetUpperRangeRebalanceFieldInfo,
];
}
export function getPositionRangeFromExpanderParams(
price: Decimal,
lowerPriceDifferenceBPS: Decimal,
upperPriceDifferenceBPS: Decimal
): PositionRange {
return getPriceRangeFromPriceAndDiffBPS(price, lowerPriceDifferenceBPS, upperPriceDifferenceBPS);
}
export function getPositionResetRangeFromExpanderParams(
price: Decimal,
lowerPriceDifferenceBPS: Decimal,
upperPriceDifferenceBPS: Decimal,
resetLowerPriceDifferenceBPS: Decimal,
resetUpperPriceDifferenceBPS: Decimal
): PositionRange {
return getResetRangeFromPriceAndDiffBPS(
price,
lowerPriceDifferenceBPS,
upperPriceDifferenceBPS,
resetLowerPriceDifferenceBPS,
resetUpperPriceDifferenceBPS
);
}
export function getDefaultExpanderRebalanceFieldInfos(price: Decimal): RebalanceFieldInfo[] {
return getExpanderRebalanceFieldInfos(
price,
DefaultLowerPercentageBPSDecimal,
DefaultUpperPercentageBPSDecimal,
DefaultLowerPercentageBPSDecimal,
DefaultUpperPercentageBPSDecimal,
DefaultLowerPercentageBPSDecimal,
DefaultMaxNumberOfExpansions,
DefaultSwapUnevenAllowed
);
}
export function readRawExpanderRebalanceParamsFromStrategy(rebalanceRaw: RebalanceRaw) {
let paramsBuffer = Buffer.from(rebalanceRaw.params);
let params: RebalanceFieldsDict = {};
params['lowerRangeBps'] = new Decimal(paramsBuffer.readUInt16LE(0));
params['upperRangeBps'] = new Decimal(paramsBuffer.readUInt16LE(2));
params['lowerResetRatioBps'] = new Decimal(paramsBuffer.readUInt16LE(4));
params['upperResetRatioBps'] = new Decimal(paramsBuffer.readUInt16LE(6));
params['expansionBps'] = new Decimal(paramsBuffer.readUInt16LE(8));
params['maxNumberOfExpansions'] = new Decimal(paramsBuffer.readUInt16LE(10));
params['swapUnevenAllowed'] = new Decimal(paramsBuffer.readUInt8(12));
return params;
}
export function readExpanderRebalanceParamsFromStrategy(rebalanceRaw: RebalanceRaw): RebalanceFieldInfo[] {
let paramsBuffer = Buffer.from(rebalanceRaw.params);
let lowerRangeBps = new Decimal(paramsBuffer.readUInt16LE(0));
let upperRangeBps = new Decimal(paramsBuffer.readUInt16LE(2));
let lowerResetRatioBps = new Decimal(paramsBuffer.readUInt16LE(4));
let upperResetRatioBps = new Decimal(paramsBuffer.readUInt16LE(6));
let expansionBps = new Decimal(paramsBuffer.readUInt16LE(8));
let maxNumberOfExpansions = new Decimal(paramsBuffer.readUInt16LE(10));
let swapUnevenAllowed = new Decimal(paramsBuffer.readUInt8(12));
let lowerBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'lowerRangeBps',
type: 'number',
value: lowerRangeBps,
enabled: true,
};
let upperBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'upperRangeBps',
type: 'number',
value: upperRangeBps,
enabled: true,
};
let resetLowerBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'resetLowerRangeBps',
type: 'number',
value: lowerResetRatioBps,
enabled: true,
};
let resetUpperBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'resetUpperRangeBps',
type: 'number',
value: upperResetRatioBps,
enabled: true,
};
let expansionBpsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'expansionBps',
type: 'number',
value: expansionBps,
enabled: true,
};
let maxNumberOfExpansionsRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'maxNumberOfExpansions',
type: 'number',
value: maxNumberOfExpansions,
enabled: true,
};
let swapUnevenAllowedFieldInfo: RebalanceFieldInfo = {
label: 'swapUnevenAllowed',
type: 'number',
value: swapUnevenAllowed,
enabled: true,
};
return [
lowerBpsRebalanceFieldInfo,
upperBpsRebalanceFieldInfo,
resetLowerBpsRebalanceFieldInfo,
resetUpperBpsRebalanceFieldInfo,
expansionBpsRebalanceFieldInfo,
maxNumberOfExpansionsRebalanceFieldInfo,
swapUnevenAllowedFieldInfo,
];
}
export function readRawExpanderRebalanceStateFromStrategy(rebalanceRaw: RebalanceRaw) {
let stateBuffer = Buffer.from(rebalanceRaw.state);
let state: RebalanceFieldsDict = {};
state['initialPoolPrice'] = new Decimal(readBigUint128LE(stateBuffer, 0).toString());
state['expansionCount'] = new Decimal(stateBuffer.readUInt16LE(16));
return state;
}
export function readExpanderRebalanceStateFromStrategy(
dex: Dex,
tokenADecimals: number,
tokenBDecimals: number,
rebalanceRaw: RebalanceRaw
): RebalanceFieldInfo[] {
let stateBuffer = Buffer.from(rebalanceRaw.state);
let params = readRawExpanderRebalanceParamsFromStrategy(rebalanceRaw);
let lowerRangeBps = params['lowerRangeBps'];
let upperRangeBps = params['upperRangeBps'];
let lowerResetRatioBps = params['lowerResetRatioBps'];
let upperResetRatioBps = params['upperResetRatioBps'];
let expansionBps = params['expansionBps'];
let state = readRawExpanderRebalanceStateFromStrategy(rebalanceRaw);
let initialPriceX64 = state['initialPoolPrice'];
let expansionCount = state['expansionCount'];
let initialPrice: Decimal;
if (dex == 'ORCA') {
initialPrice = sqrtPriceX64ToPrice(new BN(initialPriceX64.toString()), tokenADecimals, tokenBDecimals);
} else if (dex == 'RAYDIUM') {
initialPrice = sqrtPriceX64ToPrice(new BN(initialPriceX64.toString()), tokenADecimals, tokenBDecimals);
} else {
throw new Error(`Unknown DEX ${dex}`);
}
let lowerRangeFactorBPS = lowerRangeBps.add(expansionBps.mul(expansionCount));
let upperRangeFactorBPS = upperRangeBps.add(expansionBps.mul(expansionCount));
let { lowerPrice, upperPrice } = getPriceRangeFromPriceAndDiffBPS(
initialPrice,
lowerRangeFactorBPS,
upperRangeFactorBPS
);
let { lowerPrice: lowerResetPrice, upperPrice: upperResetPrice } = getResetRangeFromPriceAndDiffBPS(
initialPrice,
lowerRangeFactorBPS,
upperRangeFactorBPS,
lowerResetRatioBps,
upperResetRatioBps
);
let lowerRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceLower',
type: 'number',
value: lowerPrice,
enabled: false,
};
let upperRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'rangePriceUpper',
type: 'number',
value: upperPrice,
enabled: false,
};
let resetLowerRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'resetPriceLower',
type: 'number',
value: lowerResetPrice,
enabled: false,
};
let resetUpperRangeRebalanceFieldInfo: RebalanceFieldInfo = {
label: 'resetPriceUpper',
type: 'number',
value: upperResetPrice,
enabled: false,
};
return [
lowerRangeRebalanceFieldInfo,
upperRangeRebalanceFieldInfo,
resetLowerRangeRebalanceFieldInfo,
resetUpperRangeRebalanceFieldInfo,
];
}
export function readExpanderRebalanceFieldInfosFromStrategy(price: Decimal, rebalanceRaw: RebalanceRaw) {
const params = readExpanderRebalanceParamsFromStrategy(rebalanceRaw);
let paramsBuffer = Buffer.from(rebalanceRaw.params);
let lowerRangeBps = new Decimal(paramsBuffer.readUInt16LE(0));
let upperRangeBps = new Decimal(paramsBuffer.readUInt16LE(2));
let lowerResetRatioBps = new Decimal(paramsBuffer.readUInt16LE(4));
let upperResetRatioBps = new Decimal(paramsBuffer.readUInt16LE(6));
let expansionBps = new Decimal(paramsBuffer.readUInt16LE(8));
let maxNumberOfExpansions = new Decimal(paramsBuffer.readUInt16LE(10));
let swapUnevenAllowed = new Decimal(paramsBuffer.readUInt8(12));
return getExpanderRebalanceFieldInfos(
price,
lowerRangeBps,
upperRangeBps,
lowerResetRatioBps,
upperResetRatioBps,
expansionBps,
maxNumberOfExpansions,
swapUnevenAllowed
);
}
export function deserializeExpanderRebalanceWithStateOverride(
dex: Dex,
tokenADecimals: number,
tokenBDecimals: number,
price: Decimal,
rebalanceRaw: RebalanceRaw
): RebalanceFieldInfo[] {
const stateFields = readExpanderRebalanceStateFromStrategy(dex, tokenADecimals, tokenBDecimals, rebalanceRaw);
let fields = readExpanderRebalanceFieldInfosFromStrategy(price, rebalanceRaw);
return upsertManyRebalanceFieldInfos(fields, stateFields);
}