Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable testnet OTE flows #446

Merged
merged 5 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface IRouterProcessor {
var tokens: List<Any>?
var chains: List<Any>?
var exchangeDestinationChainId: String?

fun receivedChains(
existing: Map<String, Any>?,
payload: Map<String, Any>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import exchange.dydx.abacus.processor.router.squid.SquidStatusProcessor
import exchange.dydx.abacus.protocols.ParserProtocol
import exchange.dydx.abacus.state.internalstate.InternalTransferInputState
import exchange.dydx.abacus.state.manager.CctpConfig.cctpChainIds
import exchange.dydx.abacus.utils.NATIVE_TOKEN_DEFAULT_ADDRESS
import exchange.dydx.abacus.utils.mutable
import exchange.dydx.abacus.utils.safeSet

Expand Down Expand Up @@ -47,9 +48,12 @@ internal class SkipProcessor(
modified = it.mutable()
}
val chainOptions = chainOptions()

internalState.chains = chainOptions
val selectedChainId = defaultChainId()
// We diff based on map values in order to determine whether to return new state
// Until we diff on `internalState` changes we need to update old map state as well
modified.safeSet("transfer.depositOptions.chains", chainOptions)
modified.safeSet("transfer.withdrawOptions.chains", chainOptions)
modified.safeSet("transfer.chain", selectedChainId)
selectedChainId?.let {
internalState.chainResources = chainResources(chainId = selectedChainId)
Expand Down Expand Up @@ -118,7 +122,6 @@ internal class SkipProcessor(
return receivedRoute(existing, payload, requestId)
}

// TODO: deduplicate this from squid
override fun usdcAmount(data: Map<String, Any>): Double? {
var toAmountUSD = parser.asString(parser.value(data, "transfer.route.toAmountUSD"))
toAmountUSD = toAmountUSD?.replace(",", "")
Expand All @@ -142,11 +145,18 @@ internal class SkipProcessor(
val tokenOptions = tokenOptions(selectedChainId)
internalState.tokens = tokenOptions
modified.safeSet("transfer.token", defaultTokenAddress(selectedChainId))
modified.safeSet("transfer.depositOptions.tokens", tokenOptions)
modified.safeSet("transfer.withdrawalOptions.tokens", tokenOptions)
internalState.tokenResources = tokenResources(selectedChainId)
}

private fun getChainById(chainId: String): Map<String, Any>? {
return parser.asNativeMap(this.chains?.find { parser.asString(parser.asNativeMap(it)?.get("chain_id")) == chainId })
}

override fun defaultChainId(): String? {
val selectedChain = parser.asNativeMap(this.chains?.find { parser.asString(parser.asNativeMap(it)?.get("chain_id")) == "1" })
// eth mainnet chainId is 1
val selectedChain = getChainById("1") ?: parser.asNativeMap(this.chains?.firstOrNull())

return parser.asString(selectedChain?.get("chain_id"))
}
Expand Down Expand Up @@ -181,7 +191,7 @@ internal class SkipProcessor(
val assetsMapForChainId = parser.asNativeMap(this.skipTokens?.get(chainIdToUse))
val assetsForChainId = parser.asNativeList(assetsMapForChainId?.get("assets"))
// coinbase exchange chainId is noble-1. we only allow usdc withdrawals from it
if (chainId === "noble-1") {
if (chainId === exchangeDestinationChainId) {
return assetsForChainId?.filter {
parser.asString(parser.asNativeMap(it)?.get("denom")) == "uusdc"
}
Expand All @@ -198,7 +208,7 @@ internal class SkipProcessor(
val denom = parser.asString(token["denom"])
if (denom?.endsWith("native") == true) {
token["skipDenom"] = denom
token["denom"] = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
token["denom"] = NATIVE_TOKEN_DEFAULT_ADDRESS
}
filteredTokens.add(token.toMap())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,20 @@ class V4StateManagerConfigs(
return if (environment.isMainNet) "noble-1" else "grand-1"
}

fun osmosisChainId(): String? {
return if (environment.isMainNet) "osmosis-1" else "osmosis-5"
fun osmosisChainId(): String {
return if (environment.isMainNet) "osmosis-1" else "osmo-test-5"
}

fun neutronChainId(): String {
return if (environment.isMainNet) "neutron-1" else "pion-1"
}

fun skipV1Chains(): String {
return "$skipHost/v1/info/chains?include_evm=true"
return "$skipHost/v1/info/chains?include_evm=true$onlyTestnets"
}

fun skipV1Assets(): String {
return "$skipHost/v1/fungible/assets?include_evm_assets=true"
return "$skipHost/v1/fungible/assets?include_evm_assets=true$onlyTestnets"
}

fun skipV2MsgsDirect(): String {
Expand All @@ -121,6 +125,11 @@ class V4StateManagerConfigs(

val nobleDenom = "uusdc"

private val onlyTestnets: String
get() {
return if (environment.isMainNet) "" else "&only_testnets=true"
}

private val skipHost: String
get() {
return "https://api.skip.money"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,56 @@ internal open class AccountSupervisor(
}
}

private fun transferNobleBalanceSkip(amount: BigDecimal) {
val url = helper.configs.skipV2MsgsDirect()
val fromChain = helper.configs.nobleChainId()
val fromToken = helper.configs.nobleDenom
val nobleAddress = accountAddress.toNobleAddress() ?: return
val chainId = helper.environment.dydxChainId ?: return
val dydxTokenDemon = helper.environment.tokens["usdc"]?.denom ?: return
val body: Map<String, Any> = mapOf(
"amount_in" to amount.toPlainString(),
// from noble denom and chain
"source_asset_denom" to fromToken,
"source_asset_chain_id" to fromChain,
// to dydx denom and chain
"dest_asset_denom" to dydxTokenDemon,
"dest_asset_chain_id" to chainId,
"chain_ids_to_addresses" to mapOf(
fromChain to nobleAddress,
chainId to accountAddress,
),
"slippage_tolerance_percent" to SLIPPAGE_PERCENT,
)
val header =
iMapOf(
"Content-Type" to "application/json",
)
helper.post(url, header, body.toJsonPrettyPrint()) { _, response, code, _ ->
if (response == null) {
val json = helper.parser.decodeJsonObject(response)
if (json != null) {
val skipRoutePayloadProcessor = SkipRoutePayloadProcessor(parser = helper.parser)
val processedPayload = skipRoutePayloadProcessor.received(existing = mapOf(), payload = json)
val ibcPayload =
helper.parser.asString(
processedPayload.get("data"),
)
if (ibcPayload != null) {
helper.transaction(TransactionType.SendNobleIBC, ibcPayload) {
val error = helper.parseTransactionResponse(it)
if (error != null) {
Logger.e { "transferNobleBalanceSkip error: $error" }
}
}
}
}
} else {
Logger.e { "transferNobleBalanceSkip error, code: $code" }
}
}
}

private fun handleComplianceResponse(response: String?, httpCode: Int): ComplianceStatus {
var complianceStatus = ComplianceStatus.UNKNOWN
var updatedAt: String? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1018,10 +1018,9 @@ internal class OnboardingSupervisor(
return
}
if (fromAmount <= 0) return
val osmosisAddress = accountAddress.toOsmosisAddress() ?: return
val nobleAddress = accountAddress.toNobleAddress() ?: return
val osmosisChainId = helper.configs.osmosisChainId() ?: return
val osmosisChainId = helper.configs.osmosisChainId()
val nobleChainId = helper.configs.nobleChainId()
val neutronChainId = helper.configs.neutronChainId()
val fromChain = helper.environment.dydxChainId ?: return
val fromToken = helper.environment.tokens["usdc"]?.denom ?: return
val fromAmountString = helper.parser.asString(fromAmount) ?: return
Expand All @@ -1034,9 +1033,9 @@ internal class OnboardingSupervisor(
"dest_asset_chain_id" to toChain,
"chain_ids_to_addresses" to mapOf(
fromChain to accountAddress,
osmosisChainId to osmosisAddress,
nobleChainId to nobleAddress,
"neutron-1" to accountAddress.toNeutronAddress(),
osmosisChainId to accountAddress.toOsmosisAddress(),
nobleChainId to accountAddress.toNobleAddress(),
neutronChainId to accountAddress.toNeutronAddress(),
toChain to toAddress,
),
"allow_multi_tx" to true,
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 @@ -17,3 +17,6 @@ internal const val MAX_LEVERAGE_BUFFER_PERCENT = 0.98
// Order flags
internal const val SHORT_TERM_ORDER_FLAGS = 0
internal const val CONDITIONAL_ORDER_FLAGS = 32

// Asset Constants
internal const val NATIVE_TOKEN_DEFAULT_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,96 @@ class SkipRouteProcessorTests {
assertEquals(expected, result)
}

/**
* Tests a CCTP withdrawal initiated from the cctpToNobleSkip method
* This payload is used by the chain transaction method WithdrawToNobleIBC
* This processes a Dydx -> Noble CCTP transaction
*/
@Test
fun testReceivedCCTPDydxToNoble() {
val payload = skipRouteMock.payloadCCTPDydxToNoble
val result = skipRouteProcessor.received(existing = mapOf(), payload = templateToJson(payload), decimals = 6.0)
val jsonEncoder = JsonEncoder()
val expectedMsg = mapOf(
"sourcePort" to "transfer",
"sourceChannel" to "channel-0",
"token" to mapOf(
"denom" to "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"amount" to "10996029",
),
"sender" to "dydx1nhzuazjhyfu474er6v4ey8zn6wa5fy6g2dgp7s",
"receiver" to "noble1nhzuazjhyfu474er6v4ey8zn6wa5fy6gthndxf",
"timeoutHeight" to mapOf<String, Any>(),
"timeoutTimestamp" to 1718308711061386287,
)
val expectedData = jsonEncoder.encode(
mapOf(
"msg" to expectedMsg,
"value" to expectedMsg,
"msgTypeUrl" to "/ibc.applications.transfer.v1.MsgTransfer",
"typeUrl" to "/ibc.applications.transfer.v1.MsgTransfer",
),
)
val expected = mapOf(
"toAmountUSD" to 11.01,
"toAmount" to 10.996029,
"slippage" to "1",
"requestPayload" to mapOf(
"fromChainId" to "dydx-mainnet-1",
"fromAddress" to "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"toChainId" to "noble-1",
"toAddress" to "uusdc",
"data" to expectedData,
),
)
assertEquals(expected, result)
}

/**
* Tests a CCTP autosweep from the transferNobleBalance method
* This payload is used by the chain transaction method sendNobleIBC
* This processes a Noble -> Dydx CCTP transaction
*/
@Test
fun testReceivedCCTPNobleToDydx() {
val payload = skipRouteMock.payloadCCTPNobleToDydx
val result = skipRouteProcessor.received(existing = mapOf(), payload = templateToJson(payload), decimals = 6.0)
val jsonEncoder = JsonEncoder()
val expectedMsg = mapOf(
"sourcePort" to "transfer",
"sourceChannel" to "channel-33",
"token" to mapOf(
"denom" to "uusdc",
"amount" to "5884",
),
"sender" to "noble1nhzuazjhyfu474er6v4ey8zn6wa5fy6gthndxf",
"receiver" to "dydx1nhzuazjhyfu474er6v4ey8zn6wa5fy6g2dgp7s",
"timeoutHeight" to mapOf<String, Any>(),
"timeoutTimestamp" to 1718318348813666048,
)
val expectedData = jsonEncoder.encode(
mapOf(
"msg" to expectedMsg,
"value" to expectedMsg,
"msgTypeUrl" to "/ibc.applications.transfer.v1.MsgTransfer",
"typeUrl" to "/ibc.applications.transfer.v1.MsgTransfer",
),
)
val expected = mapOf(
"toAmountUSD" to 0.01,
"toAmount" to 0.005884,
"slippage" to "1",
"requestPayload" to mapOf(
"fromChainId" to "noble-1",
"fromAddress" to "uusdc",
"toChainId" to "dydx-mainnet-1",
"toAddress" to "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"data" to expectedData,
),
)
assertEquals(expected, result)
}

@Test
fun testReceivedError() {
val payload = skipRouteMock.payloadError
Expand Down
Loading