@alcorexchange/alcor-swap-sdk
Version:
**npm** ``` npm i @alcorexchange/alcor-swap-sdk ``` **yarn** ``` yarn add @alcorexchange/alcor-swap-sdk ``` ## Usage ### Import:
189 lines (182 loc) • 7.93 kB
JavaScript
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
import invariant from "tiny-invariant";
import { ZERO } from "../internalConstants";
import { isSorted } from "./isSorted";
function tickComparator(a, b) {
return a.id - b.id;
}
/**x
* Utility methods for interacting with sorted lists of ticks
*/
export let TickList = /*#__PURE__*/function () {
/**
* Cannot be constructed
*/
function TickList() {
_classCallCheck(this, TickList);
}
return _createClass(TickList, null, [{
key: "validateList",
value: function validateList(ticks, tickSpacing) {
invariant(tickSpacing > 0, "TICK_SPACING_NONZERO");
// ensure ticks are spaced appropriately
invariant(ticks.every(({
id
}) => id % tickSpacing === 0), "TICK_SPACING");
const totalNet = ticks.reduce((accumulator, {
liquidityNet
}) => accumulator + liquidityNet, ZERO);
if (!(totalNet === ZERO)) console.error('ZERO_NET INVARIAN ISSUE!');
// HOTFIX ignoring for now TODO
// ensure tick liquidity deltas sum to 0
// invariant(
// (// ticks.reduce(
// (accumulator, { liquidityNet }) =>
// (accumulator + liquidityNet),
// ZERO
// ) === // ZERO
//),
// "ZERO_NET"
// );
invariant(isSorted(ticks, tickComparator), "SORTED");
}
}, {
key: "isBelowSmallest",
value: function isBelowSmallest(ticks, tick) {
invariant(ticks.length > 0, "LENGTH");
return tick < ticks[0].id;
}
}, {
key: "isAtOrAboveLargest",
value: function isAtOrAboveLargest(ticks, tick) {
invariant(ticks.length > 0, "LENGTH");
return tick >= ticks[ticks.length - 1].id;
}
}, {
key: "getTick",
value: function getTick(ticks, id) {
const tick = ticks[this.binarySearch(ticks, id)];
invariant(tick.id === id, "NOT_CONTAINED");
return tick;
}
/**
* Finds the largest tick in the list of ticks that is less than or equal to tick
* @param ticks list of ticks
* @param tick tick to find the largest tick that is less than or equal to tick
* @private
*/
}, {
key: "binarySearch",
value: function binarySearch(ticks, tick) {
let l = 0;
let r = ticks.length - 1;
while (l <= r) {
const i = Math.floor((l + r) / 2);
if (ticks[i].id <= tick && (i === ticks.length - 1 || ticks[i + 1].id > tick)) {
return i;
}
if (ticks[i].id < tick) l = i + 1;else r = i - 1;
}
return r; // Если не нашли точное совпадение, возвращаем последний меньший индекс
}
}, {
key: "nextInitializedTick",
value: function nextInitializedTick(ticks, tick, lte) {
if (lte) {
invariant(!TickList.isBelowSmallest(ticks, tick), "BELOW_SMALLEST");
if (TickList.isAtOrAboveLargest(ticks, tick)) {
return ticks[ticks.length - 1];
}
const id = this.binarySearch(ticks, tick);
return ticks[id];
} else {
invariant(!this.isAtOrAboveLargest(ticks, tick), "AT_OR_ABOVE_LARGEST");
if (this.isBelowSmallest(ticks, tick)) {
return ticks[0];
}
const id = this.binarySearch(ticks, tick);
return ticks[id + 1];
}
}
}, {
key: "nextInitializedTickWithinOneWord",
value: function nextInitializedTickWithinOneWord(ticks, tick, lte, tickSpacing) {
const compressed = Math.floor(tick / tickSpacing);
const tickCount = ticks.length;
if (lte) {
const wordPos = compressed >> 7;
const minimum = (wordPos << 7) * tickSpacing;
if (tick < ticks[0].id) return [minimum, false];
if (tick >= ticks[tickCount - 1].id) return [ticks[tickCount - 1].id, true];
const id = this.binarySearch(ticks, tick);
const nextInitializedTick = Math.max(minimum, ticks[id].id);
return [nextInitializedTick, nextInitializedTick === ticks[id].id];
} else {
const wordPos = compressed + 1 >> 7;
const maximum = ((wordPos + 1 << 7) - 1) * tickSpacing;
if (tick >= ticks[tickCount - 1].id) return [maximum, false];
if (tick < ticks[0].id) return [ticks[0].id, true];
const id = this.binarySearch(ticks, tick);
const nextInitializedTick = Math.min(maximum, ticks[id + 1].id);
return [nextInitializedTick, nextInitializedTick === ticks[id + 1].id];
}
}
/**
* Optimized version with cursor hint for sequential access (swap loops)
* @param cursorHint - last known index position, -1 if unknown
* @returns [tickId, initialized, newCursorIndex]
*/
}, {
key: "nextInitializedTickWithinOneWordWithCursor",
value: function nextInitializedTickWithinOneWordWithCursor(ticks, tick, lte, tickSpacing, cursorHint) {
const compressed = Math.floor(tick / tickSpacing);
const tickCount = ticks.length;
if (lte) {
const wordPos = compressed >> 7;
const minimum = (wordPos << 7) * tickSpacing;
if (tick < ticks[0].id) return [minimum, false, -1];
if (tick >= ticks[tickCount - 1].id) return [ticks[tickCount - 1].id, true, tickCount - 1];
// Use cursor hint for O(1) lookup if valid
let id;
if (cursorHint >= 0 && cursorHint < tickCount) {
// Linear scan from hint (swap moves monotonically)
if (ticks[cursorHint].id <= tick && (cursorHint === tickCount - 1 || ticks[cursorHint + 1].id > tick)) {
id = cursorHint;
} else if (cursorHint > 0 && ticks[cursorHint - 1].id <= tick && ticks[cursorHint].id > tick) {
id = cursorHint - 1;
} else {
id = this.binarySearch(ticks, tick);
}
} else {
id = this.binarySearch(ticks, tick);
}
const nextInitializedTick = Math.max(minimum, ticks[id].id);
return [nextInitializedTick, nextInitializedTick === ticks[id].id, id];
} else {
const wordPos = compressed + 1 >> 7;
const maximum = ((wordPos + 1 << 7) - 1) * tickSpacing;
if (tick >= ticks[tickCount - 1].id) return [maximum, false, tickCount - 1];
if (tick < ticks[0].id) return [ticks[0].id, true, 0];
// Use cursor hint for O(1) lookup if valid
let id;
if (cursorHint >= 0 && cursorHint < tickCount - 1) {
if (ticks[cursorHint].id <= tick && ticks[cursorHint + 1].id > tick) {
id = cursorHint;
} else if (cursorHint < tickCount - 2 && ticks[cursorHint + 1].id <= tick && ticks[cursorHint + 2].id > tick) {
id = cursorHint + 1;
} else {
id = this.binarySearch(ticks, tick);
}
} else {
id = this.binarySearch(ticks, tick);
}
const nextInitializedTick = Math.min(maximum, ticks[id + 1].id);
return [nextInitializedTick, nextInitializedTick === ticks[id + 1].id, id];
}
}
}]);
}();