diff --git a/src/abacus-ts/calculators/accountActions.ts b/src/abacus-ts/calculators/accountActions.ts new file mode 100644 index 000000000..f0b44a54e --- /dev/null +++ b/src/abacus-ts/calculators/accountActions.ts @@ -0,0 +1,136 @@ +import { produce } from 'immer'; + +import { + IndexerAssetPositionResponseObject, + IndexerPositionSide, +} from '@/types/indexer/indexerApiGen'; + +import { MustBigNumber } from '@/lib/numbers'; + +import { SubaccountBatchedOperations, SubaccountOperations } from '../types/operationTypes'; +import { ChildSubaccountData, ParentSubaccountData } from '../types/rawTypes'; + +export function createUsdcDepositOperations({ + subaccountNumber, + depositAmount, +}: { + subaccountNumber: number; + depositAmount: string; +}): SubaccountBatchedOperations { + return { + operations: [ + SubaccountOperations.ModifyUsdcAssetPosition({ + subaccountNumber, + changes: { + size: depositAmount, + }, + }), + ], + }; +} + +export function createUsdcWithdrawalOperations({ + subaccountNumber, + withdrawAmount, +}: { + subaccountNumber: number; + withdrawAmount: string; +}): SubaccountBatchedOperations { + return { + operations: [ + SubaccountOperations.ModifyUsdcAssetPosition({ + subaccountNumber, + changes: { + size: MustBigNumber(withdrawAmount).negated().toString(), + }, + }), + ], + }; +} + +function modifyUsdcAssetPosition( + parentSubaccountData: ParentSubaccountData, + payload: { + subaccountNumber: number; + changes: Partial>; + } +): ParentSubaccountData { + const { subaccountNumber, changes } = payload; + if (!changes.size) return parentSubaccountData; + const sizeBN = MustBigNumber(changes.size); + + let childSubaccount: ChildSubaccountData | undefined = + parentSubaccountData.childSubaccounts[subaccountNumber]; + + if (childSubaccount != null) { + // Modify childSubaccount + if (childSubaccount.assetPositions.USDC != null) { + const size = MustBigNumber(childSubaccount.assetPositions.USDC.size).plus(sizeBN).toString(); + + const updatedChildSubaccount = produce(childSubaccount, (draftChildSubaccount) => { + if (draftChildSubaccount.assetPositions.USDC) { + draftChildSubaccount.assetPositions.USDC.size = size; + } + }); + + childSubaccount = updatedChildSubaccount; + } else { + if (sizeBN.gt(0)) { + const updatedChildSubaccount = produce(childSubaccount, (draftChildSubaccount) => { + draftChildSubaccount.assetPositions.USDC = { + assetId: '0', + symbol: 'USDC', + size: sizeBN.toString(), + side: IndexerPositionSide.LONG, + subaccountNumber, + }; + }); + + childSubaccount = updatedChildSubaccount; + } + } + } else { + // Upsert ChildSubaccountData into parentSubaccountData.childSubaccounts + childSubaccount = { + address: parentSubaccountData.address, + subaccountNumber, + openPerpetualPositions: {}, + assetPositions: { + USDC: { + assetId: '0', + symbol: 'USDC', + size: sizeBN.toString(), + side: IndexerPositionSide.LONG, + subaccountNumber, + }, + }, + } satisfies ChildSubaccountData; + } + + return produce(parentSubaccountData, (draftParentSubaccountData) => { + draftParentSubaccountData.childSubaccounts[subaccountNumber] = childSubaccount; + }); +} + +export function applyOperationsToSubaccount( + parentSubaccount: ParentSubaccountData, + batchedOperations: SubaccountBatchedOperations +): ParentSubaccountData { + let parentSubaccountData: ParentSubaccountData = parentSubaccount; + + batchedOperations.operations.forEach((op) => { + SubaccountOperations.match(op, { + AddPerpetualPosition: () => { + // TODO: Implement addPerpetualPosition + }, + ModifyPerpetualPosition: () => { + // TODO: Implement modifyPerpetualPosition + }, + ModifyUsdcAssetPosition: (args) => { + parentSubaccountData = modifyUsdcAssetPosition(parentSubaccountData, args); + }, + }); + }); + + return parentSubaccountData; +} diff --git a/src/abacus-ts/calculators/subaccount.ts b/src/abacus-ts/calculators/subaccount.ts index 3bf5949a7..bb9532427 100644 --- a/src/abacus-ts/calculators/subaccount.ts +++ b/src/abacus-ts/calculators/subaccount.ts @@ -3,7 +3,6 @@ import { mapValues, orderBy } from 'lodash'; import { NUM_PARENT_SUBACCOUNTS } from '@/constants/account'; import { - IndexerAssetPositionResponseObject, IndexerPerpetualPositionResponseObject, IndexerPerpetualPositionStatus, IndexerPositionSide, @@ -15,7 +14,6 @@ import { calc } from '@/lib/do'; import { BIG_NUMBERS, MaybeBigNumber, MustBigNumber, ToBigNumber } from '@/lib/numbers'; import { isPresent } from '@/lib/typeUtils'; -import { SubaccountBatchedOperations, SubaccountOperations } from '../types/operationTypes'; import { ChildSubaccountData, MarketsData, ParentSubaccountData } from '../types/rawTypes'; import { GroupedSubaccountSummary, @@ -290,137 +288,3 @@ function calculatePositionDerivedExtra( updatedUnrealizedPnlPercent, }; } - -export function createUsdcDepositOperations({ - subaccountNumber, - depositAmount, -}: { - subaccountNumber: number; - depositAmount: string; -}): SubaccountBatchedOperations { - return { - operations: [ - SubaccountOperations.ModifyUsdcAssetPosition({ - subaccountNumber, - changes: { - size: depositAmount, - }, - }), - ], - }; -} - -export function createUsdcWithdrawalOperations({ - subaccountNumber, - withdrawAmount, -}: { - subaccountNumber: number; - withdrawAmount: string; -}): SubaccountBatchedOperations { - return { - operations: [ - SubaccountOperations.ModifyUsdcAssetPosition({ - subaccountNumber, - changes: { - size: MustBigNumber(withdrawAmount).negated().toString(), - }, - }), - ], - }; -} - -function modifyUsdcAssetPosition( - parentSubaccountData: ParentSubaccountData, - payload: { - subaccountNumber: number; - changes: Partial>; - } -): void { - const { subaccountNumber, changes } = payload; - - if (!changes.size) return; - const sizeBN = MustBigNumber(changes.size); - - let childSubaccount: ChildSubaccountData | undefined = - parentSubaccountData.childSubaccounts[subaccountNumber]; - - if (childSubaccount != null) { - // Modify childSubaccount - if (childSubaccount.assetPositions.USDC != null) { - const size = MustBigNumber(childSubaccount.assetPositions.USDC.size).plus(sizeBN).toString(); - const assetPositions = { - ...childSubaccount.assetPositions, - USDC: { - ...childSubaccount.assetPositions.USDC, - size, - }, - }; - - childSubaccount = { - ...childSubaccount, - assetPositions, - }; - } else { - if (sizeBN.gt(0)) { - childSubaccount.assetPositions.USDC = { - assetId: '0', - symbol: 'USDC', - size: sizeBN.toString(), - side: IndexerPositionSide.LONG, - subaccountNumber, - }; - } - } - } else { - // Upsert ChildSubaccountData into parentSubaccountData.childSubaccounts - childSubaccount = { - address: parentSubaccountData.address, - subaccountNumber, - openPerpetualPositions: {}, - assetPositions: { - USDC: { - assetId: '0', - symbol: 'USDC', - size: sizeBN.toString(), - side: IndexerPositionSide.LONG, - subaccountNumber, - }, - }, - }; - } - - parentSubaccountData.childSubaccounts = { - ...parentSubaccountData.childSubaccounts, - [subaccountNumber]: childSubaccount, - }; -} - -export function applyOperationsToSubaccount( - parentSubaccount: ParentSubaccountData, - batchedOperations: SubaccountBatchedOperations -): ParentSubaccountData { - const parentSubaccountData: ParentSubaccountData = { ...parentSubaccount }; - - batchedOperations.operations.forEach((op) => { - const { payload, operation } = op; - - switch (operation) { - case 'AddPerpetualPosition': { - // TODO: Implement addPerpetualPosition - break; - } - case 'ModifyPerpetualPosition': { - // TODO: Implement modifyPerpetualPosition - break; - } - case 'ModifyUsdcAssetPosition': { - modifyUsdcAssetPosition(parentSubaccountData, payload); - break; - } - default: - throw new Error(`Error processing invalid operation type`); - } - }); - - return parentSubaccountData; -} diff --git a/src/abacus-ts/ontology.ts b/src/abacus-ts/ontology.ts index 5986e194e..1e3933d38 100644 --- a/src/abacus-ts/ontology.ts +++ b/src/abacus-ts/ontology.ts @@ -17,8 +17,6 @@ import { selectParentSubaccountSummaryLoading, } from './selectors/account'; import { - createSelectParentSubaccountPositionsDeposit, - createSelectParentSubaccountPositionsWithdrawal, createSelectParentSubaccountSummaryDeposit, createSelectParentSubaccountSummaryWithdrawal, } from './selectors/accountActions'; @@ -103,14 +101,12 @@ export const BonsaiHelpers = { fills: getCurrentMarketAccountFills, }, }, - account: { + forms: { deposit: { parentSubaccountSummary: createSelectParentSubaccountSummaryDeposit, - parentSubaccountPositions: createSelectParentSubaccountPositionsDeposit, }, withdraw: { parentSubaccountSummary: createSelectParentSubaccountSummaryWithdrawal, - parentSubaccountPositions: createSelectParentSubaccountPositionsWithdrawal, }, }, } as const satisfies NestedSelectors; diff --git a/src/abacus-ts/selectors/accountActions.ts b/src/abacus-ts/selectors/accountActions.ts index 3c6b267fa..5351cbd6b 100644 --- a/src/abacus-ts/selectors/accountActions.ts +++ b/src/abacus-ts/selectors/accountActions.ts @@ -1,14 +1,11 @@ -import { shallowEqual } from 'react-redux'; - import { createAppSelector } from '@/state/appTypes'; import { applyOperationsToSubaccount, - calculateParentSubaccountPositions, - calculateParentSubaccountSummary, createUsdcDepositOperations, createUsdcWithdrawalOperations, -} from '../calculators/subaccount'; +} from '../calculators/accountActions'; +import { calculateParentSubaccountSummary } from '../calculators/subaccount'; import { selectRelevantMarketsData } from './account'; import { selectRawParentSubaccountData } from './base'; @@ -34,39 +31,6 @@ export const createSelectParentSubaccountSummaryDeposit = () => const modifiedParentSubaccount = applyOperationsToSubaccount(parentSubaccount, operations); const result = calculateParentSubaccountSummary(modifiedParentSubaccount, markets); return result; - }, - { - // use shallow equal for result so that we only update when these specific keys differ - memoizeOptions: { resultEqualityCheck: shallowEqual }, - } - ); - -export const createSelectParentSubaccountPositionsDeposit = () => - createAppSelector( - [ - selectRawParentSubaccountData, - selectRelevantMarketsData, - ( - _s, - input: { - subaccountNumber: number; - depositAmount: string; - } - ) => input, - ], - (parentSubaccount, markets, depositInputs) => { - if (parentSubaccount == null || markets == null) { - return undefined; - } - - const operations = createUsdcDepositOperations(depositInputs); - const modifiedParentSubaccount = applyOperationsToSubaccount(parentSubaccount, operations); - const result = calculateParentSubaccountPositions(modifiedParentSubaccount, markets); - return result; - }, - { - // use shallow equal for result so that we only update when these specific keys differ - memoizeOptions: { resultEqualityCheck: shallowEqual }, } ); @@ -92,38 +56,5 @@ export const createSelectParentSubaccountSummaryWithdrawal = () => const modifiedParentSubaccount = applyOperationsToSubaccount(parentSubaccount, operations); const result = calculateParentSubaccountSummary(modifiedParentSubaccount, markets); return result; - }, - { - // use shallow equal for result so that we only update when these specific keys differ - memoizeOptions: { resultEqualityCheck: shallowEqual }, - } - ); - -export const createSelectParentSubaccountPositionsWithdrawal = () => - createAppSelector( - [ - selectRawParentSubaccountData, - selectRelevantMarketsData, - ( - _s, - input: { - subaccountNumber: number; - withdrawAmount: string; - } - ) => input, - ], - (parentSubaccount, markets, withdrawalInputs) => { - if (parentSubaccount == null || markets == null) { - return undefined; - } - - const operations = createUsdcWithdrawalOperations(withdrawalInputs); - const modifiedParentSubaccount = applyOperationsToSubaccount(parentSubaccount, operations); - const result = calculateParentSubaccountPositions(modifiedParentSubaccount, markets); - return result; - }, - { - // use shallow equal for result so that we only update when these specific keys differ - memoizeOptions: { resultEqualityCheck: shallowEqual }, } ); diff --git a/src/views/dialogs/DepositDialog2/queries.ts b/src/views/dialogs/DepositDialog2/queries.ts index 1dee08050..2390707a8 100644 --- a/src/views/dialogs/DepositDialog2/queries.ts +++ b/src/views/dialogs/DepositDialog2/queries.ts @@ -151,12 +151,17 @@ export function useDepositRoutes(token: TokenForTransfer, amount: string) { } export function useDepositDeltas({ depositAmount }: { depositAmount: string }) { - const modifiedParentSubaccount = useParameterizedSelector( - BonsaiHelpers.account.deposit.parentSubaccountSummary, - { + const depositInput = useMemo( + () => ({ subaccountNumber: 0, depositAmount, - } + }), + [depositAmount] + ); + + const modifiedParentSubaccount = useParameterizedSelector( + BonsaiHelpers.forms.deposit.parentSubaccountSummary, + depositInput ); return modifiedParentSubaccount; diff --git a/src/views/dialogs/WithdrawDialog2/queries.ts b/src/views/dialogs/WithdrawDialog2/queries.ts index d76e702fb..76e969026 100644 --- a/src/views/dialogs/WithdrawDialog2/queries.ts +++ b/src/views/dialogs/WithdrawDialog2/queries.ts @@ -1,3 +1,5 @@ +import { useMemo } from 'react'; + import { BonsaiHelpers } from '@/abacus-ts/ontology'; import { RouteRequest, SkipClient } from '@skip-go/client'; import { useQuery } from '@tanstack/react-query'; @@ -57,12 +59,17 @@ export function useWithdrawalRoutes({ } export function useWithdrawalDeltas({ withdrawAmount }: { withdrawAmount: string }) { - const modifiedParentSubaccount = useParameterizedSelector( - BonsaiHelpers.account.withdraw.parentSubaccountSummary, - { + const withdrawInput = useMemo( + () => ({ subaccountNumber: 0, withdrawAmount, - } + }), + [withdrawAmount] + ); + + const modifiedParentSubaccount = useParameterizedSelector( + BonsaiHelpers.forms.withdraw.parentSubaccountSummary, + withdrawInput ); return modifiedParentSubaccount;