Updated on 2026-08-14
This commit is contained in:
parent
c21ef23449
commit
0ad1913175
8 changed files with 275 additions and 34 deletions
|
|
@ -8,7 +8,9 @@ import java.util.Locale
|
|||
* exchange rate for a swap pair ([REDACTED_TASK_KEY]).
|
||||
*
|
||||
* Categories used by the rules:
|
||||
* - **Stable** — a [CryptoCurrency.Token] whose symbol is in [STABLECOIN_RANKS].
|
||||
* - **Stable** — a [CryptoCurrency.Token] whose normalized symbol is in [STABLECOIN_RANKS].
|
||||
* The symbol is normalized to uppercase and the suffix after `.` is stripped, so bridged
|
||||
* variants (`USDC.E`, `USDT.e`, etc.) are matched against their underlying asset.
|
||||
* - **Coin** — a [CryptoCurrency.Coin] (any native coin: BTC, ETH, SOL, TRX, ...).
|
||||
* - Anything else (a [CryptoCurrency.Token] outside the stable list) falls into the default
|
||||
* branch and is treated as a regular token.
|
||||
|
|
@ -43,8 +45,8 @@ internal object SwapRateDirectionResolver {
|
|||
}
|
||||
|
||||
private fun resolveStableToStable(from: CryptoCurrency, to: CryptoCurrency): SwapRateDirection {
|
||||
val fromRank = stableRank(from.symbol.uppercaseRoot())
|
||||
val toRank = stableRank(to.symbol.uppercaseRoot())
|
||||
val fromRank = stableRank(from.symbol.normalizeStableSymbol())
|
||||
val toRank = stableRank(to.symbol.normalizeStableSymbol())
|
||||
return if (fromRank <= toRank) {
|
||||
SwapRateDirection(base = from, quote = to)
|
||||
} else {
|
||||
|
|
@ -77,7 +79,7 @@ internal object SwapRateDirectionResolver {
|
|||
private fun stableRank(symbol: String): Int = STABLECOIN_RANKS[symbol] ?: Int.MAX_VALUE
|
||||
|
||||
private fun CryptoCurrency.isStable(): Boolean {
|
||||
return this is CryptoCurrency.Token && STABLECOIN_RANKS.containsKey(symbol.uppercaseRoot())
|
||||
return this is CryptoCurrency.Token && STABLECOIN_RANKS.containsKey(symbol.normalizeStableSymbol())
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.isCoin(): Boolean = this is CryptoCurrency.Coin
|
||||
|
|
@ -85,6 +87,12 @@ internal object SwapRateDirectionResolver {
|
|||
private fun String.isBtcOrEth(): Boolean = this == BTC_SYMBOL || this == ETH_SYMBOL
|
||||
|
||||
private fun String.uppercaseRoot(): String = uppercase(Locale.ROOT)
|
||||
|
||||
/**
|
||||
* Drops bridge/wrapped suffix (e.g. `USDC.E` → `USDC`, `USDT.e` → `USDT`) before stable lookup.
|
||||
* Bridged variants share the underlying asset's rank.
|
||||
*/
|
||||
private fun String.normalizeStableSymbol(): String = substringBefore('.').uppercaseRoot()
|
||||
}
|
||||
|
||||
internal data class SwapRateDirection(val base: CryptoCurrency, val quote: CryptoCurrency)
|
||||
|
|
@ -172,6 +172,66 @@ internal class SwapRateDirectionResolverTest {
|
|||
assertThat(result).isEqualTo(SwapRateDirection(base = usdt, quote = usdc))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN stable usdt and bridged usdc_e WHEN resolve THEN base is usdt`() {
|
||||
val usdt = stable(symbol = "USDT")
|
||||
val usdcE = stable(symbol = "USDC.E")
|
||||
|
||||
val result = SwapRateDirectionResolver.resolve(from = usdt, to = usdcE)
|
||||
|
||||
assertThat(result).isEqualTo(SwapRateDirection(base = usdt, quote = usdcE))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN bridged usdc_e and stable usdt WHEN resolve THEN base is usdt`() {
|
||||
val usdcE = stable(symbol = "USDC.E")
|
||||
val usdt = stable(symbol = "USDT")
|
||||
|
||||
val result = SwapRateDirectionResolver.resolve(from = usdcE, to = usdt)
|
||||
|
||||
assertThat(result).isEqualTo(SwapRateDirection(base = usdt, quote = usdcE))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN coin sol and bridged usdc_e WHEN resolve THEN base is coin`() {
|
||||
val sol = coin(symbol = "SOL")
|
||||
val usdcE = stable(symbol = "USDC.E")
|
||||
|
||||
val result = SwapRateDirectionResolver.resolve(from = sol, to = usdcE)
|
||||
|
||||
assertThat(result).isEqualTo(SwapRateDirection(base = sol, quote = usdcE))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN bridged usdc_e and coin sol WHEN resolve THEN base is coin`() {
|
||||
val usdcE = stable(symbol = "USDC.E")
|
||||
val sol = coin(symbol = "SOL")
|
||||
|
||||
val result = SwapRateDirectionResolver.resolve(from = usdcE, to = sol)
|
||||
|
||||
assertThat(result).isEqualTo(SwapRateDirection(base = sol, quote = usdcE))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN lowercase bridged usdt_e and stable usdc WHEN resolve THEN base is usdt`() {
|
||||
val usdtE = stable(symbol = "usdt.e")
|
||||
val usdc = stable(symbol = "USDC")
|
||||
|
||||
val result = SwapRateDirectionResolver.resolve(from = usdtE, to = usdc)
|
||||
|
||||
assertThat(result).isEqualTo(SwapRateDirection(base = usdtE, quote = usdc))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN bridged dai_e and bridged usdc_e WHEN resolve THEN base is usdc`() {
|
||||
val daiE = stable(symbol = "DAI.E")
|
||||
val usdcE = stable(symbol = "USDC.E")
|
||||
|
||||
val result = SwapRateDirectionResolver.resolve(from = daiE, to = usdcE)
|
||||
|
||||
assertThat(result).isEqualTo(SwapRateDirection(base = usdcE, quote = daiE))
|
||||
}
|
||||
|
||||
private fun coin(symbol: String): CryptoCurrency = mockk<CryptoCurrency.Coin> {
|
||||
every { this@mockk.symbol } returns symbol
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue