diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index d7a3d2fe..5cd1c5a4 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -14,23 +14,23 @@ module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['react-refresh', 'react-hooks'],
rules: {
- // 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
- // indent: 'off',
- // '@typescript-eslint/indent': 'off',
- // 'multiline-ternary': 'off',
- // 'no-unused-vars': 'off',
- // '@typescript-eslint/no-unused-vars': 'off',
- // '@typescript-eslint/explicit-function-return-type': 'off',
- // '@typescript-eslint/prefer-reduce-type-parameter': 'off',
- // '@typescript-eslint/strict-boolean-expressions': 'off',
- // '@typescript-eslint/space-before-function-paren': 'off',
- // '@typescript-eslint/prefer-nullish-coalescing': 'off',
- // '@typescript-eslint/member-delimiter-style': 'off',
+ 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
+ indent: 'off',
+ '@typescript-eslint/indent': 'off',
+ 'multiline-ternary': 'off',
+ 'no-unused-vars': 'off',
+ '@typescript-eslint/no-unused-vars': 'off',
+ '@typescript-eslint/explicit-function-return-type': 'off',
+ '@typescript-eslint/prefer-reduce-type-parameter': 'off',
+ '@typescript-eslint/strict-boolean-expressions': 'off',
+ '@typescript-eslint/space-before-function-paren': 'off',
+ '@typescript-eslint/prefer-nullish-coalescing': 'off',
+ '@typescript-eslint/member-delimiter-style': 'off',
'@typescript-eslint/no-explicit-any': 'off',
- // 'generator-star-spacing': ['error', { before: false, after: true }],
- // 'yield-star-spacing': ['error', { before: false, after: true }],
+ 'generator-star-spacing': ['error', { before: false, after: true }],
+ 'yield-star-spacing': ['error', { before: false, after: true }],
'react-hooks/exhaustive-deps': 'off',
- // 'react-hooks/rules-of-hooks': 'error',
+ 'react-hooks/rules-of-hooks': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
diff --git a/.storybook/decorators.jsx b/.storybook/decorators.jsx
index 7ee29888..ba553cdb 100644
--- a/.storybook/decorators.jsx
+++ b/.storybook/decorators.jsx
@@ -1,8 +1,4 @@
import React from 'react'
import { Provider } from 'react-redux'
-// export const withMaterialStyles = storyFn => (
-// {storyFn()}
-// )
-
export const withStore = store => storyFn => {storyFn()}
diff --git a/.storybook/preview.ts b/.storybook/preview.ts
index 6e1333f4..2116d026 100644
--- a/.storybook/preview.ts
+++ b/.storybook/preview.ts
@@ -73,7 +73,6 @@ const preview: Preview = {
]
},
createBrowserRouter: { withRouter }
- // actions: { argTypesRegex: '^on.*' }
},
decorators: [
@@ -85,7 +84,6 @@ const preview: Preview = {
Provider: ThemeProvider,
GlobalStyles: CssBaseline
})
- // withRouter
]
}
diff --git a/index.html b/index.html
index bcecd014..56076cd3 100644
--- a/index.html
+++ b/index.html
@@ -69,9 +69,6 @@
-
-
diff --git a/src/static/svg/twitter-ic-footer.svg b/src/static/svg/twitter-ic-footer.svg
deleted file mode 100644
index d4f2b087..00000000
--- a/src/static/svg/twitter-ic-footer.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/src/static/svg/twitterCircle.svg b/src/static/svg/twitterCircle.svg
deleted file mode 100644
index 8b083331..00000000
--- a/src/static/svg/twitterCircle.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
diff --git a/public/unknownToken.svg b/src/static/svg/unknownToken.svg
similarity index 100%
rename from public/unknownToken.svg
rename to src/static/svg/unknownToken.svg
diff --git a/src/static/svg/xCircle.svg b/src/static/svg/xCircle.svg
new file mode 100644
index 00000000..30cb29db
--- /dev/null
+++ b/src/static/svg/xCircle.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/static/theme/index.ts b/src/static/theme/index.ts
index c524a747..37b289f1 100644
--- a/src/static/theme/index.ts
+++ b/src/static/theme/index.ts
@@ -1,17 +1,16 @@
-// theme.ts
import { createTheme } from '@mui/material/styles'
export const colors = {
black: {
full: '#000000',
- background: '#1B1C2A', // v2.0
+ background: '#1B1C2A',
light: '#090B1B',
kinda: '#1A1A1A',
greyish: '#081323',
- cinder: '#0E0C12', // v2.0 background color
- controls: '#44424E', // v2.0 controls background color
- header: '#1A1D28', // v2.0 header
- card: '#28242E' // v2.0 card color
+ cinder: '#0E0C12',
+ controls: '#44424E',
+ header: '#1A1D28',
+ card: '#28242E'
},
blue: {
accent: '#072E5A',
@@ -22,7 +21,7 @@ export const colors = {
neon: '#08F7FE',
astel: '#48ADF1',
bastille: '#1E1A23',
- charade: '#272735', // v2.0 component
+ charade: '#272735',
deep: '#4B5983'
},
green: {
@@ -47,7 +46,6 @@ export const colors = {
neon: '#F5D300'
},
navy: {
- // colors with suffix "2" on figma
background: '#0C0D2C',
dark: '#0E0E2A',
component: '#1D1D49',
@@ -158,12 +156,11 @@ export const typography = {
}
}
-// Create a theme instance.
export const theme = createTheme({
palette: {
primary: {
- main: colors.navy.button, // v2.0
- contrastText: colors.navy.veryLightGrey // v2.0
+ main: colors.navy.button,
+ contrastText: colors.navy.veryLightGrey
},
secondary: {
main: colors.green.button,
@@ -198,15 +195,4 @@ export const theme = createTheme({
xl: 1920
}
}
- // overrides: {
- // MuiInputBase: {
- // input: {
- // MozAppearance: "textfield",
- // "&::-webkit-clear-button, &::-webkit-outer-spin-button, &::-webkit-inner-spin-button":
- // {
- // display: "none",
- // },
- // },
- // },
- // },
})
diff --git a/src/store/consts/static.ts b/src/store/consts/static.ts
index 93c9f9fa..89a292c9 100644
--- a/src/store/consts/static.ts
+++ b/src/store/consts/static.ts
@@ -1,6 +1,7 @@
import {
FEE_TIERS,
Network,
+ Position,
TESTNET_BTC_ADDRESS,
TESTNET_ETH_ADDRESS,
TESTNET_USDC_ADDRESS,
@@ -8,7 +9,8 @@ import {
} from '@invariant-labs/a0-sdk'
import { Keyring } from '@polkadot/api'
import { BestTier, FormatNumberThreshold, PrefixConfig, Token, TokenPriceData } from './types'
-import { testnetBestTiersCreator } from './utils'
+import { testnetBestTiersCreator } from '@utils/utils'
+import { POSITIONS_ENTRIES_LIMIT } from '@invariant-labs/a0-sdk/target/consts'
export enum AlephZeroNetworks {
TEST = 'wss://ws.test.azero.dev',
@@ -255,3 +257,24 @@ export const reversedAddressTickerMap = Object.fromEntries(
export const LIQUIDITY_PLOT_DECIMAL = 12n
export const DEFAULT_TOKEN_DECIMAL = 12n
+
+export const EMPTY_POSITION: Position = {
+ poolKey: {
+ tokenX: TESTNET_BTC_ADDRESS,
+ tokenY: TESTNET_ETH_ADDRESS,
+ feeTier: { fee: 0n, tickSpacing: 1n }
+ },
+ liquidity: 0n,
+ lowerTickIndex: 0n,
+ upperTickIndex: 0n,
+ feeGrowthInsideX: 0n,
+ feeGrowthInsideY: 0n,
+ lastBlockNumber: 0n,
+ tokensOwedX: 0n,
+ tokensOwedY: 0n
+}
+
+export const POSITIONS_PER_QUERY =
+ Number(POSITIONS_ENTRIES_LIMIT) - (Number(POSITIONS_ENTRIES_LIMIT) % POSITIONS_PER_PAGE)
+
+export const MINIMAL_POOL_INIT_PRICE = 0.00000001
diff --git a/src/store/consts/types.ts b/src/store/consts/types.ts
index 5c58efc5..ef1f56bc 100644
--- a/src/store/consts/types.ts
+++ b/src/store/consts/types.ts
@@ -35,6 +35,7 @@ export type SimulateResult = {
amountOut: bigint
priceImpact: number
targetSqrtPrice: bigint
+ fee: bigint
errors: SwapError[]
}
diff --git a/src/store/reducers/connection.ts b/src/store/reducers/connection.ts
index 920d8c53..5a8ea165 100644
--- a/src/store/reducers/connection.ts
+++ b/src/store/reducers/connection.ts
@@ -1,4 +1,4 @@
-import { Network, TESTNET_INVARIANT_ADDRESS } from '@invariant-labs/a0-sdk'
+import { Network, TESTNET_INVARIANT_ADDRESS, TESTNET_WAZERO_ADDRESS } from '@invariant-labs/a0-sdk'
import { PayloadAction, createSlice } from '@reduxjs/toolkit'
import { AlephZeroNetworks } from '@store/consts/static'
import { PayloadType } from '@store/consts/types'
@@ -16,6 +16,7 @@ export interface IAlephZeroConnectionStore {
blockNumber: number
rpcAddress: string
invariantAddress: string
+ wrappedAZEROAddress: string
}
export const defaultState: IAlephZeroConnectionStore = {
@@ -24,7 +25,8 @@ export const defaultState: IAlephZeroConnectionStore = {
networkType: Network.Testnet,
blockNumber: 0,
rpcAddress: AlephZeroNetworks.TEST,
- invariantAddress: TESTNET_INVARIANT_ADDRESS
+ invariantAddress: TESTNET_INVARIANT_ADDRESS,
+ wrappedAZEROAddress: TESTNET_WAZERO_ADDRESS
}
export const connectionSliceName = 'connection'
const connectionSlice = createSlice({
diff --git a/src/store/reducers/pools.ts b/src/store/reducers/pools.ts
index 1de5e8e9..7c809dcd 100644
--- a/src/store/reducers/pools.ts
+++ b/src/store/reducers/pools.ts
@@ -13,7 +13,7 @@ import {
import { PayloadAction, createSlice } from '@reduxjs/toolkit'
import { AZERO, BTC, ETH, USDC } from '@store/consts/static'
import { PayloadType, Token } from '@store/consts/types'
-import { poolKeyToString } from '@store/consts/utils'
+import { poolKeyToString } from '@utils/utils'
import * as R from 'remeda'
@@ -113,9 +113,6 @@ const poolsSlice = createSlice({
name: poolsSliceName,
initialState: defaultState,
reducers: {
- initPool(state, _action: PayloadAction) {
- return state
- },
addTokens(state, action: PayloadAction>) {
state.tokens = {
...state.tokens,
@@ -149,11 +146,7 @@ const poolsSlice = createSlice({
const { poolKey } = action.payload
const keyStringified = poolKeyToString(poolKey)
- // Check if a pool with the same PoolKey already exists
- // if (!state.pools[keyStringified]) {
- // If the pool does not exist, add it to the pools object
state.pools[keyStringified] = action.payload
- // }
}
state.isLoadingLatestPoolsForTransaction = false
diff --git a/src/store/reducers/positions.ts b/src/store/reducers/positions.ts
index da5aef23..294fbfac 100644
--- a/src/store/reducers/positions.ts
+++ b/src/store/reducers/positions.ts
@@ -3,6 +3,8 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit'
import { PayloadType } from '@store/consts/types'
export interface PositionsListStore {
+ length: bigint
+ loadedPages: Record
list: Position[]
loading: boolean
}
@@ -13,6 +15,9 @@ export interface PlotTickData {
}
export type TickPlotPositionData = Omit
+
+export type InitMidPrice = TickPlotPositionData & { sqrtPrice: bigint }
+
export interface PlotTicks {
data: PlotTickData[]
loading: boolean
@@ -87,6 +92,8 @@ export const defaultState: IPositionsStore = {
loading: false
},
positionsList: {
+ length: 0n,
+ loadedPages: {},
list: [],
loading: true
},
@@ -142,10 +149,46 @@ const positionsSlice = createSlice({
state.positionsList.loading = false
return state
},
- getPositionsList(state) {
+ getPositionsListPage(state, _action: PayloadAction<{ index: number; refresh: boolean }>) {
+ state.positionsList.loading = true
+ return state
+ },
+ getRemainingPositions(state, _action: PayloadAction) {
state.positionsList.loading = true
return state
},
+ setPositionsListLength(state, action: PayloadAction) {
+ state.positionsList.length = action.payload
+ return state
+ },
+ setPositionsListLoadedStatus(
+ state,
+ action: PayloadAction<{ indexes: number[]; isLoaded: boolean }>
+ ) {
+ const { indexes, isLoaded } = action.payload
+
+ for (const index of indexes) {
+ state.positionsList.loadedPages[index] = isLoaded
+ }
+
+ return state
+ },
+ removePosition(state, action: PayloadAction) {
+ if (Number(action.payload) !== state.positionsList.list.length - 1) {
+ state.positionsList.list[Number(action.payload)] =
+ state.positionsList.list[state.positionsList.list.length - 1]
+ }
+
+ state.positionsList.list.pop()
+ state.positionsList.length -= 1n
+
+ return state
+ },
+ addPosition(state, action: PayloadAction) {
+ state.positionsList.list.push(action.payload)
+ state.positionsList.length += 1n
+ return state
+ },
getSinglePosition(state, _action: PayloadAction) {
return state
},
diff --git a/src/store/reducers/snackbars.ts b/src/store/reducers/snackbars.ts
index 2a8507ce..69cc47c5 100644
--- a/src/store/reducers/snackbars.ts
+++ b/src/store/reducers/snackbars.ts
@@ -1,6 +1,6 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { PayloadType } from '../consts/types'
-import { createLoaderKey } from '@store/consts/utils'
+import { createLoaderKey } from '@utils/utils'
import { SnackbarAction, VariantType } from 'notistack'
export interface ISnackbar {
diff --git a/src/store/reducers/swap.ts b/src/store/reducers/swap.ts
index 5937e865..794af2ee 100644
--- a/src/store/reducers/swap.ts
+++ b/src/store/reducers/swap.ts
@@ -44,6 +44,7 @@ export const defaultState: ISwapStore = {
amountOut: 0n,
priceImpact: 0,
targetSqrtPrice: 0n,
+ fee: 0n,
errors: []
}
}
diff --git a/src/store/sagas/connection.ts b/src/store/sagas/connection.ts
index 6a182cc7..6e1a8baa 100644
--- a/src/store/sagas/connection.ts
+++ b/src/store/sagas/connection.ts
@@ -1,22 +1,83 @@
import { all, call, put, SagaGenerator, select, takeLeading, spawn, delay } from 'typed-redux-saga'
import { actions, Status, PayloadTypes } from '@store/reducers/connection'
import { actions as snackbarsActions } from '@store/reducers/snackbars'
-import { rpcAddress, networkType } from '@store/selectors/connection'
+import {
+ rpcAddress,
+ networkType,
+ invariantAddress,
+ wrappedAZEROAddress
+} from '@store/selectors/connection'
import { PayloadAction } from '@reduxjs/toolkit'
import { ApiPromise } from '@polkadot/api'
import apiSingleton from '@store/services/apiSingleton'
+import invariantSingleton from '@store/services/invariantSingleton'
+import { Invariant, PSP22, WrappedAZERO } from '@invariant-labs/a0-sdk'
+import SingletonPSP22 from '@store/services/psp22Singleton'
+import SingletonWrappedAZERO from '@store/services/wrappedAZEROSingleton'
-export function* getConnection(): SagaGenerator {
- const rpc = yield* select(rpcAddress)
- const network = yield* select(networkType)
- const api = yield* call([apiSingleton, apiSingleton.loadInstance], network, rpc)
+export function* getApi(): SagaGenerator {
+ let api = yield* call([apiSingleton, apiSingleton.getInstance])
+
+ if (!api) {
+ const network = yield* select(networkType)
+ const rpc = yield* select(rpcAddress)
+ api = yield* call([apiSingleton, apiSingleton.loadInstance], network, rpc)
+ }
return api
}
+export function* getInvariant(): SagaGenerator {
+ let invariant = yield* call([invariantSingleton, invariantSingleton.getInstance])
+
+ if (!invariant) {
+ const api = yield* call(getApi)
+ const network = yield* select(networkType)
+ const invariantAddr = yield* select(invariantAddress)
+ invariant = yield* call(
+ [invariantSingleton, invariantSingleton.loadInstance],
+ api,
+ network,
+ invariantAddr
+ )
+ }
+
+ return invariant
+}
+
+export function* getPSP22(): SagaGenerator {
+ let psp22 = yield* call([SingletonPSP22, SingletonPSP22.getInstance])
+
+ if (!psp22) {
+ const api = yield* call(getApi)
+ const network = yield* select(networkType)
+ psp22 = yield* call([SingletonPSP22, SingletonPSP22.loadInstance], api, network)
+ }
+
+ return psp22
+}
+
+export function* getWrappedAZERO(): SagaGenerator {
+ let wrappedAZERO = yield* call([SingletonWrappedAZERO, SingletonWrappedAZERO.getInstance])
+
+ if (!wrappedAZERO) {
+ const api = yield* call(getApi)
+ const network = yield* select(networkType)
+ const wrappedAZEROAddr = yield* select(wrappedAZEROAddress)
+ wrappedAZERO = yield* call(
+ [SingletonWrappedAZERO, SingletonWrappedAZERO.loadInstance],
+ api,
+ network,
+ wrappedAZEROAddr
+ )
+ }
+
+ return wrappedAZERO
+}
+
export function* initConnection(): Generator {
try {
- yield* call(getConnection)
+ yield* getApi()
yield* put(
snackbarsActions.add({
@@ -44,8 +105,7 @@ export function* initConnection(): Generator {
export function* handleNetworkChange(action: PayloadAction): Generator {
yield* delay(1000)
- const { networkType, rpcAddress } = action.payload
- yield* call([apiSingleton, apiSingleton.loadInstance], networkType, rpcAddress)
+ yield* getApi()
yield* put(
snackbarsActions.add({
diff --git a/src/store/sagas/pools.ts b/src/store/sagas/pools.ts
index 13ddacbc..15ec55ff 100644
--- a/src/store/sagas/pools.ts
+++ b/src/store/sagas/pools.ts
@@ -1,13 +1,11 @@
-import { PoolKey, newPoolKey, sendTx, toSqrtPrice } from '@invariant-labs/a0-sdk'
-import { Signer } from '@polkadot/api/types'
+import { PoolKey, newPoolKey } from '@invariant-labs/a0-sdk'
import { PayloadAction } from '@reduxjs/toolkit'
import {
- createLoaderKey,
findPairs,
getPoolsByPoolKeys,
getTokenBalances,
getTokenDataByAddresses
-} from '@store/consts/utils'
+} from '@utils/utils'
import {
FetchTicksAndTickMaps,
ListPoolsRequest,
@@ -15,30 +13,18 @@ import {
PoolWithPoolKey,
actions
} from '@store/reducers/pools'
-import { actions as snackbarsActions } from '@store/reducers/snackbars'
import { actions as walletActions } from '@store/reducers/wallet'
-import { invariantAddress, networkType } from '@store/selectors/connection'
import { tokens } from '@store/selectors/pools'
import { address } from '@store/selectors/wallet'
-import invariantSingleton from '@store/services/invariantSingleton'
-import { getAlephZeroWallet } from '@utils/web3/wallet'
-import { closeSnackbar } from 'notistack'
import { all, call, put, select, spawn, takeEvery, takeLatest } from 'typed-redux-saga'
-import { getConnection } from './connection'
import { MAX_POOL_KEYS_RETURNED } from '@invariant-labs/a0-sdk/target/consts'
+import { getInvariant, getPSP22 } from './connection'
export function* fetchPoolsDataForList(action: PayloadAction) {
const walletAddress = yield* select(address)
- const connection = yield* call(getConnection)
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
- const pools = yield* call(
- getPoolsByPoolKeys,
- invAddress,
- action.payload.poolKeys,
- connection,
- network
- )
+ const invariant = yield* getInvariant()
+ const pools = yield* call(getPoolsByPoolKeys, invariant, action.payload.poolKeys)
+ const psp22 = yield* getPSP22()
const allTokens = yield* select(tokens)
const unknownTokens = new Set(
@@ -55,17 +41,10 @@ export function* fetchPoolsDataForList(action: PayloadAction)
const unknownTokensData = yield* call(
getTokenDataByAddresses,
[...unknownTokens],
- connection,
- network,
- walletAddress
- )
- const knownTokenBalances = yield* call(
- getTokenBalances,
- [...knownTokens],
- connection,
- network,
+ psp22,
walletAddress
)
+ const knownTokenBalances = yield* call(getTokenBalances, [...knownTokens], psp22, walletAddress)
yield* put(walletActions.getBalances(Object.keys(unknownTokensData)))
yield* put(actions.addTokens(unknownTokensData))
@@ -76,91 +55,11 @@ export function* fetchPoolsDataForList(action: PayloadAction)
yield* put(actions.addPoolsForList({ data: pools, listType: action.payload.listType }))
}
-export function* handleInitPool(action: PayloadAction): Generator {
- const loaderKey = createLoaderKey()
- const loaderSigningTx = createLoaderKey()
- try {
- yield put(
- snackbarsActions.add({
- message: 'Creating new pool...',
- variant: 'pending',
- persist: true,
- key: loaderKey
- })
- )
-
- const { tokenX, tokenY, feeTier } = action.payload
-
- const api = yield* getConnection()
- const network = yield* select(networkType)
- const walletAddress = yield* select(address)
- const adapter = yield* call(getAlephZeroWallet)
- const invAddress = yield* select(invariantAddress)
-
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
-
- const poolKey = newPoolKey(tokenX, tokenY, feeTier)
-
- const initSqrtPrice = toSqrtPrice(1n, 0n)
-
- const tx = yield* call([invariant, invariant.createPoolTx], poolKey, initSqrtPrice)
-
- yield put(
- snackbarsActions.add({
- message: 'Signing transaction...',
- variant: 'pending',
- persist: true,
- key: loaderSigningTx
- })
- )
-
- const signedTx = yield* call([tx, tx.signAsync], walletAddress, {
- signer: adapter.signer as Signer
- })
-
- closeSnackbar(loaderSigningTx)
- yield put(snackbarsActions.remove(loaderSigningTx))
-
- const txResult = yield* call(sendTx, signedTx)
-
- yield put(
- snackbarsActions.add({
- message: 'Pool successfully created',
- variant: 'success',
- persist: false,
- txid: txResult.hash
- })
- )
-
- closeSnackbar(loaderKey)
- yield put(snackbarsActions.remove(loaderKey))
- } catch (error) {
- console.log(error)
- closeSnackbar(loaderKey)
- yield put(snackbarsActions.remove(loaderKey))
- closeSnackbar(loaderSigningTx)
- yield put(snackbarsActions.remove(loaderSigningTx))
- }
-}
-
export function* fetchPoolData(action: PayloadAction): Generator {
- const api = yield* getConnection()
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
const { feeTier, tokenX, tokenY } = action.payload
try {
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const invariant = yield* getInvariant()
const pool = yield* call([invariant, invariant.getPool], tokenX, tokenY, feeTier)
@@ -181,17 +80,8 @@ export function* fetchPoolData(action: PayloadAction): Generator {
}
export function* fetchAllPoolKeys(): Generator {
- const api = yield* getConnection()
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
-
try {
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const invariant = yield* getInvariant()
const [poolKeys, poolKeysCount] = yield* call(
[invariant, invariant.getPoolKeys],
@@ -216,15 +106,7 @@ export function* fetchAllPoolKeys(): Generator {
}
export function* fetchAllPoolsForPairData(action: PayloadAction) {
- const api = yield* call(getConnection)
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const invariant = yield* getInvariant()
const token0 = action.payload.first.toString()
const token1 = action.payload.second.toString()
@@ -240,16 +122,7 @@ export function* fetchTicksAndTickMaps(action: PayloadAction
@@ -281,12 +154,37 @@ export function* fetchTicksAndTickMaps(action: PayloadAction
+ [tokenX, tokenY].filter(token => !allTokens[token])
+ )
+ )
+ const knownTokens = new Set(
+ poolsWithPoolKeys.flatMap(({ poolKey: { tokenX, tokenY } }) =>
+ [tokenX, tokenY].filter(token => allTokens[token])
+ )
+ )
+
+ const unknownTokensData = yield* call(
+ getTokenDataByAddresses,
+ [...unknownTokens],
+ psp22,
+ walletAddress
+ )
+ const knownTokenBalances = yield* call(getTokenBalances, [...knownTokens], psp22, walletAddress)
+
+ yield* put(walletActions.getBalances(Object.keys(unknownTokensData)))
+ yield* put(actions.addTokens(unknownTokensData))
+ yield* put(actions.updateTokenBalances(knownTokenBalances))
}
-export function* initPoolHandler(): Generator {
- yield* takeLatest(actions.initPool, handleInitPool)
+export function* getPoolsDataForListHandler(): Generator {
+ yield* takeEvery(actions.getPoolsDataForList, fetchPoolsDataForList)
}
export function* getPoolDataHandler(): Generator {
@@ -308,7 +206,6 @@ export function* getTicksAndTickMapsHandler(): Generator {
export function* poolsSaga(): Generator {
yield all(
[
- initPoolHandler,
getPoolDataHandler,
getPoolKeysHandler,
getPoolsDataForListHandler,
diff --git a/src/store/sagas/positions.ts b/src/store/sagas/positions.ts
index 1f4d3c42..a6c4a592 100644
--- a/src/store/sagas/positions.ts
+++ b/src/store/sagas/positions.ts
@@ -1,13 +1,15 @@
-import { PoolKey, TESTNET_WAZERO_ADDRESS, sendTx } from '@invariant-labs/a0-sdk'
+import { Pool, Position, TESTNET_WAZERO_ADDRESS, sendTx } from '@invariant-labs/a0-sdk'
import { Signer } from '@polkadot/api/types'
import { PayloadAction } from '@reduxjs/toolkit'
import {
+ EMPTY_POSITION,
ErrorMessage,
INVARIANT_CLAIM_FEE_OPTIONS,
INVARIANT_CREATE_POOL_OPTIONS,
INVARIANT_CREATE_POSITION_OPTIONS,
INVARIANT_REMOVE_POSITION_OPTIONS,
INVARIANT_WITHDRAW_ALL_WAZERO,
+ POSITIONS_PER_QUERY,
PSP22_APPROVE_OPTIONS,
U128MAX,
WAZERO_DEPOSIT_OPTIONS
@@ -20,7 +22,7 @@ import {
ensureError,
isErrorMessage,
poolKeyToString
-} from '@store/consts/utils'
+} from '@utils/utils'
import { FetchTicksAndTickMaps, ListType, actions as poolsActions } from '@store/reducers/pools'
import {
ClosePositionData,
@@ -32,20 +34,18 @@ import {
} from '@store/reducers/positions'
import { actions as snackbarsActions } from '@store/reducers/snackbars'
import { actions as walletActions } from '@store/reducers/wallet'
-import { invariantAddress, networkType } from '@store/selectors/connection'
+import { invariantAddress } from '@store/selectors/connection'
import { poolsArraySortedByFees, tickMaps, tokens } from '@store/selectors/pools'
import { address, balance } from '@store/selectors/wallet'
-import invariantSingleton from '@store/services/invariantSingleton'
-import psp22Singleton from '@store/services/psp22Singleton'
-import wrappedAZEROSingleton from '@store/services/wrappedAZEROSingleton'
import { getAlephZeroWallet } from '@utils/web3/wallet'
import { closeSnackbar } from 'notistack'
import { all, call, fork, join, put, select, spawn, takeEvery, takeLatest } from 'typed-redux-saga'
-import { getConnection } from './connection'
-import { fetchTicksAndTickMaps } from './pools'
+import { fetchTicksAndTickMaps, fetchTokens } from './pools'
import { fetchBalances } from './wallet'
import { SubmittableExtrinsic } from '@polkadot/api/promise/types'
import { calculateTokenAmountsWithSlippage } from '@invariant-labs/a0-sdk/target/utils'
+import { positionsList } from '@store/selectors/positions'
+import { getApi, getInvariant, getPSP22, getWrappedAZERO } from './connection'
function* handleInitPosition(action: PayloadAction): Generator {
const {
@@ -82,15 +82,14 @@ function* handleInitPosition(action: PayloadAction): Generator
})
)
- const api = yield* getConnection()
- const network = yield* select(networkType)
+ const api = yield* getApi()
const walletAddress = yield* select(address)
const adapter = yield* call(getAlephZeroWallet)
const invAddress = yield* select(invariantAddress)
const txs = []
- const psp22 = yield* call([psp22Singleton, psp22Singleton.loadInstance], api, network)
+ const psp22 = yield* getPSP22()
const [xAmountWithSlippage, yAmountWithSlippage] = calculateTokenAmountsWithSlippage(
feeTier.tickSpacing,
@@ -108,12 +107,7 @@ function* handleInitPosition(action: PayloadAction): Generator
const YTokenTx = psp22.approveTx(invAddress, yAmountWithSlippage, tokenY, PSP22_APPROVE_OPTIONS)
txs.push(YTokenTx)
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const invariant = yield* getInvariant()
if (initPool) {
const createPoolTx = invariant.createPoolTx(
@@ -174,7 +168,9 @@ function* handleInitPosition(action: PayloadAction): Generator
})
)
- yield put(actions.getPositionsList())
+ const { length } = yield* select(positionsList)
+ const position = yield* call([invariant, invariant.getPosition], walletAddress, length)
+ yield* put(actions.addPosition(position))
yield* call(fetchBalances, [tokenX, tokenY])
@@ -236,8 +232,7 @@ function* handleInitPositionWithAZERO(action: PayloadAction):
})
)
- const api = yield* getConnection()
- const network = yield* select(networkType)
+ const api = yield* getApi()
const walletAddress = yield* select(address)
const adapter = yield* call(getAlephZeroWallet)
const invAddress = yield* select(invariantAddress)
@@ -245,13 +240,9 @@ function* handleInitPositionWithAZERO(action: PayloadAction):
const txs = []
- const wazero = yield* call(
- [wrappedAZEROSingleton, wrappedAZEROSingleton.loadInstance],
- api,
- network
- )
+ const wazero = yield* getWrappedAZERO()
- const psp22 = yield* call([psp22Singleton, psp22Singleton.loadInstance], api, network)
+ const psp22 = yield* getPSP22()
const [xAmountWithSlippage, yAmountWithSlippage] = calculateTokenAmountsWithSlippage(
feeTier.tickSpacing,
@@ -278,12 +269,7 @@ function* handleInitPositionWithAZERO(action: PayloadAction):
const YTokenTx = psp22.approveTx(invAddress, yAmountWithSlippage, tokenY, PSP22_APPROVE_OPTIONS)
txs.push(YTokenTx)
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const invariant = yield* getInvariant()
if (initPool) {
const createPoolTx = invariant.createPoolTx(
@@ -369,7 +355,9 @@ function* handleInitPositionWithAZERO(action: PayloadAction):
yield put(walletActions.getBalances([tokenX, tokenY]))
- yield put(actions.getPositionsList())
+ const { length } = yield* select(positionsList)
+ const position = yield* call([invariant, invariant.getPosition], walletAddress, length)
+ yield* put(actions.addPosition(position))
yield* call(fetchBalances, [tokenX === TESTNET_WAZERO_ADDRESS ? tokenY : tokenX])
@@ -405,57 +393,10 @@ function* handleInitPositionWithAZERO(action: PayloadAction):
}
}
-export function* handleGetPositionsList() {
- try {
- const api = yield* getConnection()
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
- const walletAddress = yield* select(address)
-
- const positions = yield* call([invariant, invariant.getAllPositions], walletAddress)
-
- const pools: PoolKey[] = []
- const poolSet: Set = new Set()
- for (let i = 0; i < positions.length; i++) {
- const poolKeyString = poolKeyToString(positions[i].poolKey)
-
- if (!poolSet.has(poolKeyString)) {
- poolSet.add(poolKeyString)
- pools.push(positions[i].poolKey)
- }
- }
-
- yield* put(
- poolsActions.getPoolsDataForList({
- poolKeys: Array.from(pools),
- listType: ListType.POSITIONS
- })
- )
-
- yield* put(actions.setPositionsList(positions))
- } catch (e) {
- yield* put(actions.setPositionsList([]))
- }
-}
-
export function* handleGetCurrentPositionTicks(action: PayloadAction) {
const { poolKey, lowerTickIndex, upperTickIndex } = action.payload
- const api = yield* getConnection()
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
-
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+
+ const invariant = yield* getInvariant()
const [lowerTick, upperTick] = yield* all([
call([invariant, invariant.getTick], poolKey, lowerTickIndex),
@@ -472,9 +413,6 @@ export function* handleGetCurrentPositionTicks(action: PayloadAction): Generator {
const { poolKey, isXtoY, fetchTicksAndTickmap } = action.payload
- const api = yield* getConnection()
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
let allTickmaps = yield* select(tickMaps)
const allTokens = yield* select(tokens)
const allPools = yield* select(poolsArraySortedByFees)
@@ -483,12 +421,7 @@ export function* handleGetCurrentPlotTicks(action: PayloadAction = {
@@ -576,16 +509,8 @@ export function* handleClaimFee(action: PayloadAction) {
})
)
const walletAddress = yield* select(address)
- const api = yield* getConnection()
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const invariant = yield* getInvariant()
const adapter = yield* call(getAlephZeroWallet)
@@ -674,18 +599,12 @@ export function* handleClaimFeeWithAZERO(action: PayloadAction)
)
const walletAddress = yield* select(address)
- const api = yield* getConnection()
- const network = yield* select(networkType)
+ const api = yield* getApi()
const invAddress = yield* select(invariantAddress)
const { index, addressTokenX, addressTokenY } = action.payload
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
- const psp22 = yield* call([psp22Singleton, psp22Singleton.loadInstance], api, network)
+ const invariant = yield* getInvariant()
+ const psp22 = yield* getPSP22()
const adapter = yield* call(getAlephZeroWallet)
const txs = []
@@ -785,15 +704,7 @@ export function* handleClaimFeeWithAZERO(action: PayloadAction)
export function* handleGetSinglePosition(action: PayloadAction) {
try {
const walletAddress = yield* select(address)
- const api = yield* getConnection()
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const invariant = yield* getInvariant()
const position = yield* call([invariant, invariant.getPosition], walletAddress, action.payload)
yield* put(
actions.setSinglePosition({
@@ -841,18 +752,21 @@ export function* handleClosePosition(action: PayloadAction) {
)
const walletAddress = yield* select(address)
- const api = yield* getConnection()
- const network = yield* select(networkType)
- const invAddress = yield* select(invariantAddress)
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const invariant = yield* getInvariant()
const adapter = yield* call(getAlephZeroWallet)
+ const allPositions = yield* select(positionsList)
+ const getPositionsListPagePayload: PayloadAction<{ index: number; refresh: boolean }> = {
+ type: actions.getPositionsListPage.type,
+ payload: {
+ index: Math.floor(Number(allPositions.length) / POSITIONS_PER_QUERY),
+ refresh: false
+ }
+ }
+ const fetchTask = yield* fork(handleGetPositionsListPage, getPositionsListPagePayload)
+ yield* join(fetchTask)
+
const tx = invariant.removePositionTx(positionIndex, INVARIANT_REMOVE_POSITION_OPTIONS)
yield put(
@@ -889,7 +803,7 @@ export function* handleClosePosition(action: PayloadAction) {
})
)
- yield* put(actions.getPositionsList())
+ yield* put(actions.removePosition(positionIndex))
onSuccess()
yield* call(fetchBalances, [addressTokenX, addressTokenY])
@@ -938,18 +852,23 @@ export function* handleClosePositionWithAZERO(action: PayloadAction = {
+ type: actions.getPositionsListPage.type,
+ payload: {
+ index: Math.floor(Number(allPositions.length) / POSITIONS_PER_QUERY),
+ refresh: false
+ }
+ }
+ const fetchTask = yield* fork(handleGetPositionsListPage, getPositionsListPagePayload)
+ yield* join(fetchTask)
+
const txs = []
const removePositionTx = invariant.removePositionTx(positionIndex)
@@ -1000,7 +919,7 @@ export function* handleClosePositionWithAZERO(action: PayloadAction isLoaded)
+ .map(([index]) => Number(index)),
+ BigInt(POSITIONS_PER_QUERY)
+ )
+
+ const allList = [...list]
+ for (const { index, entries } of pages) {
+ for (let i = 0; i < entries.length; i++) {
+ allList[i + index * Number(POSITIONS_PER_QUERY)] = entries[i][0]
+ }
+ }
+
+ yield* put(actions.setPositionsList(allList))
+ yield* put(
+ actions.setPositionsListLoadedStatus({
+ indexes: pages.map(({ index }: { index: number }) => index),
+ isLoaded: true
+ })
+ )
}
-export function* getPositionsListHandler(): Generator {
- yield* takeLatest(actions.getPositionsList, handleGetPositionsList)
+export function* handleGetPositionsListPage(
+ action: PayloadAction<{ index: number; refresh: boolean }>
+) {
+ const { index, refresh } = action.payload
+
+ const walletAddress = yield* select(address)
+ const { length, list, loadedPages } = yield* select(positionsList)
+
+ const invariant = yield* getInvariant()
+
+ let entries: [Position, Pool][] = []
+ let positionsLength = 0n
+
+ if (refresh) {
+ yield* put(
+ actions.setPositionsListLoadedStatus({
+ indexes: Object.keys(loadedPages)
+ .map(key => Number(key))
+ .filter(keyIndex => keyIndex !== index),
+ isLoaded: false
+ })
+ )
+ }
+
+ if (!length || refresh) {
+ console.log('call', index)
+ const result = yield* call(
+ [invariant, invariant.getPositions],
+ walletAddress,
+ BigInt(POSITIONS_PER_QUERY),
+ BigInt(index * POSITIONS_PER_QUERY)
+ )
+ entries = result[0]
+ positionsLength = result[1]
+
+ const poolsWithPoolKeys = entries.map(entry => ({
+ poolKey: entry[0].poolKey,
+ ...entry[1]
+ }))
+
+ yield* put(
+ poolsActions.addPoolsForList({ data: poolsWithPoolKeys, listType: ListType.POSITIONS })
+ )
+ yield* call(fetchTokens, poolsWithPoolKeys)
+
+ yield* put(actions.setPositionsListLength(positionsLength))
+ }
+
+ const allList = length ? [...list] : Array(Number(positionsLength)).fill(EMPTY_POSITION)
+
+ const isPageLoaded = loadedPages[index]
+
+ if (!isPageLoaded || refresh) {
+ if (length && !refresh) {
+ console.log('call', index)
+ const result = yield* call(
+ [invariant, invariant.getPositions],
+ walletAddress,
+ BigInt(POSITIONS_PER_QUERY),
+ BigInt(index * POSITIONS_PER_QUERY)
+ )
+ entries = result[0]
+ positionsLength = result[1]
+
+ const poolsWithPoolKeys = entries.map(entry => ({
+ poolKey: entry[0].poolKey,
+ ...entry[1]
+ }))
+
+ yield* put(
+ poolsActions.addPoolsForList({ data: poolsWithPoolKeys, listType: ListType.POSITIONS })
+ )
+ yield* call(fetchTokens, poolsWithPoolKeys)
+ }
+
+ for (let i = 0; i < entries.length; i++) {
+ allList[i + index * POSITIONS_PER_QUERY] = entries[i][0]
+ }
+ }
+
+ yield* put(actions.setPositionsList(allList))
+ yield* put(actions.setPositionsListLoadedStatus({ indexes: [index], isLoaded: true }))
+}
+
+export function* initPositionHandler(): Generator {
+ yield* takeEvery(actions.initPosition, handleInitPosition)
}
export function* getCurrentPositionTicksHandler(): Generator {
@@ -1060,16 +1091,25 @@ export function* closePositionHandler(): Generator {
yield* takeEvery(actions.closePosition, handleClosePosition)
}
+export function* getPositionsListPage(): Generator {
+ yield* takeLatest(actions.getPositionsListPage, handleGetPositionsListPage)
+}
+
+export function* getRemainingPositions(): Generator {
+ yield* takeLatest(actions.getRemainingPositions, handleGetRemainingPositions)
+}
+
export function* positionsSaga(): Generator {
yield all(
[
initPositionHandler,
- getPositionsListHandler,
getCurrentPositionTicksHandler,
getCurrentPlotTicksHandler,
claimFeeHandler,
getSinglePositionHandler,
- closePositionHandler
+ closePositionHandler,
+ getPositionsListPage,
+ getRemainingPositions
].map(spawn)
)
}
diff --git a/src/store/sagas/swap.ts b/src/store/sagas/swap.ts
index 959cc354..e861985a 100644
--- a/src/store/sagas/swap.ts
+++ b/src/store/sagas/swap.ts
@@ -30,23 +30,20 @@ import {
isErrorMessage,
poolKeyToString,
printBigint
-} from '@store/consts/utils'
+} from '@utils/utils'
import { actions as poolActions } from '@store/reducers/pools'
import { actions as snackbarsActions } from '@store/reducers/snackbars'
import { Simulate, Swap, actions } from '@store/reducers/swap'
-import { invariantAddress, networkType } from '@store/selectors/connection'
+import { invariantAddress } from '@store/selectors/connection'
import { poolTicks, pools, tickMaps, tokens } from '@store/selectors/pools'
import { simulateResult } from '@store/selectors/swap'
import { address, balance } from '@store/selectors/wallet'
-import invariantSingleton from '@store/services/invariantSingleton'
-import psp22Singleton from '@store/services/psp22Singleton'
-import wrappedAZEROSingleton from '@store/services/wrappedAZEROSingleton'
import { getAlephZeroWallet } from '@utils/web3/wallet'
import { closeSnackbar } from 'notistack'
import { all, call, put, select, spawn, takeEvery } from 'typed-redux-saga'
-import { getConnection } from './connection'
import { fetchBalances } from './wallet'
import { SubmittableExtrinsic } from '@polkadot/api/promise/types'
+import { getApi, getInvariant, getPSP22, getWrappedAZERO } from './connection'
export function* handleSwap(action: PayloadAction>): Generator {
const {
@@ -83,8 +80,7 @@ export function* handleSwap(action: PayloadAction>): Generato
})
)
- const api = yield* getConnection()
- const network = yield* select(networkType)
+ const api = yield* getApi()
const walletAddress = yield* select(address)
const adapter = yield* call(getAlephZeroWallet)
const invAddress = yield* select(invariantAddress)
@@ -95,13 +91,8 @@ export function* handleSwap(action: PayloadAction>): Generato
const txs = []
- const psp22 = yield* call([psp22Singleton, psp22Singleton.loadInstance], api, network)
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const psp22 = yield* getPSP22()
+ const invariant = yield* getInvariant()
const sqrtPriceLimit = calculateSqrtPriceAfterSlippage(estimatedPriceAfterSwap, slippage, !xToY)
const calculatedAmountIn = slippage
? calculateAmountInWithSlippage(amountOut, sqrtPriceLimit, xToY, poolKey.feeTier.fee)
@@ -252,8 +243,7 @@ export function* handleSwapWithAZERO(action: PayloadAction>):
})
)
- const api = yield* getConnection()
- const network = yield* select(networkType)
+ const api = yield* getApi()
const walletAddress = yield* select(address)
const adapter = yield* call(getAlephZeroWallet)
const swapSimulateResult = yield* select(simulateResult)
@@ -265,18 +255,9 @@ export function* handleSwapWithAZERO(action: PayloadAction>):
const txs = []
- const wazero = yield* call(
- [wrappedAZEROSingleton, wrappedAZEROSingleton.loadInstance],
- api,
- network
- )
- const psp22 = yield* call([psp22Singleton, psp22Singleton.loadInstance], api, network)
- const invariant = yield* call(
- [invariantSingleton, invariantSingleton.loadInstance],
- api,
- network,
- invAddress
- )
+ const wazero = yield* getWrappedAZERO()
+ const psp22 = yield* getPSP22()
+ const invariant = yield* getInvariant()
const sqrtPriceLimit = calculateSqrtPriceAfterSlippage(estimatedPriceAfterSwap, slippage, !xToY)
const calculatedAmountIn = slippage
? calculateAmountInWithSlippage(amountOut, sqrtPriceLimit, xToY, poolKey.feeTier.fee)
@@ -454,6 +435,7 @@ export function* handleGetSimulateResult(action: PayloadAction) {
amountOut: 0n,
priceImpact: 0,
targetSqrtPrice: 0n,
+ fee: 0n,
errors: [SwapError.AmountIsZero]
})
)
@@ -472,6 +454,7 @@ export function* handleGetSimulateResult(action: PayloadAction) {
amountOut: 0n,
priceImpact: 0,
targetSqrtPrice: 0n,
+ fee: 0n,
errors: [SwapError.NoRouteFound]
})
)
@@ -483,6 +466,7 @@ export function* handleGetSimulateResult(action: PayloadAction) {
let insufficientLiquidityAmountOut = byAmountIn ? 0n : U128MAX
let priceImpact = 0
let targetSqrtPrice = 0n
+ let fee = 0n
const errors = []
for (const pool of filteredPools) {
@@ -507,9 +491,9 @@ export function* handleGetSimulateResult(action: PayloadAction) {
: result.amountIn < insufficientLiquidityAmountOut
) {
insufficientLiquidityAmountOut = byAmountIn ? result.amountOut : result.amountIn
+ fee = pool.poolKey.feeTier.fee
+ errors.push(SwapError.InsufficientLiquidity)
}
-
- errors.push(SwapError.InsufficientLiquidity)
continue
}
@@ -548,6 +532,7 @@ export function* handleGetSimulateResult(action: PayloadAction) {
amountOut: amountOut ? amountOut : insufficientLiquidityAmountOut,
priceImpact,
targetSqrtPrice,
+ fee,
errors
})
)
diff --git a/src/store/sagas/wallet.ts b/src/store/sagas/wallet.ts
index 4122d958..68c12994 100644
--- a/src/store/sagas/wallet.ts
+++ b/src/store/sagas/wallet.ts
@@ -1,15 +1,13 @@
-import { Network, sendTx } from '@invariant-labs/a0-sdk'
+import { sendTx } from '@invariant-labs/a0-sdk'
import { NightlyConnectAdapter } from '@nightlylabs/wallet-selector-polkadot'
import { PayloadAction } from '@reduxjs/toolkit'
import { FaucetTokenList, TokenAirdropAmount } from '@store/consts/static'
-import { createLoaderKey, getTokenBalances } from '@store/consts/utils'
+import { createLoaderKey, getTokenBalances } from '@utils/utils'
import { actions as positionsActions } from '@store/reducers/positions'
import { actions as snackbarsActions } from '@store/reducers/snackbars'
import { Status, actions, actions as walletActions } from '@store/reducers/wallet'
-import { networkType } from '@store/selectors/connection'
import { tokens } from '@store/selectors/pools'
import { address, status } from '@store/selectors/wallet'
-import psp22Singleton from '@store/services/psp22Singleton'
import { disconnectWallet, getAlephZeroWallet } from '@utils/web3/wallet'
import { closeSnackbar } from 'notistack'
import {
@@ -22,8 +20,9 @@ import {
takeLatest,
takeLeading
} from 'typed-redux-saga'
-import { getConnection } from './connection'
import { Signer } from '@polkadot/api/types'
+import { positionsList } from '@store/selectors/positions'
+import { getApi, getPSP22 } from './connection'
export function* getWallet(): SagaGenerator {
const wallet = yield* call(getAlephZeroWallet)
@@ -43,7 +42,7 @@ type FrameSystemAccountInfo = {
sufficients: number
}
export function* getBalance(walletAddress: string): SagaGenerator {
- const connection = yield* call(getConnection)
+ const connection = yield* getApi()
const accountInfoResponse = yield* call(
[connection.query.system.account, connection.query.system.account],
walletAddress
@@ -80,14 +79,10 @@ export function* handleAirdrop(): Generator {
})
)
- const connection = yield* getConnection()
+ const connection = yield* getApi()
const adapter = yield* call(getAlephZeroWallet)
- const psp22 = yield* call(
- [psp22Singleton, psp22Singleton.loadInstance],
- connection,
- Network.Testnet
- )
+ const psp22 = yield* getPSP22()
const txs = []
@@ -184,10 +179,20 @@ export function* handleConnect(): Generator {
export function* handleDisconnect(): Generator {
try {
+ const { loadedPages } = yield* select(positionsList)
+
yield* call(disconnectWallet)
yield* put(actions.resetState())
yield* put(positionsActions.setPositionsList([]))
+ yield* put(positionsActions.setPositionsListLength(0n))
+ yield* put(
+ positionsActions.setPositionsListLoadedStatus({
+ indexes: Object.keys(loadedPages).map(key => Number(key)),
+ isLoaded: false
+ })
+ )
+
yield* put(
positionsActions.setCurrentPositionTicks({
lowerTick: undefined,
@@ -201,15 +206,14 @@ export function* handleDisconnect(): Generator {
export function* fetchBalances(tokens: string[]): Generator {
const walletAddress = yield* select(address)
- const api = yield* getConnection()
- const network = yield* select(networkType)
+ const psp22 = yield* getPSP22()
yield* put(walletActions.setIsBalanceLoading(true))
const balance = yield* call(getBalance, walletAddress)
yield* put(walletActions.setBalance(BigInt(balance)))
- const tokenBalances = yield* call(getTokenBalances, tokens, api, network, walletAddress)
+ const tokenBalances = yield* call(getTokenBalances, tokens, psp22, walletAddress)
yield* put(
walletActions.addTokenBalances(
tokenBalances.map(([address, balance]) => {
diff --git a/src/store/selectors/connection.ts b/src/store/selectors/connection.ts
index e9152a51..3f2d80da 100644
--- a/src/store/selectors/connection.ts
+++ b/src/store/selectors/connection.ts
@@ -3,17 +3,29 @@ import { AnyProps, keySelectors } from './helpers'
const store = (s: AnyProps) => s[connectionSliceName] as IAlephZeroConnectionStore
-export const { networkType, status, blockNumber, rpcAddress, invariantAddress } = keySelectors(
- store,
- ['networkType', 'status', 'blockNumber', 'rpcAddress', 'invariantAddress']
-)
+export const {
+ networkType,
+ status,
+ blockNumber,
+ rpcAddress,
+ invariantAddress,
+ wrappedAZEROAddress
+} = keySelectors(store, [
+ 'networkType',
+ 'status',
+ 'blockNumber',
+ 'rpcAddress',
+ 'invariantAddress',
+ 'wrappedAZEROAddress'
+])
export const alephZeroConnectionSelectors = {
networkType,
status,
blockNumber,
rpcAddress,
- invariantAddress
+ invariantAddress,
+ wrappedAZEROAddress
}
export default alephZeroConnectionSelectors
diff --git a/src/store/selectors/positions.ts b/src/store/selectors/positions.ts
index 7122633a..0e392a06 100644
--- a/src/store/selectors/positions.ts
+++ b/src/store/selectors/positions.ts
@@ -1,5 +1,5 @@
import { Position } from '@invariant-labs/a0-sdk'
-import { poolKeyToString } from '@store/consts/utils'
+import { poolKeyToString } from '@utils/utils'
import { PoolWithPoolKey } from '@store/reducers/pools'
import { createSelector } from 'reselect'
import { IPositionsStore, positionsSliceName } from '../reducers/positions'
@@ -54,22 +54,30 @@ export const positionsWithPoolsData = createSelector(
}
})
- return list.map((position, index) => {
+ const result = []
+ for (let index = 0; index < list.length; index++) {
+ const position = list[index]
const tokenX = tokens.find(token => token.assetAddress === position.poolKey.tokenX)
const tokenY = tokens.find(token => token.assetAddress === position.poolKey.tokenY)
- if (!tokenX || !tokenY) {
- throw new Error(`Token not found for position: ${position}`)
+ if (!tokenX) {
+ console.log(`Token ${position.poolKey.tokenX} not found for position`)
+ continue
+ } else if (!tokenY) {
+ console.log(`Token ${position.poolKey.tokenY} not found for position`)
+ continue
}
- return {
+ result.push({
...position,
poolData: poolsByKey[poolKeyToString(position.poolKey)],
tokenX,
tokenY,
positionIndex: index
- }
- })
+ })
+ }
+
+ return result
}
)
diff --git a/src/store/services/apiSingleton.ts b/src/store/services/apiSingleton.ts
index 6785c8aa..e7c67ebf 100644
--- a/src/store/services/apiSingleton.ts
+++ b/src/store/services/apiSingleton.ts
@@ -1,33 +1,24 @@
import { Network, initPolkadotApi } from '@invariant-labs/a0-sdk'
import { ApiPromise } from '@polkadot/api'
-class SingletonAPI {
- private static instance: SingletonAPI
- private api: ApiPromise | null = null
- private currentRpc: string | null = null
- private currentNetwork: Network | null = null
+class SingletonApi {
+ static api: ApiPromise | null = null
+ static rpc: string | null = null
+ static network: Network | null = null
- private constructor() {}
-
- public static getInstance(): SingletonAPI {
- if (!SingletonAPI.instance) {
- SingletonAPI.instance = new SingletonAPI()
- }
- return SingletonAPI.instance
+ static getInstance(): ApiPromise | null {
+ return this.api
}
- public async loadInstance(network: Network, rpc: string): Promise {
- if (!this.api || this.currentRpc !== rpc || this.currentNetwork !== network) {
- const newApi = await initPolkadotApi(network, rpc)
- this.currentRpc = rpc
- this.currentNetwork = network
- this.api = newApi
-
- return newApi
+ static async loadInstance(network: Network, rpc: string): Promise {
+ if (!this.api || network !== this.network || rpc !== this.rpc) {
+ this.api = await initPolkadotApi(network, rpc)
+ this.network = network
+ this.rpc = rpc
}
return this.api
}
}
-export default SingletonAPI.getInstance()
+export default SingletonApi
diff --git a/src/store/services/invariantSingleton.ts b/src/store/services/invariantSingleton.ts
index 19237a60..ede065f5 100644
--- a/src/store/services/invariantSingleton.ts
+++ b/src/store/services/invariantSingleton.ts
@@ -3,37 +3,32 @@ import { ApiPromise } from '@polkadot/api'
import { DEFAULT_INVARIANT_OPTIONS } from '@store/consts/static'
class SingletonInvariant {
- private static instance: SingletonInvariant
- private invariant: Invariant | null = null
- private currentApi: ApiPromise | null = null
- private currentNetwork: Network | null = null
+ static invariant: Invariant | null = null
+ static api: ApiPromise | null = null
+ static network: Network | null = null
- private constructor() {}
-
- public static getInstance(): SingletonInvariant {
- if (!SingletonInvariant.instance) {
- SingletonInvariant.instance = new SingletonInvariant()
- }
- return SingletonInvariant.instance
+ static getInstance(): Invariant | null {
+ return this.invariant
}
- public async loadInstance(
+ static async loadInstance(
api: ApiPromise,
network: Network,
address: string
): Promise {
if (
!this.invariant ||
- this.currentApi !== api ||
- this.currentNetwork !== network ||
+ api !== this.api ||
+ network !== this.network ||
address !== this.invariant.contract.address.toString()
) {
this.invariant = await Invariant.load(api, network, address, DEFAULT_INVARIANT_OPTIONS)
- this.currentApi = api
- this.currentNetwork = network
+ this.api = api
+ this.network = network
}
+
return this.invariant
}
}
-export default SingletonInvariant.getInstance()
+export default SingletonInvariant
diff --git a/src/store/services/psp22Singleton.ts b/src/store/services/psp22Singleton.ts
index 986c0701..2520ca3c 100644
--- a/src/store/services/psp22Singleton.ts
+++ b/src/store/services/psp22Singleton.ts
@@ -3,28 +3,23 @@ import { ApiPromise } from '@polkadot/api'
import { DEFAULT_PSP22_OPTIONS } from '@store/consts/static'
class SingletonPSP22 {
- private static instance: SingletonPSP22
- private psp22: PSP22 | null = null
- private currentApi: ApiPromise | null = null
- private currentNetwork: Network | null = null
+ static psp22: PSP22 | null = null
+ static api: ApiPromise | null = null
+ static network: Network | null = null
- private constructor() {}
-
- public static getInstance(): SingletonPSP22 {
- if (!SingletonPSP22.instance) {
- SingletonPSP22.instance = new SingletonPSP22()
- }
- return SingletonPSP22.instance
+ static getInstance(): PSP22 | null {
+ return this.psp22
}
- public async loadInstance(api: ApiPromise, network: Network): Promise {
- if (!this.psp22 || this.currentApi !== api || this.currentNetwork !== network) {
+ static async loadInstance(api: ApiPromise, network: Network): Promise {
+ if (!this.psp22 || api !== this.api || network !== this.network) {
this.psp22 = await PSP22.load(api, network, DEFAULT_PSP22_OPTIONS)
- this.currentApi = api
- this.currentNetwork = network
+ this.api = api
+ this.network = network
}
+
return this.psp22
}
}
-export default SingletonPSP22.getInstance()
+export default SingletonPSP22
diff --git a/src/store/services/wrappedAZEROSingleton.ts b/src/store/services/wrappedAZEROSingleton.ts
index f2139296..9b9299c7 100644
--- a/src/store/services/wrappedAZEROSingleton.ts
+++ b/src/store/services/wrappedAZEROSingleton.ts
@@ -3,33 +3,37 @@ import { ApiPromise } from '@polkadot/api'
import { DEFAULT_WAZERO_OPTIONS } from '@store/consts/static'
class SingletonWrappedAZERO {
- private static instance: SingletonWrappedAZERO
- private wrappedAZERO: WrappedAZERO | null = null
- private currentApi: ApiPromise | null = null
- private currentNetwork: Network | null = null
+ static wrappedAZERO: WrappedAZERO | null = null
+ static api: ApiPromise | null = null
+ static network: Network | null = null
- private constructor() {}
-
- public static getInstance(): SingletonWrappedAZERO {
- if (!SingletonWrappedAZERO.instance) {
- SingletonWrappedAZERO.instance = new SingletonWrappedAZERO()
- }
- return SingletonWrappedAZERO.instance
+ static getInstance(): WrappedAZERO | null {
+ return this.wrappedAZERO
}
- public async loadInstance(api: ApiPromise, network: Network): Promise {
- if (!this.wrappedAZERO || this.currentApi !== api || this.currentNetwork !== network) {
+ static async loadInstance(
+ api: ApiPromise,
+ network: Network,
+ address: string
+ ): Promise {
+ if (
+ !this.wrappedAZERO ||
+ api !== this.api ||
+ network !== this.network ||
+ address !== this.wrappedAZERO.contract.address.toString()
+ ) {
this.wrappedAZERO = await WrappedAZERO.load(
api,
network,
TESTNET_WAZERO_ADDRESS,
DEFAULT_WAZERO_OPTIONS
)
- this.currentApi = api
- this.currentNetwork = network
+ this.api = api
+ this.network = network
}
+
return this.wrappedAZERO
}
}
-export default SingletonWrappedAZERO.getInstance()
+export default SingletonWrappedAZERO
diff --git a/src/store/consts/utils.ts b/src/utils/utils.ts
similarity index 85%
rename from src/store/consts/utils.ts
rename to src/utils/utils.ts
index c9c777ed..bb7ac821 100644
--- a/src/store/consts/utils.ts
+++ b/src/utils/utils.ts
@@ -2,6 +2,7 @@ import {
Invariant,
LiquidityTick,
Network,
+ PSP22,
PoolKey,
Tick,
Tickmap,
@@ -17,18 +18,19 @@ import {
PERCENTAGE_DENOMINATOR,
PERCENTAGE_SCALE,
PRICE_SCALE,
+ SQRT_PRICE_SCALE,
TESTNET_BTC_ADDRESS,
TESTNET_ETH_ADDRESS,
TESTNET_USDC_ADDRESS,
TESTNET_WAZERO_ADDRESS
} from '@invariant-labs/a0-sdk/target/consts'
-import { calculateLiquidityBreakpoints } from '@invariant-labs/a0-sdk/target/utils'
-import { ApiPromise, Keyring } from '@polkadot/api'
+import {
+ calculateLiquidityBreakpoints,
+ priceToSqrtPrice
+} from '@invariant-labs/a0-sdk/target/utils'
+import { Keyring } from '@polkadot/api'
import { PoolWithPoolKey } from '@store/reducers/pools'
import { PlotTickData } from '@store/reducers/positions'
-import apiSingleton from '@store/services/apiSingleton'
-import invariantSingleton from '@store/services/invariantSingleton'
-import psp22Singleton from '@store/services/psp22Singleton'
import axios from 'axios'
import {
BTC,
@@ -39,6 +41,8 @@ import {
FAUCET_DEPLOYER_MNEMONIC,
FormatConfig,
LIQUIDITY_PLOT_DECIMAL,
+ POSITIONS_PER_PAGE,
+ POSITIONS_PER_QUERY,
PositionTokenBlock,
STABLECOIN_ADDRESSES,
USDC,
@@ -48,7 +52,7 @@ import {
reversedAddressTickerMap,
subNumbers,
tokensPrices
-} from './static'
+} from '@store/consts/static'
import { sleep } from '@store/sagas/wallet'
import {
BestTier,
@@ -57,7 +61,8 @@ import {
PrefixConfig,
Token,
TokenPriceData
-} from './types'
+} from '@store/consts/types'
+import icons from '@static/icons'
export const createLoaderKey = () => (new Date().getMilliseconds() + Math.random()).toString()
@@ -180,7 +185,7 @@ export const toMaxNumericPlaces = (num: number, places: number): string => {
return num.toFixed(places + Math.abs(log) - 1)
}
-export const calcPrice = (
+export const calcPriceByTickIndex = (
amountTickIndex: bigint,
isXtoY: boolean,
xDecimal: bigint,
@@ -195,6 +200,16 @@ export const calcPrice = (
return price === 0 ? Number.MAX_SAFE_INTEGER : 1 / price
}
+export const calcPriceBySqrtPrice = (
+ sqrtPrice: bigint,
+ isXtoY: boolean,
+ xDecimal: bigint,
+ yDecimal: bigint
+): number => {
+ const price = calcYPerXPriceBySqrtPrice(sqrtPrice, xDecimal, yDecimal) ** (isXtoY ? 1 : -1)
+
+ return price
+}
export const createPlaceholderLiquidityPlot = (
isXtoY: boolean,
yValueToFill: number,
@@ -207,7 +222,7 @@ export const createPlaceholderLiquidityPlot = (
const min = getMinTick(tickSpacing)
const max = getMaxTick(tickSpacing)
- const minPrice = calcPrice(min, isXtoY, tokenXDecimal, tokenYDecimal)
+ const minPrice = calcPriceByTickIndex(min, isXtoY, tokenXDecimal, tokenYDecimal)
ticksData.push({
x: minPrice,
@@ -215,7 +230,7 @@ export const createPlaceholderLiquidityPlot = (
index: min
})
- const maxPrice = calcPrice(max, isXtoY, tokenXDecimal, tokenYDecimal)
+ const maxPrice = calcPriceByTickIndex(max, isXtoY, tokenXDecimal, tokenYDecimal)
ticksData.push({
x: maxPrice,
@@ -345,12 +360,9 @@ export const parseFeeToPathFee = (fee: bigint): string => {
export const getTokenDataByAddresses = async (
tokens: string[],
- api: ApiPromise,
- network: Network,
+ psp22: PSP22,
address: string
): Promise> => {
- const psp22 = await psp22Singleton.loadInstance(api, network)
-
const promises = tokens.flatMap(token => {
return [
psp22.tokenSymbol(token),
@@ -370,7 +382,7 @@ export const getTokenDataByAddresses = async (
name: results[baseIndex + 1] ? (results[baseIndex + 1] as string) : '',
decimals: results[baseIndex + 2] as bigint,
balance: results[baseIndex + 3] as bigint,
- logoURI: '/unknownToken.svg',
+ logoURI: icons.unknownToken,
isUnknown: true
}
})
@@ -379,12 +391,9 @@ export const getTokenDataByAddresses = async (
export const getTokenBalances = async (
tokens: string[],
- api: ApiPromise,
- network: Network,
+ psp22: PSP22,
address: string
): Promise<[string, bigint][]> => {
- const psp22 = await psp22Singleton.loadInstance(api, network)
-
const promises: Promise[] = []
tokens.map(token => {
promises.push(psp22.balanceOf(address, token))
@@ -399,13 +408,9 @@ export const getTokenBalances = async (
}
export const getPoolsByPoolKeys = async (
- invariantAddress: string,
- poolKeys: PoolKey[],
- api: ApiPromise,
- network: Network
+ invariant: Invariant,
+ poolKeys: PoolKey[]
): Promise => {
- const invariant = await invariantSingleton.loadInstance(api, network, invariantAddress)
-
const promises = poolKeys.map(({ tokenX, tokenY, feeTier }) =>
invariant.getPool(tokenX, tokenY, feeTier)
)
@@ -504,6 +509,52 @@ export const nearestSpacingMultiplicity = (centerTick: number, spacing: number)
)
}
+export const calculateSqrtPriceFromBalance = (
+ price: number,
+ spacing: bigint,
+ isXtoY: boolean,
+ xDecimal: bigint,
+ yDecimal: bigint
+) => {
+ const minTick = getMinTick(spacing)
+ const maxTick = getMaxTick(spacing)
+
+ const basePrice = Math.min(
+ Math.max(
+ price,
+ Number(calcPriceByTickIndex(isXtoY ? minTick : maxTick, isXtoY, xDecimal, yDecimal))
+ ),
+ Number(calcPriceByTickIndex(isXtoY ? maxTick : minTick, isXtoY, xDecimal, yDecimal))
+ )
+
+ const primaryUnitsPrice = getPrimaryUnitsPrice(
+ basePrice,
+ isXtoY,
+ Number(xDecimal),
+ Number(yDecimal)
+ )
+
+ const parsedPrimaryUnits =
+ primaryUnitsPrice > 1 && Number.isInteger(primaryUnitsPrice)
+ ? primaryUnitsPrice.toString()
+ : primaryUnitsPrice.toFixed(24)
+
+ const bigintPrice = convertBalanceToBigint(parsedPrimaryUnits, SQRT_PRICE_SCALE)
+ const sqrtPrice = priceToSqrtPrice(bigintPrice)
+
+ const minSqrtPrice = calculateSqrtPrice(minTick)
+ const maxSqrtPrice = calculateSqrtPrice(maxTick)
+
+ let validatedSqrtPrice = sqrtPrice
+ if (sqrtPrice < minSqrtPrice) {
+ validatedSqrtPrice = minSqrtPrice
+ } else if (sqrtPrice > maxSqrtPrice) {
+ validatedSqrtPrice = maxSqrtPrice
+ }
+
+ return validatedSqrtPrice
+}
+
export const calculateTickFromBalance = (
price: number,
spacing: bigint,
@@ -516,7 +567,7 @@ export const calculateTickFromBalance = (
const basePrice = Math.max(
price,
- Number(calcPrice(isXtoY ? minTick : maxTick, isXtoY, xDecimal, yDecimal))
+ Number(calcPriceByTickIndex(isXtoY ? minTick : maxTick, isXtoY, xDecimal, yDecimal))
)
const primaryUnitsPrice = getPrimaryUnitsPrice(
basePrice,
@@ -727,7 +778,7 @@ export const createLiquidityPlot = (
const max = getMaxTick(tickSpacing)
if (!ticks.length || ticks[0].index > min) {
- const minPrice = calcPrice(min, isXtoY, tokenXDecimal, tokenYDecimal)
+ const minPrice = calcPriceByTickIndex(min, isXtoY, tokenXDecimal, tokenYDecimal)
ticksData.push({
x: minPrice,
@@ -738,14 +789,24 @@ export const createLiquidityPlot = (
ticks.forEach((tick, i) => {
if (i === 0 && tick.index - tickSpacing > min) {
- const price = calcPrice(tick.index - tickSpacing, isXtoY, tokenXDecimal, tokenYDecimal)
+ const price = calcPriceByTickIndex(
+ tick.index - tickSpacing,
+ isXtoY,
+ tokenXDecimal,
+ tokenYDecimal
+ )
ticksData.push({
x: price,
y: 0,
index: tick.index - tickSpacing
})
} else if (i > 0 && tick.index - tickSpacing > ticks[i - 1].index) {
- const price = calcPrice(tick.index - tickSpacing, isXtoY, tokenXDecimal, tokenYDecimal)
+ const price = calcPriceByTickIndex(
+ tick.index - tickSpacing,
+ isXtoY,
+ tokenXDecimal,
+ tokenYDecimal
+ )
ticksData.push({
x: price,
y: +printBigint(ticks[i - 1].liqudity, LIQUIDITY_PLOT_DECIMAL),
@@ -753,7 +814,7 @@ export const createLiquidityPlot = (
})
}
- const price = calcPrice(tick.index, isXtoY, tokenXDecimal, tokenYDecimal)
+ const price = calcPriceByTickIndex(tick.index, isXtoY, tokenXDecimal, tokenYDecimal)
ticksData.push({
x: price,
y: +printBigint(ticks[i].liqudity, LIQUIDITY_PLOT_DECIMAL),
@@ -762,7 +823,7 @@ export const createLiquidityPlot = (
})
const lastTick = ticks[ticks.length - 1].index
if (!ticks.length) {
- const maxPrice = calcPrice(max, isXtoY, tokenXDecimal, tokenYDecimal)
+ const maxPrice = calcPriceByTickIndex(max, isXtoY, tokenXDecimal, tokenYDecimal)
ticksData.push({
x: maxPrice,
@@ -771,7 +832,12 @@ export const createLiquidityPlot = (
})
} else if (lastTick < max) {
if (max - lastTick > tickSpacing) {
- const price = calcPrice(lastTick + tickSpacing, isXtoY, tokenXDecimal, tokenYDecimal)
+ const price = calcPriceByTickIndex(
+ lastTick + tickSpacing,
+ isXtoY,
+ tokenXDecimal,
+ tokenYDecimal
+ )
ticksData.push({
x: price,
y: 0,
@@ -779,7 +845,7 @@ export const createLiquidityPlot = (
})
}
- const maxPrice = calcPrice(max, isXtoY, tokenXDecimal, tokenYDecimal)
+ const maxPrice = calcPriceByTickIndex(max, isXtoY, tokenXDecimal, tokenYDecimal)
ticksData.push({
x: maxPrice,
@@ -831,8 +897,12 @@ export const formatNumber = (number: number | bigint | string): string => {
FormatConfig.DecimalsAfterDot
) +
'K'
+ } else if (afterDot && countLeadingZeros(afterDot) <= 3) {
+ const roundedNumber = numberAsNumber.toFixed(countLeadingZeros(afterDot) + 4).slice(0, -1)
+ formattedNumber = trimZeros(roundedNumber)
} else {
const leadingZeros = afterDot ? countLeadingZeros(afterDot) : 0
+
const parsedAfterDot =
String(parseInt(afterDot)).length > 3 ? String(parseInt(afterDot)).slice(0, 3) : afterDot
formattedNumber = trimZeros(
@@ -872,13 +942,10 @@ export const isErrorMessage = (message: string): boolean => {
export const getNewTokenOrThrow = async (
address: string,
- network: Network,
- rpc: string,
+ psp22: PSP22,
walletAddress: string
): Promise> => {
- const api = await apiSingleton.loadInstance(network, rpc)
-
- const tokenData = await getTokenDataByAddresses([address], api, network, walletAddress)
+ const tokenData = await getTokenDataByAddresses([address], psp22, walletAddress)
if (tokenData) {
return tokenData
@@ -1015,3 +1082,38 @@ export function testnetBestTiersCreator() {
return bestTiers
}
+
+export const positionListPageToQueryPage = (page: number): number => {
+ return Math.max(Math.ceil((page * POSITIONS_PER_PAGE) / POSITIONS_PER_QUERY) - 1, 0)
+}
+
+export const validConcentrationMidPriceTick = (
+ midPriceTick: bigint,
+ isXtoY: boolean,
+ tickSpacing: bigint
+) => {
+ const minTick = getMinTick(tickSpacing)
+ const maxTick = getMaxTick(tickSpacing)
+
+ const parsedTickSpacing = Number(tickSpacing)
+ const tickDelta = BigInt(calculateTickDelta(parsedTickSpacing, 2, 2))
+
+ const minTickLimit = minTick + (2n + tickDelta) * tickSpacing
+ const maxTickLimit = maxTick - (2n + tickDelta) * tickSpacing
+
+ if (isXtoY) {
+ if (midPriceTick < minTickLimit) {
+ return minTickLimit
+ } else if (midPriceTick > maxTickLimit) {
+ return maxTickLimit
+ }
+ } else {
+ if (midPriceTick > maxTickLimit) {
+ return maxTickLimit
+ } else if (midPriceTick < minTickLimit) {
+ return minTickLimit
+ }
+ }
+
+ return midPriceTick
+}