diff --git a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt index b2cdb8f7e..d5ee3a0b6 100644 --- a/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt +++ b/src/commonMain/kotlin/exchange.dydx.abacus/state/model/TradingStateMachine.kt @@ -51,11 +51,8 @@ import exchange.dydx.abacus.protocols.ParserProtocol import exchange.dydx.abacus.protocols.TrackingProtocol import exchange.dydx.abacus.protocols.asTypedStringMap import exchange.dydx.abacus.responses.ParsingError -import exchange.dydx.abacus.responses.ParsingErrorType import exchange.dydx.abacus.responses.ParsingException -import exchange.dydx.abacus.responses.SocketInfo import exchange.dydx.abacus.responses.StateResponse -import exchange.dydx.abacus.state.app.adaptors.AbUrl import exchange.dydx.abacus.state.app.helper.Formatter import exchange.dydx.abacus.state.changes.Changes import exchange.dydx.abacus.state.changes.StateChanges @@ -77,7 +74,6 @@ import exchange.dydx.abacus.utils.mutableMapOf import exchange.dydx.abacus.utils.safeSet import exchange.dydx.abacus.utils.typedSafeSet import exchange.dydx.abacus.validator.InputValidator -import indexer.codegen.IndexerSparklineTimePeriod import indexer.models.configs.ConfigsMarketAsset import kollections.JsExport import kollections.iListOf @@ -86,9 +82,6 @@ import kollections.iMutableMapOf import kollections.toIList import kollections.toIMap import kollections.toIMutableMap -import kotlinx.serialization.SerializationException -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.jsonObject import kotlin.math.max import kotlin.math.min import kotlin.time.Duration.Companion.days @@ -283,297 +276,6 @@ open class TradingStateMachine( return StateResponse(state, null) } - fun socket( - url: AbUrl, - jsonString: String, - subaccountNumber: Int, - height: BlockAndTime?, - ): StateResponse { - val errors = iMutableListOf() - val json = - try { - Json.parseToJsonElement(jsonString).jsonObject.toMap() - } catch (e: SerializationException) { - errors.add( - ParsingError( - ParsingErrorType.ParsingError, - "$jsonString is not a valid JSON object", - e.stackTraceToString(), - ), - ) - null - } - if (json == null || errors.isNotEmpty()) { - return StateResponse(state, null, errors) - } - return socket(url, json, subaccountNumber, height) - } - - @Throws(Exception::class) - private fun socket( - url: AbUrl, - payload: Map, - subaccountNumber: Int, - height: BlockAndTime?, - ): StateResponse { - var changes: StateChanges? = null - val type = parser.asString(payload["type"]) - val channel = parser.asString(payload["channel"]) - val id = parser.asString(payload["id"]) - val childSubaccountNumber = parser.asInt(payload["subaccountNumber"]) - val info = SocketInfo(type, channel, id, childSubaccountNumber) - try { - when (type) { - "subscribed" -> { - val content = parser.asNativeMap(payload["contents"]) - ?: throw ParsingException( - ParsingErrorType.MissingContent, - payload.toString(), - ) - when (channel) { - "v3_markets", "v4_markets" -> { - changes = receivedMarkets(content, subaccountNumber) - } - - "v4_subaccounts", "v4_parent_subaccounts" -> { - changes = receivedSubaccountSubscribed(content, height) - } - - "v3_orderbook", "v4_orderbook" -> { - val market = parser.asString(payload["id"]) - changes = receivedOrderbook(market, content, subaccountNumber) - } - - "v3_trades", "v4_trades" -> { - val market = parser.asString(payload["id"]) - changes = receivedTrades(market, content) - } - - "v4_candles" -> { - val channel = parser.asString(payload["id"]) - val (market, resolution) = splitCandlesChannel(channel) - changes = receivedCandles(market, resolution, content) - } - - else -> { - throw ParsingException( - ParsingErrorType.UnknownChannel, - "$channel subscribed is not known", - ) - } - } - } - - "unsubscribed" -> {} - - "channel_data" -> { - val content = parser.asNativeMap(payload["contents"]) - ?: throw ParsingException( - ParsingErrorType.MissingContent, - payload.toString(), - ) - when (channel) { - "v3_markets", "v4_markets" -> { - changes = receivedMarketsChanges(content, subaccountNumber) - } - - "v4_subaccounts", "v4_parent_subaccounts" -> { - changes = receivedSubaccountsChanges(content, info, height) - } - - "v3_orderbook", "v4_orderbook" -> { - throw ParsingException( - ParsingErrorType.UnhandledEndpoint, - "channel_data for $channel is not implemented", - ) - // change = receivedOrderbookChanges(market, it) - } - - "v3_trades", "v4_trades" -> { - val market = parser.asString(payload["id"]) - changes = receivedTradesChanges(market, content) - } - - "v4_candles" -> { - val channel = parser.asString(payload["id"]) - val (market, resolution) = splitCandlesChannel(channel) - changes = receivedCandlesChanges(market, resolution, content) - } - - else -> { - throw ParsingException( - ParsingErrorType.UnknownChannel, - "$channel channel data is not known", - ) - } - } - } - - "channel_batch_data" -> { - val content = parser.asList(payload["contents"]) - ?: throw ParsingException( - ParsingErrorType.MissingContent, - payload.toString(), - ) - when (channel) { - "v3_markets", "v4_markets" -> { - changes = receivedBatchedMarketsChanges(content, subaccountNumber) - } - - "v3_trades", "v4_trades" -> { - val market = parser.asString(payload["id"]) - changes = receivedBatchedTradesChanges(market, content) - } - - "v4_candles" -> { - val channel = parser.asString(payload["id"]) - val (market, resolution) = splitCandlesChannel(channel) - changes = receivedBatchedCandlesChanges(market, resolution, content) - } - - "v3_orderbook", "v4_orderbook" -> { - val market = parser.asString(payload["id"]) - changes = receivedBatchOrderbookChanges( - market, - content, - subaccountNumber, - ) - } - - "v4_subaccounts", "v4_parent_subaccounts" -> { - changes = receivedBatchSubaccountsChanges(content, info, height) - } - - else -> { - throw ParsingException( - ParsingErrorType.UnknownChannel, - "$channel channel batch data is not known", - ) - } - } - } - - "connected" -> {} - - "error" -> { - throw ParsingException(ParsingErrorType.BackendError, payload.toString()) - } - - else -> { - throw ParsingException( - ParsingErrorType.Unhandled, - "Type [ $type # $channel ] is not handled", - ) - } - } - var realChanges = changes - changes?.let { - realChanges = updateStateChanges(it) - } - return StateResponse(state, realChanges, null, info) - } catch (e: ParsingException) { - return StateResponse(state, null, iListOf(e.toParsingError()), info) - } - } - - private fun splitCandlesChannel(channel: String?): Pair { - if (channel == null) { - throw ParsingException( - ParsingErrorType.UnknownChannel, - "$channel is not known", - ) - } - val marketAndResolution = channel.split("/") - if (marketAndResolution.size != 2) { - throw ParsingException( - ParsingErrorType.UnknownChannel, - "$channel is not known", - ) - } - val market = marketAndResolution[0] - val resolution = marketAndResolution[1] - return Pair(market, resolution) - } - - /** - * function specifically for testing spoofed rest response processing - */ - fun rest( - url: AbUrl, - payload: String, - subaccountNumber: Int, - height: Int?, - deploymentUri: String? = null, - period: String? = null, - ): StateResponse { - /* - For backward compatibility only - */ - var changes: StateChanges? = null - var error: ParsingError? = null - when (url.path) { - "/v3/historical-pnl", "/v4/historical-pnl" -> { - val subaccountNumber = - parser.asInt(url.params?.firstOrNull { param -> param.key == "subaccountNumber" }?.value) - ?: 0 - changes = historicalPnl(payload, subaccountNumber) - } - - "/v3/candles" -> { - changes = candles(payload) - } - - "/v4/sparklines" -> { - changes = sparklines(payload, IndexerSparklineTimePeriod.ONEDAY) - } - - "/v4/fills" -> { - val subaccountNumber = - parser.asInt(url.params?.firstOrNull { param -> param.key == "subaccountNumber" }?.value) - ?: 0 - changes = fills(payload, subaccountNumber) - } - - "/v4/transfers" -> { - val subaccountNumber = - parser.asInt(url.params?.firstOrNull { param -> param.key == "subaccountNumber" }?.value) - ?: 0 - changes = transfers(payload, subaccountNumber) - } - - "/configs/markets.json" -> { - if (deploymentUri != null) { - changes = configurations( - payload = payload, - subaccountNumber = subaccountNumber, - deploymentUri = deploymentUri, - ) - } - } - - else -> { - if (url.path.contains("/v3/historical-funding/") || url.path.contains("/v4/historicalFunding/")) { - changes = historicalFundings(payload) - } else if (url.path.contains("/v3/candles/") || url.path.contains("/v4/candles/")) { - changes = candles(payload) - } else if (url.path.contains("/v4/addresses/")) { - changes = account(payload) - } else { - error = ParsingError( - ParsingErrorType.UnhandledEndpoint, - "${url.path} parsing has not be implemented, or is an invalid endpoint", - ) - } - } - } - if (changes != null) { - updateStateChanges(changes) - } - - val errors = if (error != null) iListOf(error) else null - return StateResponse(state, changes, errors) - } - internal fun resetWallet(accountAddress: String?): StateResponse { val wallet = if (accountAddress != null) iMapOf("walletAddress" to accountAddress) else null this.wallet = wallet diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/AdjustIsolatedMarginInputTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/AdjustIsolatedMarginInputTests.kt index 55b35404f..16ca05458 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/AdjustIsolatedMarginInputTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/AdjustIsolatedMarginInputTests.kt @@ -9,6 +9,8 @@ import exchange.dydx.abacus.state.app.adaptors.AbUrl import exchange.dydx.abacus.state.model.AdjustIsolatedMarginInputField import exchange.dydx.abacus.state.model.adjustIsolatedMargin import exchange.dydx.abacus.tests.extensions.parseOnChainEquityTiers +import exchange.dydx.abacus.tests.extensions.rest +import exchange.dydx.abacus.tests.extensions.socket import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/IsolatedMarginModeTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/IsolatedMarginModeTests.kt index 33dbfc78f..b04eebe1e 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/IsolatedMarginModeTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/IsolatedMarginModeTests.kt @@ -12,6 +12,7 @@ import exchange.dydx.abacus.state.model.closePosition import exchange.dydx.abacus.state.model.trade import exchange.dydx.abacus.state.model.tradeInMarket import exchange.dydx.abacus.tests.extensions.parseOnChainEquityTiers +import exchange.dydx.abacus.tests.extensions.socket import kotlin.test.BeforeTest import kotlin.test.DefaultAsserter.assertTrue import kotlin.test.Test diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/TriggerOrdersInputTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/TriggerOrdersInputTests.kt index d6bd0de1b..78e5351ca 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/TriggerOrdersInputTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/TriggerOrdersInputTests.kt @@ -7,6 +7,7 @@ import exchange.dydx.abacus.responses.StateResponse import exchange.dydx.abacus.state.app.adaptors.AbUrl import exchange.dydx.abacus.state.model.TriggerOrdersInputField import exchange.dydx.abacus.state.model.triggerOrders +import exchange.dydx.abacus.tests.extensions.rest import exchange.dydx.abacus.utils.Rounder import kotlin.math.abs import kotlin.test.Test diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v3/V3OrderbookLoadTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v3/V3OrderbookLoadTests.kt index ef065ca93..13e0db1b4 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v3/V3OrderbookLoadTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v3/V3OrderbookLoadTests.kt @@ -1,5 +1,6 @@ package exchange.dydx.abacus.payload.v3 +import exchange.dydx.abacus.tests.extensions.socket import exchange.dydx.abacus.utils.Logger import kotlin.test.Test import kotlin.test.assertNotNull diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v3/V3OrderbookTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v3/V3OrderbookTests.kt index ffdc4e821..321395f97 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v3/V3OrderbookTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v3/V3OrderbookTests.kt @@ -1,6 +1,7 @@ package exchange.dydx.abacus.payload.v3 import exchange.dydx.abacus.state.model.setOrderbookGrouping +import exchange.dydx.abacus.tests.extensions.socket import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4AccountOrdersSortingTest.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4AccountOrdersSortingTest.kt index f3d63d4f7..2215c6bc5 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4AccountOrdersSortingTest.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4AccountOrdersSortingTest.kt @@ -3,6 +3,7 @@ package exchange.dydx.abacus.payload.v4 import exchange.dydx.abacus.calculator.CalculationPeriod import exchange.dydx.abacus.tests.extensions.loadv4SubaccountsWithPositions import exchange.dydx.abacus.tests.extensions.log +import exchange.dydx.abacus.tests.extensions.socket import exchange.dydx.abacus.utils.SHORT_TERM_ORDER_DURATION import exchange.dydx.abacus.utils.ServerTime import kotlin.test.Test diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4AccountTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4AccountTests.kt index 657dba169..0c4facdc9 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4AccountTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4AccountTests.kt @@ -25,6 +25,8 @@ import exchange.dydx.abacus.tests.extensions.loadv4SubaccountWithOrdersAndFillsC import exchange.dydx.abacus.tests.extensions.loadv4SubaccountsWithPositions import exchange.dydx.abacus.tests.extensions.log import exchange.dydx.abacus.tests.extensions.parseOnChainEquityTiers +import exchange.dydx.abacus.tests.extensions.rest +import exchange.dydx.abacus.tests.extensions.socket import exchange.dydx.abacus.utils.JsonEncoder import exchange.dydx.abacus.utils.Parser import exchange.dydx.abacus.utils.ServerTime diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4CalculationTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4CalculationTests.kt index e0f564e87..6baa692a6 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4CalculationTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4CalculationTests.kt @@ -3,6 +3,8 @@ package exchange.dydx.abacus.payload.v4 import exchange.dydx.abacus.calculator.CalculationPeriod import exchange.dydx.abacus.responses.StateResponse import exchange.dydx.abacus.state.app.adaptors.AbUrl +import exchange.dydx.abacus.tests.extensions.rest +import exchange.dydx.abacus.tests.extensions.socket import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4CandlesTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4CandlesTests.kt index ac409fbe2..663efae48 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4CandlesTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4CandlesTests.kt @@ -5,6 +5,7 @@ import exchange.dydx.abacus.tests.extensions.loadCandlesAllMarkets import exchange.dydx.abacus.tests.extensions.loadCandlesFirst import exchange.dydx.abacus.tests.extensions.loadCandlesSecond import exchange.dydx.abacus.tests.extensions.log +import exchange.dydx.abacus.tests.extensions.socket import exchange.dydx.abacus.utils.ServerTime import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4ClosePositionTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4ClosePositionTests.kt index 5306723d0..02c379d70 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4ClosePositionTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4ClosePositionTests.kt @@ -8,6 +8,7 @@ import exchange.dydx.abacus.state.manager.StatsigConfig import exchange.dydx.abacus.state.model.ClosePositionInputField import exchange.dydx.abacus.state.model.closePosition import exchange.dydx.abacus.tests.extensions.log +import exchange.dydx.abacus.tests.extensions.socket import exchange.dydx.abacus.utils.ServerTime import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4DuplicateWebsocketMessageTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4DuplicateWebsocketMessageTests.kt index 988541cc2..76ee8ebc9 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4DuplicateWebsocketMessageTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4DuplicateWebsocketMessageTests.kt @@ -2,6 +2,7 @@ package exchange.dydx.abacus.payload.v4 import exchange.dydx.abacus.calculator.CalculationPeriod import exchange.dydx.abacus.tests.extensions.loadv4TradesChanged +import exchange.dydx.abacus.tests.extensions.socket import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4MarketsTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4MarketsTests.kt index 3515daf38..710d8417e 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4MarketsTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4MarketsTests.kt @@ -8,6 +8,8 @@ import exchange.dydx.abacus.tests.extensions.loadv4TradesBatchChanged import exchange.dydx.abacus.tests.extensions.loadv4TradesChanged import exchange.dydx.abacus.tests.extensions.loadv4TradesSubscribed import exchange.dydx.abacus.tests.extensions.log +import exchange.dydx.abacus.tests.extensions.rest +import exchange.dydx.abacus.tests.extensions.socket import exchange.dydx.abacus.utils.ServerTime import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4OrderbookTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4OrderbookTests.kt index 4f423355d..ede43180a 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4OrderbookTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4OrderbookTests.kt @@ -4,6 +4,7 @@ import exchange.dydx.abacus.payload.v3.V3BaseTests import exchange.dydx.abacus.state.app.adaptors.AbUrl import exchange.dydx.abacus.state.model.setOrderbookGrouping import exchange.dydx.abacus.tests.extensions.log +import exchange.dydx.abacus.tests.extensions.socket import exchange.dydx.abacus.utils.ServerTime import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4ParentSubaccountTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4ParentSubaccountTests.kt index d6e7f64d0..fe675997f 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4ParentSubaccountTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4ParentSubaccountTests.kt @@ -4,6 +4,8 @@ import exchange.dydx.abacus.calculator.CalculationPeriod import exchange.dydx.abacus.responses.StateResponse import exchange.dydx.abacus.state.app.adaptors.AbUrl import exchange.dydx.abacus.tests.extensions.parseOnChainEquityTiers +import exchange.dydx.abacus.tests.extensions.rest +import exchange.dydx.abacus.tests.extensions.socket import indexer.codegen.IndexerPerpetualPositionStatus import kotlin.test.BeforeTest import kotlin.test.Test diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4SubaccountTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4SubaccountTests.kt index c1d958c4e..21c0bbe0e 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4SubaccountTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4SubaccountTests.kt @@ -3,6 +3,8 @@ package exchange.dydx.abacus.payload.v4 import exchange.dydx.abacus.calculator.CalculationPeriod import exchange.dydx.abacus.responses.StateResponse import exchange.dydx.abacus.state.app.adaptors.AbUrl +import exchange.dydx.abacus.tests.extensions.rest +import exchange.dydx.abacus.tests.extensions.socket import exchange.dydx.abacus.utils.ServerTime import indexer.codegen.IndexerPerpetualPositionStatus import kotlin.test.Test diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4TradeInputTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4TradeInputTests.kt index 0d06db84a..016fe9a1f 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4TradeInputTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/payload/v4/V4TradeInputTests.kt @@ -12,6 +12,7 @@ import exchange.dydx.abacus.state.model.TradeInputField import exchange.dydx.abacus.state.model.trade import exchange.dydx.abacus.state.model.tradeInMarket import exchange.dydx.abacus.tests.extensions.loadv4Accounts +import exchange.dydx.abacus.tests.extensions.socket import kollections.iListOf import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/tests/extensions/TradingStateMachine+TestUtils.kt b/src/commonTest/kotlin/exchange.dydx.abacus/tests/extensions/TradingStateMachine+TestUtils.kt index 9cccc0da8..cd0c7eb53 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/tests/extensions/TradingStateMachine+TestUtils.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/tests/extensions/TradingStateMachine+TestUtils.kt @@ -1,17 +1,46 @@ package exchange.dydx.abacus.tests.extensions import exchange.dydx.abacus.responses.ParsingError +import exchange.dydx.abacus.responses.ParsingErrorType import exchange.dydx.abacus.responses.ParsingException +import exchange.dydx.abacus.responses.SocketInfo import exchange.dydx.abacus.responses.StateResponse import exchange.dydx.abacus.state.app.adaptors.AbUrl import exchange.dydx.abacus.state.app.adaptors.NetworkParam import exchange.dydx.abacus.state.changes.StateChanges +import exchange.dydx.abacus.state.manager.BlockAndTime import exchange.dydx.abacus.state.model.TradingStateMachine +import exchange.dydx.abacus.state.model.account +import exchange.dydx.abacus.state.model.candles +import exchange.dydx.abacus.state.model.fills +import exchange.dydx.abacus.state.model.historicalFundings +import exchange.dydx.abacus.state.model.historicalPnl import exchange.dydx.abacus.state.model.onChainEquityTiers +import exchange.dydx.abacus.state.model.receivedBatchOrderbookChanges +import exchange.dydx.abacus.state.model.receivedBatchSubaccountsChanges +import exchange.dydx.abacus.state.model.receivedBatchedCandlesChanges +import exchange.dydx.abacus.state.model.receivedBatchedMarketsChanges +import exchange.dydx.abacus.state.model.receivedBatchedTradesChanges +import exchange.dydx.abacus.state.model.receivedCandles +import exchange.dydx.abacus.state.model.receivedCandlesChanges +import exchange.dydx.abacus.state.model.receivedMarkets +import exchange.dydx.abacus.state.model.receivedMarketsChanges +import exchange.dydx.abacus.state.model.receivedOrderbook +import exchange.dydx.abacus.state.model.receivedSubaccountSubscribed +import exchange.dydx.abacus.state.model.receivedSubaccountsChanges +import exchange.dydx.abacus.state.model.receivedTrades +import exchange.dydx.abacus.state.model.receivedTradesChanges +import exchange.dydx.abacus.state.model.sparklines +import exchange.dydx.abacus.state.model.transfers import exchange.dydx.abacus.tests.payloads.AbacusMockData import exchange.dydx.abacus.utils.ServerTime +import indexer.codegen.IndexerSparklineTimePeriod import kollections.iListOf +import kollections.iMutableListOf import kotlinx.datetime.Instant +import kotlinx.serialization.SerializationException +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonObject fun TradingStateMachine.loadMarkets(mock: AbacusMockData): StateResponse { return socket(mock.socketUrl, mock.marketsChannel.subscribed, 0, null) @@ -305,3 +334,294 @@ fun TradingStateMachine.parseOnChainEquityTiers(payload: String): StateResponse val errors = if (error != null) iListOf(error) else null return StateResponse(state, changes, errors) } + +fun TradingStateMachine.socket( + url: AbUrl, + jsonString: String, + subaccountNumber: Int, + height: BlockAndTime?, +): StateResponse { + val errors = iMutableListOf() + val json = + try { + Json.parseToJsonElement(jsonString).jsonObject.toMap() + } catch (e: SerializationException) { + errors.add( + ParsingError( + ParsingErrorType.ParsingError, + "$jsonString is not a valid JSON object", + e.stackTraceToString(), + ), + ) + null + } + if (json == null || errors.isNotEmpty()) { + return StateResponse(state, null, errors) + } + return socket(url, json, subaccountNumber, height) +} + +@Throws(Exception::class) +private fun TradingStateMachine.socket( + url: AbUrl, + payload: Map, + subaccountNumber: Int, + height: BlockAndTime?, +): StateResponse { + var changes: StateChanges? = null + val type = parser.asString(payload["type"]) + val channel = parser.asString(payload["channel"]) + val id = parser.asString(payload["id"]) + val childSubaccountNumber = parser.asInt(payload["subaccountNumber"]) + val info = SocketInfo(type, channel, id, childSubaccountNumber) + try { + when (type) { + "subscribed" -> { + val content = parser.asNativeMap(payload["contents"]) + ?: throw ParsingException( + ParsingErrorType.MissingContent, + payload.toString(), + ) + when (channel) { + "v3_markets", "v4_markets" -> { + changes = receivedMarkets(content, subaccountNumber) + } + + "v4_subaccounts", "v4_parent_subaccounts" -> { + changes = receivedSubaccountSubscribed(content, height) + } + + "v3_orderbook", "v4_orderbook" -> { + val market = parser.asString(payload["id"]) + changes = receivedOrderbook(market, content, subaccountNumber) + } + + "v3_trades", "v4_trades" -> { + val market = parser.asString(payload["id"]) + changes = receivedTrades(market, content) + } + + "v4_candles" -> { + val channel = parser.asString(payload["id"]) + val (market, resolution) = splitCandlesChannel(channel) + changes = receivedCandles(market, resolution, content) + } + + else -> { + throw ParsingException( + ParsingErrorType.UnknownChannel, + "$channel subscribed is not known", + ) + } + } + } + + "unsubscribed" -> {} + + "channel_data" -> { + val content = parser.asNativeMap(payload["contents"]) + ?: throw ParsingException( + ParsingErrorType.MissingContent, + payload.toString(), + ) + when (channel) { + "v3_markets", "v4_markets" -> { + changes = receivedMarketsChanges(content, subaccountNumber) + } + + "v4_subaccounts", "v4_parent_subaccounts" -> { + changes = receivedSubaccountsChanges(content, info, height) + } + + "v3_orderbook", "v4_orderbook" -> { + throw ParsingException( + ParsingErrorType.UnhandledEndpoint, + "channel_data for $channel is not implemented", + ) + // change = receivedOrderbookChanges(market, it) + } + + "v3_trades", "v4_trades" -> { + val market = parser.asString(payload["id"]) + changes = receivedTradesChanges(market, content) + } + + "v4_candles" -> { + val channel = parser.asString(payload["id"]) + val (market, resolution) = splitCandlesChannel(channel) + changes = receivedCandlesChanges(market, resolution, content) + } + + else -> { + throw ParsingException( + ParsingErrorType.UnknownChannel, + "$channel channel data is not known", + ) + } + } + } + + "channel_batch_data" -> { + val content = parser.asList(payload["contents"]) + ?: throw ParsingException( + ParsingErrorType.MissingContent, + payload.toString(), + ) + when (channel) { + "v3_markets", "v4_markets" -> { + changes = receivedBatchedMarketsChanges(content, subaccountNumber) + } + + "v3_trades", "v4_trades" -> { + val market = parser.asString(payload["id"]) + changes = receivedBatchedTradesChanges(market, content) + } + + "v4_candles" -> { + val channel = parser.asString(payload["id"]) + val (market, resolution) = splitCandlesChannel(channel) + changes = receivedBatchedCandlesChanges(market, resolution, content) + } + + "v3_orderbook", "v4_orderbook" -> { + val market = parser.asString(payload["id"]) + changes = receivedBatchOrderbookChanges( + market, + content, + subaccountNumber, + ) + } + + "v4_subaccounts", "v4_parent_subaccounts" -> { + changes = receivedBatchSubaccountsChanges(content, info, height) + } + + else -> { + throw ParsingException( + ParsingErrorType.UnknownChannel, + "$channel channel batch data is not known", + ) + } + } + } + + "connected" -> {} + + "error" -> { + throw ParsingException(ParsingErrorType.BackendError, payload.toString()) + } + + else -> { + throw ParsingException( + ParsingErrorType.Unhandled, + "Type [ $type # $channel ] is not handled", + ) + } + } + var realChanges = changes + changes?.let { + realChanges = updateStateChanges(it) + } + return StateResponse(state, realChanges, null, info) + } catch (e: ParsingException) { + return StateResponse(state, null, iListOf(e.toParsingError()), info) + } +} + +private fun TradingStateMachine.splitCandlesChannel(channel: String?): Pair { + if (channel == null) { + throw ParsingException( + ParsingErrorType.UnknownChannel, + "$channel is not known", + ) + } + val marketAndResolution = channel.split("/") + if (marketAndResolution.size != 2) { + throw ParsingException( + ParsingErrorType.UnknownChannel, + "$channel is not known", + ) + } + val market = marketAndResolution[0] + val resolution = marketAndResolution[1] + return Pair(market, resolution) +} + +/** + * function specifically for testing spoofed rest response processing + */ +fun TradingStateMachine.rest( + url: AbUrl, + payload: String, + subaccountNumber: Int, + height: Int?, + deploymentUri: String? = null, + period: String? = null, +): StateResponse { + /* + For backward compatibility only + */ + var changes: StateChanges? = null + var error: ParsingError? = null + when (url.path) { + "/v3/historical-pnl", "/v4/historical-pnl" -> { + val subaccountNumber = + parser.asInt(url.params?.firstOrNull { param -> param.key == "subaccountNumber" }?.value) + ?: 0 + changes = historicalPnl(payload, subaccountNumber) + } + + "/v3/candles" -> { + changes = candles(payload) + } + + "/v4/sparklines" -> { + changes = sparklines(payload, IndexerSparklineTimePeriod.ONEDAY) + } + + "/v4/fills" -> { + val subaccountNumber = + parser.asInt(url.params?.firstOrNull { param -> param.key == "subaccountNumber" }?.value) + ?: 0 + changes = fills(payload, subaccountNumber) + } + + "/v4/transfers" -> { + val subaccountNumber = + parser.asInt(url.params?.firstOrNull { param -> param.key == "subaccountNumber" }?.value) + ?: 0 + changes = transfers(payload, subaccountNumber) + } + + "/configs/markets.json" -> { + if (deploymentUri != null) { + changes = configurations( + payload = payload, + subaccountNumber = subaccountNumber, + deploymentUri = deploymentUri, + ) + } + } + + else -> { + if (url.path.contains("/v3/historical-funding/") || url.path.contains("/v4/historicalFunding/")) { + changes = historicalFundings(payload) + } else if (url.path.contains("/v3/candles/") || url.path.contains("/v4/candles/")) { + changes = candles(payload) + } else if (url.path.contains("/v4/addresses/")) { + changes = account(payload) + } else { + error = ParsingError( + ParsingErrorType.UnhandledEndpoint, + "${url.path} parsing has not be implemented, or is an invalid endpoint", + ) + } + } + } + if (changes != null) { + updateStateChanges(changes) + } + + val errors = if (error != null) iListOf(error) else null + return StateResponse(state, changes, errors) +} diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/tickets/TRCL2998Tests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/tickets/TRCL2998Tests.kt index db77de8eb..7a9105a14 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/tickets/TRCL2998Tests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/tickets/TRCL2998Tests.kt @@ -4,6 +4,7 @@ import exchange.dydx.abacus.calculator.CalculationPeriod import exchange.dydx.abacus.payload.v4.V4BaseTests import exchange.dydx.abacus.responses.StateResponse import exchange.dydx.abacus.state.model.tradeInMarket +import exchange.dydx.abacus.tests.extensions.socket import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/validation/TradeBracketsTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/validation/TradeBracketsTests.kt index f8ad8ed96..12b34452e 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/validation/TradeBracketsTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/validation/TradeBracketsTests.kt @@ -8,6 +8,7 @@ import exchange.dydx.abacus.state.model.TradeInputField import exchange.dydx.abacus.state.model.trade import exchange.dydx.abacus.state.model.tradeInMarket import exchange.dydx.abacus.tests.extensions.log +import exchange.dydx.abacus.tests.extensions.socket import exchange.dydx.abacus.utils.ServerTime import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/validation/TriggerOrdersInputValidationTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/validation/TriggerOrdersInputValidationTests.kt index 8098f514b..a70d9be46 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/validation/TriggerOrdersInputValidationTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/validation/TriggerOrdersInputValidationTests.kt @@ -8,6 +8,7 @@ import exchange.dydx.abacus.responses.StateResponse import exchange.dydx.abacus.state.app.adaptors.AbUrl import exchange.dydx.abacus.state.model.TriggerOrdersInputField import exchange.dydx.abacus.state.model.triggerOrders +import exchange.dydx.abacus.tests.extensions.rest import kollections.iListOf import kotlin.test.Test import kotlin.test.assertEquals diff --git a/src/commonTest/kotlin/exchange.dydx.abacus/validation/ValidationsTests.kt b/src/commonTest/kotlin/exchange.dydx.abacus/validation/ValidationsTests.kt index 694a94924..bd337ab74 100644 --- a/src/commonTest/kotlin/exchange.dydx.abacus/validation/ValidationsTests.kt +++ b/src/commonTest/kotlin/exchange.dydx.abacus/validation/ValidationsTests.kt @@ -4,6 +4,7 @@ import exchange.dydx.abacus.payload.v4.V4BaseTests import exchange.dydx.abacus.responses.StateResponse import exchange.dydx.abacus.state.model.trade import exchange.dydx.abacus.tests.extensions.parseOnChainEquityTiers +import exchange.dydx.abacus.tests.extensions.socket open class ValidationsTests : V4BaseTests() { override fun setup() {