Skip to content

Commit

Permalink
Limit Orders and Swap Flow Redesign (#3683)
Browse files Browse the repository at this point in the history
* feat: create generic disclaimer tooltip

* fix: enable propagation on generic disclaimer

* feat: re-arrange and update market trade details

* feat: add free state to trade details

* feat: re-arrange limit order fees

* feat: add gas icon

* feat: move and update review-swap into review-order

* feat: re-implement review-order modal

* feat: added gas fee esimate to place limit state

* feat: reduced fee estimate calls

* feat: add button disabling on review modal

* fix: removed repeating asset price query

* fix: adjusted placed quantity to be exact input

* fix: remove unused var

* refactor: remove old limit order review modal and extract recap row

* feat: add high slippage warning to review order

* feat: inject maxSlippage into usePlaceLimit hook

* chore: remove useless style from GenericDisclaimer

* feat: add gas icon to limit trade details network fees recap row

* feat: add tooltips to trade details row

* build: fixed translation json error

* redesign swap widget (#3591)

* fix: fixed isTyping field for inAmountInput

* Improve rate info (#3588)

* rate

* fix build lint

* improve buy/sell view

* Update packages/web/components/swap-tool/trade-details.tsx

Co-authored-by: Connor Barr <[email protected]>

* undo inlang change

---------

Co-authored-by: Connor Barr <[email protected]>

* fix: fix missing attributes

* feat: redesign widget base layout & top selectors

* chore: remove unused old commented code

* chore: remove tab uqs

* feat: update token selection & input/output designs

* feat: redesign quote selection

* feat: WIP redesign limit price selector and update page widget size

* fix: invert triangles direction and comment out unused code

* fix: fix button click disabled when tooltip open

* feat: re-implement open orders card

* feat: WIP init implementing focus switching

* feat: add switching button UI

* feat: change default percent adjustment and label render logic

* feat: re-add trade details

* chore: remove unused code

* refactor: extract AssetFieldset

* chore: comment out unused code

* feat: wire-up trade details limit variant

* feat: WIP implement new swap tool designs

* refactor: moved order mapping to caching function

* build: fixed build errors

* feat: added refetch to cancel/claim [WARNING DO NOT TEST]

* fix: fixed refetching on order table

* feat: finish implementing swap tool redesign

* feat: add popover on limit order disabled & tweak GenericDisclaimer customization

* feat: refetch orders on first load

* feat: add "add funds" button to asset fieldset header balance

* feat: add no balance logics to place limit tool & swap tool

* fix: inputs adjust value of other input for limit orders

* feat: change all site bg & fix earn page radio buttons opacity

* fix: removed unnecessary button disabled state

* fix: removed unnecessary button disabled state

* feat: add trade bg

* chore: removed unused code

* fix: fixed price input error on 0 input

* fix: fixed rounding issue with max button

* fix: fix faulting overflow-hidden on alt swap tool

* fix: fix bar width

* fix: fix alt swap tool labels

* fix: fix scroll on home fixed container

* fix: fix "add funds" display on wallet disconnected

* fix: fix alt headers and remove old code

* fix: fix spacing on tool

* fix: fix trade details bugged animation

* feat: add prefixes/suffixes empty states

* fix: disable ticker on sell tab balance

* fix: fix sell balance precision & fieldset footer height

* fix: fix buy available usdc amount formatting

* feat: update selectableQuotes to cover wallet not connected case

* fix: hide add funds button when wallet is disconnected

* fix: hide add funds cta on price selector and fix lint

* fix: filter in UI default quotes on defaultQuotesWithBalances

* feat: add empty states to swap widget footers

* feat: make "stablecoin" tooltip trigger underlined

* fix: update decimal places and add "<$0.01" to trade details fees

* build: fixed build errors

* fix: update display of gasUsdValueToPay

* feat: update rendering of gasAmount and wire it up on swap tool

* refactor: updated button disabled & loading states

* fix: update gas display on review modal

* chore: readded insufficient balance display to hidden error messages

* fix: update chart icon size and variant display logic

* feat: remove triangle indicator when percentAdjusted is zero

* fix: update token-details-shadow color

* chore: sentence case adjustments

* fix: fixed review modal changing price and incorrect values

* feat: WIP add scaling to asset input

* fix: fix rounding issues on order type selector and wrapping on parent container

* fix: move isValidNumericalRawInput validation to useLimitPrice

* refactor: move orderType prop computation to ReviewOrder and remove unused prop

* fix: fixed low price value display in limit price selector

* feat: WIP add diffGteSlippage handling

* fix: update isManualSlippageTooHigh treshold

* feat: truncate last slippage number if it's gt 50

* feat: add isManualSlippageTooLow disclaimer and update designs

* fix: fix max button spinner distortion

* fix: fix sticky scroll bg color to thead

* feat: add tooltip on order history page

* feat: update order history columns

* fix: fixed overflow on trade details and insufficient funds not disabling review button

* fix: added label for limit price input and fixed hover state for denom selection

* feat: button hover states for price selection

* fix: removed place limit msg generation for market orders

* build: fixed build error

* fix: adjusted text overflow for expected output

* feat: ✨ add limit orders localizations

* feat: added no gas fee estimation state to trade details

* feat: added gas estimate changes to review order

* chore: machine translate

* chore: added more localisations

* feat: added refetchOnMount to all/claimable orders queries

* fix: filtered unwanted quote assets

* feat: added amount formatting for quote denom selection

* refactor: refactored balance queries in placelimit hook

* fix: 💄 fix order history table overflow (#3649)

* fix: fixed modal not being attached to top of screen for denom selection

* fix: 💄 fix trade widget overlap with sidebar

* feat: clear denom selection on modal close

* fix: undid regression

* feat: ✨ add missing translation and fix analytics event page

* fix: 🐛 fix build error

* fix: removed limit price display in review modal on swap tab

* fix: fixed available amount formatting for buy/sell tab

* feat: moved error display

* fix: disabled button on no funds

* fix: limited decimal input to 3 decimal places

* fix: removed gas fee estimation from trade details

* feat: added state for no routes in trade details

* feat: focus input on swap for buy/sell

* feat: automatically display base denom in trade details

* fix: non-focused percentage 3 decimal places

* refactor: altered trade details outAsBase state and added more localisations

* [WIP] feat: price selection has default value

* fix: hid routes chevron when no routes found

* fix: fixed swap tool layout on pools page modal

* fix: background colors for pool page

* fix: background colour changes

* fix: review order terminology changes

* fix: trade-tokens terminology and padding changes

* fix: adjusted ttl for all orders cache

* refactor: changed cache times for orders

* refactor: altered cache times and added logs for active orders query

* feat: small improvement to all orders query

* chore: cleaned up logs

* fix: active orders optimisation

* fix: fixed bug with input form not resetting

* fix: refetch orders when order placed

* fix: caching trade tool active orders query to orders history page

* fix: readded claim all orders function

* refactor: refactored price input to be simpler

* fix: updating price while in percent mode for price selection

* feat: extreme value handling

* feat: extreme value handling

* fix: small bug fixes

* fix: price adjustment as spot price changes in price selection, removed unused log

* feat: spinner/disabled look for claim & close button

* fix: fixed above/below display for price selector

* fix: show order history link when user has any order history

* fix: fixed font scaling for inputs

* fix: reworked order history page

* chore: removed old order history page

* build: fixed build error

* fix: small changes to order history

* build: fix build errors

* feat: moved orderbook denom query to sqs pools

* fix: more active orders query changes

* feat: added no wallet state for order history

* fix: wallet loading state order history page

* feat: limits can now be placed on opposite side of market price

* chore: localisations

* fix: fixed error on high price amount inputs

* fix: small percentage adjustments for price input

* chore: removed logs, small formatting adjustment

* fix: toggle background colours for limit order ff

* fix: percent fill rounding

* feat: added geolocation based feature flags for limit orders

* fix: improved feel of cancel/claim buttons

* fix: correct swap tool depending on feature flags

* fix: missed backgrounds for feature flags

* build: fix build errors

* fix: remove swr and invalidate orderbook queries on tx

* refactor: moved historical orders query, fixed schema issue

* chore: removed unused code

* chore: reduced use of useMemo where possible

* chore: remove unused code

* test: moved tickToPrice to jest in case

* chore: added debug logs for claim/cancel buttons when no wallet found

* feat: added intersection component to orders history

* feat: valid/default limit quote denoms uses MainnetAssetSymbols

* fix: feature flag bg colour in main

* feat: added placeholder prop to ScaledCurrencyInput

* build: fixed build errors

* feat: undid order history first column changes from yesterday

* test: added unit tests for new formatter functions

* refactor: moved countDecimals test to correct file

* refactor: ♻️ add useKeyboardNavigation instead of custom implementation

* chore: added comments to disabled hooks in various places

* fix: 🐛 fix build error

* lint

* clean up unused localizations

* remove tests for old swap tool (deprecated)

---------

Co-authored-by: fabryscript <[email protected]>
Co-authored-by: Fabrizio Emanuele Piperno <[email protected]>
Co-authored-by: Sunny Aggarwal <[email protected]>
Co-authored-by: Davide Segullo <[email protected]>
Co-authored-by: Jon Ator <[email protected]>
  • Loading branch information
6 people authored Aug 7, 2024
1 parent be7fcba commit aab2946
Show file tree
Hide file tree
Showing 107 changed files with 10,723 additions and 791 deletions.
3 changes: 3 additions & 0 deletions packages/math/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,8 @@
"@keplr-wallet/types": "0.10.24-ibc.go.v7.hot.fix",
"@keplr-wallet/unit": "0.10.24-ibc.go.v7.hot.fix",
"big-integer": "^1.6.48"
},
"devDependencies": {
"jest-in-case": "^1.0.2"
}
}
39 changes: 38 additions & 1 deletion packages/math/src/pool/concentrated/__tests__/tick.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Dec, Int } from "@keplr-wallet/unit";
// eslint-disable-next-line import/no-extraneous-dependencies
import cases from "jest-in-case";

import { approxSqrt } from "../../../utils";
import { maxSpotPrice, maxTick, minSpotPrice } from "../const";
import { priceToTick, tickToSqrtPrice } from "../tick";
import { priceToTick, tickToPrice, tickToSqrtPrice } from "../tick";

// https://github.com/osmosis-labs/osmosis/blob/0f9eb3c1259078035445b3e3269659469b95fd9f/x/concentrated-liquidity/math/tick_test.go#L30
describe("tickToSqrtPrice", () => {
Expand Down Expand Up @@ -217,6 +219,41 @@ describe("priceToTick", () => {
});
});

cases(
"tickToPrice",
({ tick, priceExpected }) => {
const price = tickToPrice(tick);
expect(price.toString()).toEqual(priceExpected.toString());
},
[
{
name: "Tick Zero",
tick: new Int("0"),
priceExpected: new Dec("1"),
},
{
name: "Large Positive Tick",
tick: new Int("1000000"),
priceExpected: new Dec("2"),
},
{
name: "Large Negative Tick",
tick: new Int("-5000000"),
priceExpected: new Dec("0.5"),
},
{
name: "Max Tick",
tick: new Int("182402823"),
priceExpected: new Dec("340282300000000000000"),
},
{
name: "Min Tick",
tick: new Int("-108000000"),
priceExpected: new Dec("0.000000000001"),
},
]
);

// TEMORARY: commenting out until we confirm adding a buffer around current tick avoids invalid queries
// describe("estimateInitialTickBound", () => {
// // src: https://github.com/osmosis-labs/osmosis/blob/0b199ee187fbff02f68c2dc503d60efe617a67b2/x/concentrated-liquidity/tick_test.go#L1865
Expand Down
47 changes: 47 additions & 0 deletions packages/math/src/pool/concentrated/tick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,53 @@ export function tickToSqrtPrice(tickIndex: Int): Dec {
return approxSqrt(price);
}

export function tickToPrice(tickIndex: Int): Dec {
if (tickIndex.isZero()) {
return new Dec(1);
}

const geometricExponentIncrementDistanceInTicks = nine.mul(
powTenBigDec(new Int(exponentAtPriceOne).neg()).toDec()
);

if (tickIndex.lt(minTick) || tickIndex.gt(maxTick)) {
throw new Error(
`tickIndex is out of range: ${tickIndex.toString()}, min: ${minTick.toString()}, max: ${maxTick.toString()}`
);
}

const geometricExponentDelta = new Dec(tickIndex)
.quoTruncate(new Dec(geometricExponentIncrementDistanceInTicks.truncate()))
.truncate();

let exponentAtCurTick = new Int(exponentAtPriceOne).add(
geometricExponentDelta
);
if (tickIndex.lt(new Int(0))) {
exponentAtCurTick = exponentAtCurTick.sub(new Int(1));
}

const currentAdditiveIncrementInTicks = powTenBigDec(exponentAtCurTick);

const numAdditiveTicks = tickIndex.sub(
geometricExponentDelta.mul(
geometricExponentIncrementDistanceInTicks.truncate()
)
);

const price = powTenBigDec(geometricExponentDelta)
.add(new BigDec(numAdditiveTicks).mul(currentAdditiveIncrementInTicks))
.toDec();

if (price.gt(maxSpotPrice) || price.lt(minSpotPrice)) {
throw new Error(
`price is out of range: ${price.toString()}, min: ${minSpotPrice.toString()}, max: ${maxSpotPrice.toString()}`
);
}

return price;
}

/** PriceToTick takes a price and returns the corresponding tick index
* This function does not take into consideration tick spacing.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/queries/complex/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./chains";
export * from "./concentrated-liquidity";
export * from "./earn";
export * from "./get-timeout-height";
export * from "./orderbooks";
export * from "./osmosis";
export * from "./pools";
export * from "./portfolio";
Expand Down
158 changes: 158 additions & 0 deletions packages/server/src/queries/complex/orderbooks/active-orders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { Dec, Int } from "@keplr-wallet/unit";
import { tickToPrice } from "@osmosis-labs/math";
import { Chain } from "@osmosis-labs/types";
import { getAssetFromAssetList } from "@osmosis-labs/utils";
import cachified, { CacheEntry } from "cachified";
import dayjs from "dayjs";
import { LRUCache } from "lru-cache";

import { DEFAULT_LRU_OPTIONS } from "../../../utils/cache";
import { LimitOrder, queryOrderbookActiveOrders } from "../../osmosis";
import {
getOrderbookTickState,
getOrderbookTickUnrealizedCancels,
} from "./tick-state";
import type { MappedLimitOrder, OrderStatus } from "./types";

const activeOrdersCache = new LRUCache<string, CacheEntry>(DEFAULT_LRU_OPTIONS);

export function getOrderbookActiveOrders({
orderbookAddress,
userOsmoAddress,
chainList,
baseAsset,
quoteAsset,
}: {
orderbookAddress: string;
userOsmoAddress: string;
chainList: Chain[];
baseAsset: ReturnType<typeof getAssetFromAssetList>;
quoteAsset: ReturnType<typeof getAssetFromAssetList>;
}) {
return cachified({
cache: activeOrdersCache,
key: `orderbookActiveOrders-${orderbookAddress}-${userOsmoAddress}`,
ttl: 1500, // 1.5 seconds
getFreshValue: () =>
queryOrderbookActiveOrders({
orderbookAddress,
userAddress: userOsmoAddress,
chainList,
}).then(
async ({ data }: { data: { count: number; orders: LimitOrder[] } }) => {
const resp = await getTickInfoAndTransformOrders(
orderbookAddress,
data.orders,
chainList,
quoteAsset,
baseAsset
);
return resp;
}
),
});
}

function mapOrderStatus(order: LimitOrder, percentFilled: Dec): OrderStatus {
const quantInt = parseInt(order.quantity);
if (quantInt === 0 || percentFilled.equals(new Dec(1))) return "filled";
if (percentFilled.isZero()) return "open";
if (percentFilled.lt(new Dec(1))) return "partiallyFilled";

return "open";
}

async function getTickInfoAndTransformOrders(
orderbookAddress: string,
orders: LimitOrder[],
chainList: Chain[],
quoteAsset: ReturnType<typeof getAssetFromAssetList>,
baseAsset: ReturnType<typeof getAssetFromAssetList>
): Promise<MappedLimitOrder[]> {
if (orders.length === 0) return [];

const tickIds = [...new Set(orders.map((o) => o.tick_id))];

const [tickStates, unrealizedTickCancels] = await Promise.all([
getOrderbookTickState({
orderbookAddress,
chainList,
tickIds,
}),
getOrderbookTickUnrealizedCancels({
orderbookAddress,
chainList,
tickIds,
}),
]);

const fullTickState = tickStates.map(({ tick_id, tick_state }) => ({
tickId: tick_id,
tickState: tick_state,
unrealizedCancels: unrealizedTickCancels.find((c) => c.tick_id === tick_id),
}));

return orders.map((o) => {
const { tickState, unrealizedCancels } = fullTickState.find(
({ tickId }) => tickId === o.tick_id
) ?? { tickState: undefined, unrealizedCancels: undefined };

const quantity = parseInt(o.quantity);
const placedQuantity = parseInt(o.placed_quantity);

const percentClaimed = new Dec(
(placedQuantity - quantity) / placedQuantity
);

const normalizationFactor = new Dec(10).pow(
new Int((quoteAsset?.decimals ?? 0) - (baseAsset?.decimals ?? 0))
);
const [tickEtas, tickUnrealizedCancelled] =
o.order_direction === "bid"
? [
parseInt(
tickState?.bid_values.effective_total_amount_swapped ?? "0"
),
parseInt(
unrealizedCancels?.unrealized_cancels.bid_unrealized_cancels ??
"0"
),
]
: [
parseInt(
tickState?.ask_values.effective_total_amount_swapped ?? "0"
),
parseInt(
unrealizedCancels?.unrealized_cancels.ask_unrealized_cancels ??
"0"
),
];
const tickTotalEtas = tickEtas + tickUnrealizedCancelled;
const totalFilled = Math.max(
tickTotalEtas - (parseInt(o.etas) - (placedQuantity - quantity)),
0
);
const percentFilled = new Dec(Math.min(totalFilled / placedQuantity, 1));
const price = tickToPrice(new Int(o.tick_id));
const status = mapOrderStatus(o, percentFilled);
const output =
o.order_direction === "bid"
? new Dec(placedQuantity).quo(price)
: new Dec(placedQuantity).mul(price);
return {
...o,
price: price.quo(normalizationFactor),
quantity,
placed_quantity: placedQuantity,
percentClaimed,
totalFilled,
percentFilled,
orderbookAddress,
status,
output,
quoteAsset,
baseAsset,
placed_at: dayjs(parseInt(o.placed_at) / 1_000).unix(),
};
});
}
45 changes: 45 additions & 0 deletions packages/server/src/queries/complex/orderbooks/denoms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { AssetList, Chain } from "@osmosis-labs/types";
import { getAssetFromAssetList } from "@osmosis-labs/utils";
import cachified, { CacheEntry } from "cachified";
import { LRUCache } from "lru-cache";

import { DEFAULT_LRU_OPTIONS } from "../../../utils/cache";
import { getOrderbookPools } from "./pools";

const orderbookDenomsCache = new LRUCache<string, CacheEntry>(
DEFAULT_LRU_OPTIONS
);

export function getOrderbookDenoms({
orderbookAddress,
assetLists,
}: {
orderbookAddress: string;
chainList: Chain[];
assetLists: AssetList[];
}) {
return cachified({
cache: orderbookDenomsCache,
key: `orderbookDenoms-${orderbookAddress}`,
ttl: 1000 * 60 * 60 * 24 * 30, // 30 days (unlikely to change)
getFreshValue: () =>
getOrderbookPools().then((pools) => {
const pool = pools.find((p) => p.contractAddress === orderbookAddress);
if (!pool)
return {
quoteAsset: undefined,
baseAsset: undefined,
};
const quoteAsset = getAssetFromAssetList({
coinMinimalDenom: pool.quoteDenom,
assetLists,
});
const baseAsset = getAssetFromAssetList({
coinMinimalDenom: pool.baseDenom,
assetLists,
});

return { quoteAsset, baseAsset };
}),
});
}
Loading

0 comments on commit aab2946

Please sign in to comment.