@renec-foundation/redex-sdk
Version:
Typescript SDK to interact with Orca's Whirlpool program.
121 lines (120 loc) • 5.93 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TickArraySequence = void 0;
const errors_1 = require("../../errors/errors");
const public_1 = require("../../types/public");
const tick_array_index_1 = require("./tick-array-index");
/**
* NOTE: differs from contract method of having the swap manager keep track of array index.
* This is due to the initial requirement to lazy load tick-arrays. This requirement is no longer necessary.
*/
class TickArraySequence {
constructor(tickArrays, tickSpacing, aToB) {
this.tickSpacing = tickSpacing;
this.aToB = aToB;
if (!tickArrays[0] || !tickArrays[0].data) {
throw new Error("TickArray index 0 must be initialized");
}
// If an uninitialized TickArray appears, truncate all TickArrays after it (inclusive).
this.sequence = [];
for (const tickArray of tickArrays) {
if (!tickArray || !tickArray.data) {
break;
}
this.sequence.push({
address: tickArray.address,
data: tickArray.data,
});
}
this.touchedArrays = [...Array(this.sequence.length).fill(false)];
this.startArrayIndex = tick_array_index_1.TickArrayIndex.fromTickIndex(this.sequence[0].data.startTickIndex, this.tickSpacing).arrayIndex;
}
isValidTickArray0(tickCurrentIndex) {
const shift = this.aToB ? 0 : this.tickSpacing;
const tickArray = this.sequence[0].data;
return this.checkIfIndexIsInTickArrayRange(tickArray.startTickIndex, tickCurrentIndex + shift);
}
getNumOfTouchedArrays() {
return this.touchedArrays.filter((val) => !!val).length;
}
getTouchedArrays(minArraySize) {
let result = this.touchedArrays.reduce((prev, curr, index) => {
if (curr) {
prev.push(this.sequence[index].address);
}
return prev;
}, []);
// Edge case: nothing was ever touched.
if (result.length === 0) {
return [];
}
// The quote object should contain the specified amount of tick arrays to be plugged
// directly into the swap instruction.
// If the result does not fit minArraySize, pad the rest with the last touched array
const sizeDiff = minArraySize - result.length;
if (sizeDiff > 0) {
result = result.concat(Array(sizeDiff).fill(result[result.length - 1]));
}
return result;
}
getTick(index) {
const targetTaIndex = tick_array_index_1.TickArrayIndex.fromTickIndex(index, this.tickSpacing);
if (!this.isArrayIndexInBounds(targetTaIndex, this.aToB)) {
throw new Error("Provided tick index is out of bounds for this sequence.");
}
const localArrayIndex = this.getLocalArrayIndex(targetTaIndex.arrayIndex, this.aToB);
const tickArray = this.sequence[localArrayIndex].data;
this.touchedArrays[localArrayIndex] = true;
if (!tickArray) {
throw new errors_1.WhirlpoolsError(`TickArray at index ${localArrayIndex} is not initialized.`, errors_1.SwapErrorCode.TickArrayIndexNotInitialized);
}
if (!this.checkIfIndexIsInTickArrayRange(tickArray.startTickIndex, index)) {
throw new errors_1.WhirlpoolsError(`TickArray at index ${localArrayIndex} is unexpected for this sequence.`, errors_1.SwapErrorCode.TickArraySequenceInvalid);
}
return tickArray.ticks[targetTaIndex.offsetIndex];
}
/**
* if a->b, currIndex is included in the search
* if b->a, currIndex is always ignored
* @param currIndex
* @returns
*/
findNextInitializedTickIndex(currIndex) {
const searchIndex = this.aToB ? currIndex : currIndex + this.tickSpacing;
let currTaIndex = tick_array_index_1.TickArrayIndex.fromTickIndex(searchIndex, this.tickSpacing);
// Throw error if the search attempted to search for an index out of bounds
if (!this.isArrayIndexInBounds(currTaIndex, this.aToB)) {
throw new errors_1.WhirlpoolsError(`Swap input value traversed too many arrays. Out of bounds at attempt to traverse tick index - ${currTaIndex.toTickIndex()}.`, errors_1.SwapErrorCode.TickArraySequenceInvalid);
}
while (this.isArrayIndexInBounds(currTaIndex, this.aToB)) {
const currTickData = this.getTick(currTaIndex.toTickIndex());
if (currTickData.initialized) {
return { nextIndex: currTaIndex.toTickIndex(), nextTickData: currTickData };
}
currTaIndex = this.aToB
? currTaIndex.toPrevInitializableTickIndex()
: currTaIndex.toNextInitializableTickIndex();
}
const lastIndexInArray = Math.max(Math.min(this.aToB ? currTaIndex.toTickIndex() + this.tickSpacing : currTaIndex.toTickIndex() - 1, public_1.MAX_TICK_INDEX), public_1.MIN_TICK_INDEX);
return { nextIndex: lastIndexInArray, nextTickData: null };
}
getLocalArrayIndex(arrayIndex, aToB) {
return aToB ? this.startArrayIndex - arrayIndex : arrayIndex - this.startArrayIndex;
}
/**
* Check whether the array index potentially exists in this sequence.
* Note: assumes the sequence of tick-arrays are sequential
* @param index
*/
isArrayIndexInBounds(index, aToB) {
// a+0...a+n-1 array index is ok
const localArrayIndex = this.getLocalArrayIndex(index.arrayIndex, aToB);
const seqLength = this.sequence.length;
return localArrayIndex >= 0 && localArrayIndex < seqLength;
}
checkIfIndexIsInTickArrayRange(startTick, tickIndex) {
const upperBound = startTick + this.tickSpacing * public_1.TICK_ARRAY_SIZE;
return tickIndex >= startTick && tickIndex < upperBound;
}
}
exports.TickArraySequence = TickArraySequence;