Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Osmosis - Continuously creating position on amm-LP strategy #318

Open
wants to merge 7 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions src/chains/osmosis/osmosis.controllers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@

import {
HttpException,
import { HttpException,
LOAD_WALLET_ERROR_CODE,
LOAD_WALLET_ERROR_MESSAGE,
PRICE_FAILED_ERROR_CODE,
Expand Down Expand Up @@ -673,29 +671,29 @@ export class OsmosisController {
if (req.txHash){
const transaction = await osmosis.getTransaction(req.txHash);
const currentBlock = await osmosis.getCurrentBlockNumber();

const decoder = new TextDecoder();
//@ts-ignore cosmojs models again
var pool_id = undefined;
var position_id = undefined;
//@ts-ignore cosmojs models again
if (transaction.txResponse.logs){
//@ts-ignore cosmojs models again
transaction.txResponse.logs.forEach((log) => {
if (transaction.txResponse.events){
//@ts-ignore cosmojs models again
const create_position_event = log.events.find(({ type }) => type === 'create_position');
const create_position_event = transaction.txResponse.events.find(({ type }) => type === 'create_position');
if (create_position_event){
//@ts-ignore cosmojs models again
const pool_id_attribute = create_position_event.attributes.find(({ key }) => key === 'pool_id');
if (pool_id_attribute){
pool_id = pool_id_attribute.value
}
//@ts-ignore cosmojs models again
const position_id_attribute = create_position_event.attributes.find(({ key }) => key === 'position_id');
if (position_id_attribute){
position_id = position_id_attribute.value
}
}
})
//@ts-ignore cosmojs models again
create_position_event.attributes.forEach(attribute => {
const key = decoder.decode(attribute.key);
const value = decoder.decode(attribute.value);

if (key === 'pool_id'){
pool_id = value;
}

if (key === 'position_id'){
position_id = value;
}
});
}
}
var tokenId = position_id;
if (position_id == undefined){
Expand Down
34 changes: 21 additions & 13 deletions src/chains/osmosis/osmosis.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// OSMO message composer classes don't quite match up with what the RPC/Go backend actually accepts.

import { CosmosWallet, CosmosAsset, CosmosTokenValue, CosmosBase } from '../../chains/cosmos/cosmos-base';
import { OsmosisController } from './osmosis.controllers';
import BigNumber from 'bignumber.js';
Expand Down Expand Up @@ -51,7 +50,7 @@ import { Pool as CLPool } from 'osmo-query/dist/codegen/osmosis/concentrated-liq
import { TradeInfo } from './osmosis.controllers';
import { HttpException, TRADE_FAILED_ERROR_CODE, TRADE_FAILED_ERROR_MESSAGE, GAS_LIMIT_EXCEEDED_ERROR_MESSAGE, GAS_LIMIT_EXCEEDED_ERROR_CODE, AMOUNT_LESS_THAN_MIN_AMOUNT_ERROR_MESSAGE, AMOUNT_LESS_THAN_MIN_AMOUNT_ERROR_CODE } from '../../services/error-handler';
import { extendPool, filterPoolsSwap, filterPoolsLP, filterPoolsSwapAndLP } from './osmosis.lp.utils';
import { fetchFees, findTickForPrice, tickToPrice } from './osmosis.utils';
import { fetchFees, findTickForPrice, tickToPrice, calculatePriceToTick } from './osmosis.utils';
import { getImperatorPriceHash } from './osmosis.prices';
import { GasPrice, calculateFee, setupIbcExtension, SigningStargateClient, AminoTypes } from '@cosmjs/stargate';
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";
Expand Down Expand Up @@ -1361,8 +1360,12 @@ export class Osmosis extends CosmosBase implements Cosmosish{
tokenMinAmount1 = token1_bignumber.shiftedBy(this.getExponentByBase(token1.base)).multipliedBy(100-slippage).dividedBy(100).integerValue(BigNumber.ROUND_CEIL)
}

const lowerTick = findTickForPrice(req.lowerPrice!, pool.exponentAtPriceOne, pool.tickSpacing, true) // pool.currentTick,
const upperTick = findTickForPrice(req.upperPrice!, pool.exponentAtPriceOne, pool.tickSpacing, false)
// FIXME: The calculation of lowerTick and upperTick is not correct for the case where price is less than 1
const lowerTick = calculatePriceToTick(req.lowerPrice!, pool.exponentAtPriceOne, pool.tickSpacing, true) // pool.currentTick,
const upperTick = calculatePriceToTick(req.upperPrice!, pool.exponentAtPriceOne, pool.tickSpacing, false)

console.log('lowerTick', lowerTick)
console.log('upperTick', upperTick)

var tokenMinAmount0_final = tokenMinAmount0.toString()
var tokenMinAmount1_final = tokenMinAmount1.toString()
Expand Down Expand Up @@ -2266,7 +2269,7 @@ export class Osmosis extends CosmosBase implements Cosmosish{
countTotal: false,
reverse: false,
},
poolId: final_poolId.toString()
poolId: BigInt(final_poolId)
})
clPositions = clPositionsContainer.positions
} catch (error) {
Expand All @@ -2290,7 +2293,6 @@ export class Osmosis extends CosmosBase implements Cosmosish{
owner: address,
});
const lockedCoins: Coin[] = lockedCoinsContainer.lockedCoins ? lockedCoinsContainer.lockedCoins : []

// RETURN TYPES:
// concentrated-liquidity/pool || cosmwasmpool/v1beta1/model/pool || gamm/pool-models/balancer/balancerPool || gamm/pool-models/stableswap/stableswap_pool
const poolsContainer = await this._provider.osmosis.poolmanager.v1beta1.allPools({});
Expand Down Expand Up @@ -2351,17 +2353,23 @@ export class Osmosis extends CosmosBase implements Cosmosish{
}
}
});
const clPositionPool = extendedPools.find((pl) => pl.id.toString() === clPosition.position.poolId.toString());
const exponentToken0 = this.getExponentByBase(clPosition.asset0.denom)
const exponentToken1 = this.getExponentByBase(clPosition.asset1.denom)
// @ts-ignore
const lowerPrice = tickToPrice(exponentToken0, exponentToken1, clPosition.position.lowerTick.toString(), clPositionPool.exponentAtPriceOne.toString())
// @ts-ignore
const upperPrice = tickToPrice(exponentToken0, exponentToken1, clPosition.position.upperTick.toString(), clPositionPool.exponentAtPriceOne.toString())

returnObj.token0 = clPosition.asset0.denom;
returnObj.token1 = clPosition.asset1.denom;
returnObj.amount0 = clPosition.asset0.amount;
returnObj.amount1 = clPosition.asset1.amount;
returnObj.lowerPrice = clPosition.position.lowerTick.toString();
returnObj.upperPrice = clPosition.position.upperTick.toString();
returnObj.amount0 = (parseFloat(clPosition.asset0.amount) / Math.pow(10, exponentToken0)).toString();
returnObj.amount1 = (parseFloat(clPosition.asset1.amount) / Math.pow(10, exponentToken1)).toString();
returnObj.lowerPrice = lowerPrice.toString();
returnObj.upperPrice = upperPrice.toString();
returnObj.poolShares = clPosition.position.liquidity
returnObj.unclaimedToken0 = unclaimedToken0.toString()
returnObj.unclaimedToken1 = unclaimedToken1.toString()
const clPositionPool = extendedPools.find((pl) => pl.id.toString() === clPosition.position.poolId.toString());
returnObj.unclaimedToken0 = (parseFloat(unclaimedToken0.toString()) / Math.pow(10, exponentToken0)).toString();
returnObj.unclaimedToken1 = (parseFloat(unclaimedToken1.toString()) / Math.pow(10, exponentToken1)).toString();
returnObj.fee = clPositionPool?.fees7D.toString()
}
// not returning GAMM positons here; problematic for strat and apparently this is amm-liquidity route only
Expand Down
102 changes: 102 additions & 0 deletions src/chains/osmosis/osmosis.utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// Disable eslint for this file because it's a copy of the original file
/* eslint-disable */
import BigNumber from 'bignumber.js';
import Decimal from 'decimal.js-light';

export interface Fee {
pool_id: string;
Expand Down Expand Up @@ -93,3 +96,102 @@
return returnTick.toString()
}


export function calculatePriceToTick(desiredPriceString: string, exponentAtPriceOne: number, tickSpacing: number, is_lowerBound: boolean): string {
console.log(`Inputs: desiredPriceString=${desiredPriceString}, exponentAtPriceOne=${exponentAtPriceOne}, tickSpacing=${tickSpacing}, is_lowerBound=${is_lowerBound}`);

const desiredPrice = new BigNumber(desiredPriceString)
const exponent = new BigNumber(exponentAtPriceOne);
const geometricExponentIncrementDistanceInTicks = new BigNumber(9).multipliedBy(new BigNumber(10).exponentiatedBy(exponent.multipliedBy(-1)))

console.log(`Initial calculations: desiredPrice=${desiredPrice}, exponent=${exponent}, geometricExponentIncrementDistanceInTicks=${geometricExponentIncrementDistanceInTicks}`);

let currentPrice = new BigNumber(1);
let ticksPassed = new BigNumber(0);
let exponentAtCurrentTick = exponent;
let currentAdditiveIncrementInTicks = new BigNumber(10).exponentiatedBy(exponent)

if (desiredPrice.gt(new BigNumber(1))) {
while (currentPrice.lt(desiredPrice)) {
currentAdditiveIncrementInTicks = new BigNumber(10).exponentiatedBy(exponentAtCurrentTick);
const maxPriceForCurrentAdditiveIncrementInTicks = geometricExponentIncrementDistanceInTicks.multipliedBy(currentAdditiveIncrementInTicks);
currentPrice = currentPrice.plus(maxPriceForCurrentAdditiveIncrementInTicks);
exponentAtCurrentTick = exponentAtCurrentTick.plus(1);
ticksPassed = ticksPassed.plus(geometricExponentIncrementDistanceInTicks);

console.log(`Loop (desiredPrice > 1): currentPrice=${currentPrice}, exponentAtCurrentTick=${exponentAtCurrentTick}, ticksPassed=${ticksPassed}`);
}
} else {
exponentAtCurrentTick = exponent.minus(1);
while (currentPrice.gt(desiredPrice)) {
currentAdditiveIncrementInTicks = new BigNumber(10).exponentiatedBy(exponentAtCurrentTick);
const maxPriceForCurrentAdditiveIncrementInTicks = geometricExponentIncrementDistanceInTicks.multipliedBy(currentAdditiveIncrementInTicks);
currentPrice = currentPrice.minus(maxPriceForCurrentAdditiveIncrementInTicks);
exponentAtCurrentTick = exponentAtCurrentTick.minus(1);
ticksPassed = ticksPassed.minus(geometricExponentIncrementDistanceInTicks);

console.log(`Loop (desiredPrice <= 1): currentPrice=${currentPrice}, exponentAtCurrentTick=${exponentAtCurrentTick}, ticksPassed=${ticksPassed}`);
}
}

const ticksToBeFulfilledByExponentAtCurrentTick = desiredPrice.minus(currentPrice).dividedBy(currentAdditiveIncrementInTicks);
console.log(`Ticks to be fulfilled by current exponent: ${ticksToBeFulfilledByExponentAtCurrentTick}`);

const tickIndex = ticksPassed.plus(ticksToBeFulfilledByExponentAtCurrentTick);
console.log(`Tick index: ${tickIndex}`);

let returnTick = new BigNumber(0);
if (is_lowerBound){
returnTick = tickIndex.dividedBy(tickSpacing).integerValue(BigNumber.ROUND_DOWN).multipliedBy(tickSpacing)
}
else{
returnTick = tickIndex.dividedBy(tickSpacing).integerValue(BigNumber.ROUND_CEIL).multipliedBy(tickSpacing)
}

console.log(`Final calculations: tickIndex=${tickIndex}, returnTick=${BigInt(returnTick.toNumber()).toString()}`);
return BigInt(returnTick.toNumber()).toString();
}

function calculatePriceToTickDec(price: Decimal): [number, Error | null] {

Check failure on line 155 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

'calculatePriceToTickDec' is declared but its value is never read.
if (price.isNegative()) {
return [0, new Error("price must be greater than zero")];
}

if (price.equals(1)) {
return [0, null];
}

// N.B. this exists to maintain backwards compatibility with
// the old version of the function that operated on decimal with precision of 18.
if (price.gte(types.MinSpotPriceBigDec)) {

Check failure on line 166 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

Cannot find name 'types'.
price.tru(osmomath.DecPrecision);

Check failure on line 167 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

Property 'tru' does not exist on type 'Decimal'.

Check failure on line 167 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

Cannot find name 'osmomath'.
}

// The approach here is to try determine which "geometric spacing" we are in.
let geoSpacing: TickExpIndexData;

Check failure on line 171 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

Cannot find name 'TickExpIndexData'.
let index: number;

if (price.gt(osmomath.BigOneDec)) {

Check failure on line 174 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

Cannot find name 'osmomath'.
index = 0;
geoSpacing = tickExpCache[index];

Check failure on line 176 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

Cannot find name 'tickExpCache'.
while (geoSpacing.maxPrice.lt(price)) {
index += 1;
geoSpacing = tickExpCache[index];

Check failure on line 179 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

Cannot find name 'tickExpCache'.
}
} else {
index = -1;
geoSpacing = tickExpCache[index];

Check failure on line 183 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

Cannot find name 'tickExpCache'.
while (geoSpacing.initialPrice.gt(price)) {
index -= 1;
geoSpacing = tickExpCache[index];

Check failure on line 186 in src/chains/osmosis/osmosis.utils.ts

View workflow job for this annotation

GitHub Actions / Gateway build + unit tests

Cannot find name 'tickExpCache'.
}
}

// Calculate the number of ticks that need to be filled by our current spacing
const priceInThisExponent = price.sub(geoSpacing.initialPrice);
const ticksFilledByCurrentSpacing = priceInThisExponent.div(geoSpacing.additiveIncrementPerTick).truncate();

// Calculate the final tick index
const tickIndex = ticksFilledByCurrentSpacing + geoSpacing.initialTick;
return [tickIndex, null];
}
Loading