diff --git a/src/chains/osmosis/osmosis.controllers.ts b/src/chains/osmosis/osmosis.controllers.ts index ab90642ae1..b887541279 100755 --- a/src/chains/osmosis/osmosis.controllers.ts +++ b/src/chains/osmosis/osmosis.controllers.ts @@ -1,6 +1,4 @@ - -import { - HttpException, +import { HttpException, LOAD_WALLET_ERROR_CODE, LOAD_WALLET_ERROR_MESSAGE, PRICE_FAILED_ERROR_CODE, @@ -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){ diff --git a/src/chains/osmosis/osmosis.ts b/src/chains/osmosis/osmosis.ts index c1a23d0f90..cbf8b11a6b 100755 --- a/src/chains/osmosis/osmosis.ts +++ b/src/chains/osmosis/osmosis.ts @@ -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'; @@ -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"; @@ -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() @@ -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) { @@ -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({}); @@ -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 diff --git a/src/chains/osmosis/osmosis.utils.ts b/src/chains/osmosis/osmosis.utils.ts index b54d387d0a..6de370c7fd 100755 --- a/src/chains/osmosis/osmosis.utils.ts +++ b/src/chains/osmosis/osmosis.utils.ts @@ -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; @@ -93,3 +96,102 @@ export function findTickForPrice(desiredPriceString: string, exponentAtPriceOne: 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] { + 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)) { + price.tru(osmomath.DecPrecision); + } + + // The approach here is to try determine which "geometric spacing" we are in. + let geoSpacing: TickExpIndexData; + let index: number; + + if (price.gt(osmomath.BigOneDec)) { + index = 0; + geoSpacing = tickExpCache[index]; + while (geoSpacing.maxPrice.lt(price)) { + index += 1; + geoSpacing = tickExpCache[index]; + } + } else { + index = -1; + geoSpacing = tickExpCache[index]; + while (geoSpacing.initialPrice.gt(price)) { + index -= 1; + geoSpacing = tickExpCache[index]; + } + } + + // 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]; +} \ No newline at end of file