Skip to content

Commit

Permalink
Merge branch 'main' into tu/vault-validate
Browse files Browse the repository at this point in the history
  • Loading branch information
tyleroooo authored Aug 22, 2024
2 parents 346528a + c1111d4 commit 8c8aeee
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ internal class TradeInputCalculator(
calculateNonMarketTrade(
trade,
market,
subaccount,
type,
isBuying,
input,
Expand Down Expand Up @@ -147,19 +148,21 @@ internal class TradeInputCalculator(
private fun calculateNonMarketTrade(
trade: Map<String, Any>,
market: Map<String, Any>?,
subaccount: Map<String, Any>?,
type: String,
isBuying: Boolean,
input: String,
): Map<String, Any> {
val modifiedTrade = trade.mutable()
val tradeSize = parser.asNativeMap(trade["size"])
val modified = calculateSize(trade, subaccount, market)
val tradeSize = parser.asNativeMap(modified["size"])?.mutable()
val tradePrices = parser.asNativeMap(trade["price"])
val stepSize =
parser.asDouble(parser.value(market, "configs.stepSize") ?: 0.001)!!
if (tradeSize != null) {
val modifiedTradeSize = tradeSize.mutable()
when (input) {
"size.size" -> {
"size.size", "size.percent" -> {
val price = nonMarketOrderPrice(tradePrices, market, type, isBuying)
val size = parser.asDouble(tradeSize.get("size"))
val usdcSize =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,5 +355,7 @@ interface PresentationProtocol {
interface LoggingProtocol {
fun d(tag: String, message: String)

fun e(tag: String, message: String)
fun e(tag: String, message: String, context: Map<String, Any>?, error: Error?)

fun ddInfo(tag: String, message: String, context: Map<String, Any>?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ data object StatsigConfig {
var useSkip: Boolean = false
var ff_enable_evm_swaps: Boolean = false
var dc_max_safe_bridge_fees: Float = Float.POSITIVE_INFINITY
var ff_enable_limit_close: Boolean = false
}

@JsExport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import exchange.dydx.abacus.responses.StateResponse
import exchange.dydx.abacus.responses.cannotModify
import exchange.dydx.abacus.state.changes.Changes
import exchange.dydx.abacus.state.changes.StateChanges
import exchange.dydx.abacus.state.manager.StatsigConfig
import exchange.dydx.abacus.utils.Numeric
import exchange.dydx.abacus.utils.mutable
import exchange.dydx.abacus.utils.mutableMapOf
Expand All @@ -22,7 +23,10 @@ import kotlinx.serialization.Serializable
enum class ClosePositionInputField(val rawValue: String) {
market("market"),
size("size.size"),
percent("size.percent");
percent("size.percent"),

useLimit("useLimit"),
limitPrice("price.limitPrice");

companion object {
operator fun invoke(rawValue: String?) =
Expand Down Expand Up @@ -105,6 +109,35 @@ fun TradingStateMachine.closePosition(
subaccountNumberChanges,
)
}
ClosePositionInputField.useLimit.rawValue -> {
val useLimitClose = (parser.asBool(data) ?: false) && StatsigConfig.ff_enable_limit_close
trade.safeSet(typeText, useLimitClose)

if (useLimitClose) {
trade["type"] = "LIMIT"
trade["timeInForce"] = "GTT"
parser.asString(trade["marketId"])?.let {
trade.safeSet("price.limitPrice", getMidMarketPrice(it))
}
} else {
trade["type"] = "MARKET"
trade["timeInForce"] = "IOC"
}

changes = StateChanges(
iListOf(Changes.subaccount, Changes.input),
null,
subaccountNumberChanges,
)
}
ClosePositionInputField.limitPrice.rawValue -> {
trade.safeSet(typeText, parser.asDouble(data))
changes = StateChanges(
iListOf(Changes.subaccount, Changes.input),
null,
subaccountNumberChanges,
)
}
else -> {}
}
if (sizeChanged) {
Expand Down Expand Up @@ -178,3 +211,16 @@ private fun TradingStateMachine.initiateClosePosition(

return parser.asMap(modified["trade"])?.mutable() ?: trade
}

private fun TradingStateMachine.getMidMarketPrice(
marketId: String
): Double? {
val markets = parser.asNativeMap(marketsSummary?.get("markets"))
return parser.asNativeMap(parser.asNativeMap(markets?.get(marketId))?.get("orderbook_consolidated"))?.let { orderbook ->
parser.asDouble(parser.value(orderbook, "asks.0.price"))?.let { firstAskPrice ->
parser.asDouble(parser.value(orderbook, "bids.0.price"))?.let { firstBidPrice ->
(firstAskPrice + firstBidPrice) / 2.0
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import exchange.dydx.abacus.utils.toNobleAddress
import exchange.dydx.abacus.utils.toOsmosisAddress
import io.ktor.util.encodeBase64
import kollections.iListOf
import kollections.toIMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
Expand Down Expand Up @@ -357,8 +358,10 @@ internal class OnboardingSupervisor(
val header = iMapOf(
"Content-Type" to "application/json",
)
Logger.ddInfo(body.toIMap(), { "retrieveSkipDepositRouteNonCCTP payload sending" })
helper.post(url, header, body.toJsonPrettyPrint()) { _, response, code, headers ->
if (response != null) {
Logger.ddInfo(helper.parser.decodeJsonObject(response), { "retrieveSkipDepositRouteCCTP payload received" })
val currentFromAmount = stateMachine.state?.input?.transfer?.size?.size
val oldFromAmount = oldState?.input?.transfer?.size?.size
if (currentFromAmount == oldFromAmount) {
Expand Down Expand Up @@ -415,8 +418,10 @@ internal class OnboardingSupervisor(
val header = iMapOf(
"Content-Type" to "application/json",
)
Logger.ddInfo(body.toIMap(), { "retrieveSkipDepositRouteCCTP payload sending" })
helper.post(url, header, body.toJsonPrettyPrint()) { _, response, code, headers ->
if (response != null) {
Logger.ddInfo(helper.parser.decodeJsonObject(response), { "retrieveSkipDepositRouteCCTP payload received" })
val currentFromAmount = stateMachine.state?.input?.transfer?.size?.size
val oldFromAmount = oldState?.input?.transfer?.size?.size
if (currentFromAmount == oldFromAmount) {
Expand Down Expand Up @@ -1139,8 +1144,10 @@ internal class OnboardingSupervisor(
"Content-Type" to "application/json",
)
val oldState = stateMachine.state
Logger.ddInfo(body.toIMap(), { "retrieveSkipWithdrawalRouteNonCCTP payload sending" })
helper.post(url, header, body.toJsonPrettyPrint()) { _, response, code, headers ->
if (response != null) {
Logger.ddInfo(helper.parser.decodeJsonObject(response), { "retrieveSkipWithdrawalRouteNonCCTP payload received" })
update(stateMachine.squidRoute(response, subaccountNumber ?: 0, null), oldState)
} else {
Logger.e { "retrieveSkipWithdrawalRouteNonCCTP error, code: $code" }
Expand Down Expand Up @@ -1194,8 +1201,10 @@ internal class OnboardingSupervisor(
val header = iMapOf(
"Content-Type" to "application/json",
)
Logger.ddInfo(body.toIMap(), { "retrieveSkipWithdrawalRouteCCTP payload sending" })
helper.post(url, header, body.toJsonPrettyPrint()) { _, response, code, _ ->
if (response != null) {
Logger.ddInfo(helper.parser.decodeJsonObject(response), { "retrieveSkipWithdrawalRouteCCTP payload received" })
val currentFromAmount = stateMachine.state?.input?.transfer?.size?.size
val oldFromAmount = oldState?.input?.transfer?.size?.size
if (currentFromAmount == oldFromAmount) {
Expand Down Expand Up @@ -1693,9 +1702,11 @@ internal class OnboardingSupervisor(
val header = iMapOf(
"Content-Type" to "application/json",
)
Logger.ddInfo(body.toIMap(), { "cctpToNobleSkip payload sending" })
helper.post(url, header, body.toJsonPrettyPrint()) { _, response, code, _ ->
val json = helper.parser.decodeJsonObject(response)
if (json != null) {
Logger.ddInfo(json, { "cctpToNobleSkip payload received" })
val skipRoutePayloadProcessor = SkipRoutePayloadProcessor(parser = helper.parser)
val processedPayload = skipRoutePayloadProcessor.received(existing = mapOf(), payload = json)
val ibcPayload = helper.parser.asString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import exchange.dydx.abacus.state.manager.HumanReadableTriggerOrdersPayload
import exchange.dydx.abacus.state.manager.HumanReadableWithdrawPayload
import exchange.dydx.abacus.state.manager.PlaceOrderMarketInfo
import exchange.dydx.abacus.state.model.TradingStateMachine
import exchange.dydx.abacus.utils.LIMIT_CLOSE_ORDER_DEFAULT_DURATION_DAYS
import exchange.dydx.abacus.utils.MAX_SUBACCOUNT_NUMBER
import exchange.dydx.abacus.utils.NUM_PARENT_SUBACCOUNTS
import exchange.dydx.abacus.utils.SHORT_TERM_ORDER_DURATION
Expand Down Expand Up @@ -248,27 +249,30 @@ internal class SubaccountTransactionPayloadProvider(
@Throws(Exception::class)
override fun closePositionPayload(currentHeight: Int?): HumanReadablePlaceOrderPayload {
val closePosition = stateMachine.state?.input?.closePosition
val isLimitClose = closePosition?.type == OrderType.Limit
val marketId = closePosition?.marketId ?: throw Exception("marketId is null")
val summary = closePosition.summary ?: throw Exception("summary is null")
val clientId = Random.nextInt(0, Int.MAX_VALUE)
val type = closePosition.type?.rawValue ?: "MARKET"
val side = closePosition.side?.rawValue ?: throw Exception("side is null")
val price = summary.payloadPrice ?: throw Exception("price is null")
val size = summary.size ?: throw Exception("size is null")
val sizeInput = null
val timeInForce = "IOC"
val timeInForce = if (isLimitClose) "GTT" else "IOC"
val execution = "DEFAULT"
val reduceOnly = true
val postOnly = false
val goodTilTimeInSeconds = null
val goodTilBlock = currentHeight?.plus(SHORT_TERM_ORDER_DURATION)
val limitCloseDuration = TradeInputGoodUntil(LIMIT_CLOSE_ORDER_DEFAULT_DURATION_DAYS, "D").timeInterval ?: throw Exception("invalid duration")
val goodTilTimeInSeconds = if (isLimitClose) (limitCloseDuration / 1.seconds).toInt() else null
val goodTilBlock = if (isLimitClose) null else currentHeight?.plus(SHORT_TERM_ORDER_DURATION)
val marketInfo = marketInfo(marketId)
val subaccountNumberForPosition = helper.parser.asInt(helper.parser.value(stateMachine.data, "wallet.account.groupedSubaccounts.$subaccountNumber.openPositions.$marketId.childSubaccountNumber")) ?: subaccountNumber

return HumanReadablePlaceOrderPayload(
subaccountNumber = subaccountNumberForPosition,
marketId = marketId,
clientId = clientId,
type = "MARKET",
type = type,
side = side,
price = price,
triggerPrice = null,
Expand Down
3 changes: 3 additions & 0 deletions src/commonMain/kotlin/exchange.dydx.abacus/utils/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ internal const val MIN_USDC_AMOUNT_FOR_AUTO_SWEEP = 50000
// Gas Constants based on historical Squid responses
internal const val DEFAULT_GAS_LIMIT = 1500000
internal const val DEFAULT_GAS_PRICE = 1520000000

// Limit Close GTT duration in Days
internal const val LIMIT_CLOSE_ORDER_DEFAULT_DURATION_DAYS = 28.0
12 changes: 10 additions & 2 deletions src/commonMain/kotlin/exchange.dydx.abacus/utils/Logger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,19 @@ object Logger {
}
}

fun e(message: () -> String) {
fun e(context: Map<String, Any>? = null, error: Error? = null, message: () -> String) {
clientLogger?.let {
it.e(TAG, message())
it.e(TAG, message(), context, error)
} ?: platformErrorLog(message())
}

fun ddInfo(context: Map<String, Any>? = null, message: () -> String) {
if (isDebugEnabled) {
clientLogger?.let {
it.ddInfo(TAG, message(), context)
}
}
}
}

expect fun platformDebugLog(message: String)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package exchange.dydx.abacus.payload.v4

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
Expand All @@ -17,6 +18,8 @@ class V4ClosePositionTests : V4BaseTests() {

testCloseShortPositionInput()
time = perp.log("Close Position", time)

testLimitClosePositionInput()
}

override fun setup() {
Expand Down Expand Up @@ -285,4 +288,120 @@ class V4ClosePositionTests : V4BaseTests() {
""".trimIndent(),
)
}

private fun testLimitClosePositionInput() {
StatsigConfig.ff_enable_limit_close = true
test(
{
perp.socket(mock.socketUrl, mock.accountsChannel.v4_subscribed, 0, null)
},
"""
{
}
""".trimIndent(),
)
/*
Initial setup
*/
test(
{
perp.closePosition("ETH-USD", ClosePositionInputField.market, 0)
},
"""
{
"input": {
"current": "closePosition",
"closePosition": {
"type": "MARKET",
"side": "BUY",
"size": {
"percent": 1,
"input": "size.percent",
"size": 106.179
},
"reduceOnly": true
}
}
}
""".trimIndent(),
)

test(
{
perp.closePosition("true", ClosePositionInputField.useLimit, 0)
},
"""
{
"input": {
"current": "closePosition",
"closePosition": {
"type": "LIMIT",
"side": "BUY",
"size": {
"percent": 1,
"input": "size.percent",
"size": 106.179
},
"price": {
"limitPrice": 2000
},
"reduceOnly": true
}
}
}
""".trimIndent(),
)

test(
{
perp.closePosition("2500", ClosePositionInputField.limitPrice, 0)
},
"""
{
"input": {
"current": "closePosition",
"closePosition": {
"type": "LIMIT",
"side": "BUY",
"size": {
"percent": 1,
"input": "size.percent",
"size": 106.179
},
"price": {
"limitPrice": 2500
},
"reduceOnly": true
}
}
}
""".trimIndent(),
)

test(
{
perp.closePosition("false", ClosePositionInputField.useLimit, 0)
},
"""
{
"input": {
"current": "closePosition",
"closePosition": {
"type": "MARKET",
"side": "BUY",
"size": {
"percent": 1,
"input": "size.percent",
"size": 106.179
},
"price": {
"limitPrice": 2500
},
"reduceOnly": true
}
}
}
""".trimIndent(),
)
}
}
Loading

0 comments on commit 8c8aeee

Please sign in to comment.