Skip to content

Commit 4087121

Browse files
committed
chore(ocp): include balance capping in LocalFiat#valueExchangeIn
additionally add some debug logging that can be turned on when needed Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 0e4ae9a commit 4087121

4 files changed

Lines changed: 60 additions & 18 deletions

File tree

apps/flipcash/features/cash/src/main/kotlin/com/flipcash/app/cash/internal/CashScreenViewModel.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,11 @@ internal class CashScreenViewModel @Inject constructor(
149149
if (rate.currency != CurrencyCode.USD) {
150150
exchange.fetchRatesIfNeeded()
151151
}
152-
val token = stateFlow.value.token!!.token
152+
val (token, balance) = stateFlow.value.token!!
153153
val amountFiat = LocalFiat.valueExchangeIn(
154154
amount = Fiat(amount, rate.currency),
155155
token = token,
156+
balance = balance.underlyingTokenAmount,
156157
rate = rate,
157158
)
158159

@@ -294,7 +295,7 @@ internal class CashScreenViewModel @Inject constructor(
294295
.filter { !(checkBalanceLimit() || checkSendLimit()) }
295296
.onEach { data ->
296297
dispatchEvent(Event.UpdateLoadingState(loading = true))
297-
val token = stateFlow.value.token!!.token
298+
val (token, balance) = stateFlow.value.token!!
298299
val rate = exchange.entryRate
299300
// if we are USD we can skip the rate fetch since its 1:1
300301
if (token.address == Mint.usdc) {
@@ -306,6 +307,7 @@ internal class CashScreenViewModel @Inject constructor(
306307
val amountFiat = LocalFiat.valueExchangeIn(
307308
amount = Fiat(data.amountData.amount, rate.currency),
308309
token = token,
310+
balance = balance.underlyingTokenAmount,
309311
rate = rate,
310312
)
311313

apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/WithdrawalViewModel.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ internal class WithdrawalViewModel @Inject constructor(
279279
val amountFiat = LocalFiat.valueExchangeIn(
280280
amount = Fiat(data.amountData.amount, rate.currency),
281281
token = token,
282+
balance = stateFlow.value.token!!.balance,
282283
rate = rate,
283284
)
284285

@@ -419,7 +420,8 @@ internal class WithdrawalViewModel @Inject constructor(
419420
LocalFiat.valueExchangeIn(
420421
fee,
421422
token = token,
422-
rate = exchange.rateToUsd(CurrencyCode.USD)!!
423+
balance = stateFlow.value.token!!.balance,
424+
rate = exchange.rateToUsd(CurrencyCode.USD)!!,
423425
).underlyingTokenAmount
424426
}
425427

services/opencode/src/main/kotlin/com/getcode/opencode/model/financial/Fiat.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ data class Fiat(
113113
currencyCode = rate.currency
114114
)
115115

116+
fun convertingToUsdIfNeeded(rate: Rate): Fiat {
117+
return if (rate.currency != CurrencyCode.USD) {
118+
convertingTo(Rate(1 / rate.fx, rate.currency))
119+
} else {
120+
this
121+
}
122+
}
123+
116124
// Comparable implementation
117125
override fun compareTo(other: Fiat): Int = this.quarks.compareTo(other.quarks)
118126

@@ -208,4 +216,6 @@ fun Number.toFiat(currencyCode: CurrencyCode = CurrencyCode.USD): Fiat = when (t
208216
is Long -> Fiat(this, currencyCode)
209217
is Double -> Fiat(this, currencyCode)
210218
else -> throw IllegalArgumentException("Unsupported number type")
211-
}
219+
}
220+
221+
fun min(a: Fiat, b: Fiat): Fiat = if (a < b) a else b

services/opencode/src/main/kotlin/com/getcode/opencode/model/financial/LocalFiat.kt

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,14 @@ data class LocalFiat(
6363
fun valueExchangeIn(
6464
amount: Fiat,
6565
token: Token,
66+
balance: Fiat = Fiat.Zero,
6667
rate: Rate,
68+
debug: Boolean = false,
6769
): LocalFiat {
68-
val usdValue = if (rate.currency != CurrencyCode.USD) {
69-
amount.convertingTo(Rate(1 / rate.fx, rate.currency))
70-
} else {
71-
amount
72-
}
70+
val usdValue = amount.convertingToUsdIfNeeded(rate)
7371

7472
if (token.address == Mint.usdc) {
75-
// this doesn't need a calcuated value exchange since we are USDC
73+
// this doesn't need a calculated value exchange since we are USDC
7674
return if (rate.currency != CurrencyCode.USD) {
7775
LocalFiat(
7876
usdc = usdValue,
@@ -83,25 +81,38 @@ data class LocalFiat(
8381
}
8482
}
8583

84+
val circulatingSupply = token.launchpadMetadata?.currentCirculatingSupplyQuarks ?: 0
8685

86+
val cappedValue = min(balance, usdValue)
8787

8888
// determine quarks to exchange for the desired amount
8989
val quarks = Estimator.valueExchangeAsQuarks(
90-
valueInQuarks = usdValue.quarks,
91-
currentSupplyInQuarks = token.launchpadMetadata?.currentCirculatingSupplyQuarks
92-
?: 0,
90+
valueInQuarks = cappedValue.quarks,
91+
currentSupplyInQuarks = circulatingSupply,
9392
mintDecimals = 6, // usdc is 6 decimals
9493
).getOrThrow()
9594

9695
// determine the "full units" of the token being exchanged
9796
val units = quarks.units()
9897
// determine the exchange rate (native amount / units of token) (USD based)
99-
val usdFx = BigDecimal(usdValue.decimalValue).divideWithHighPrecision(units)
98+
val usdFx = BigDecimal(cappedValue.decimalValue).divideWithHighPrecision(units)
10099

101100
// determine the relative exchange rate of the token in the currency selected
102101
// USD is a 1:1 fx so we can be blind here
103102
val fx = rate.fx * usdFx.toDouble()
104103

104+
if (debug) {
105+
println("requested quarks: ${usdValue.quarks * 1_000_000}")
106+
println("balance quarks: ${balance.quarks * 1_000_000}")
107+
println("capped quarks: ${cappedValue.quarks * 1_000_000}")
108+
println("circulating supply: $circulatingSupply")
109+
println("calculated quarks: $quarks")
110+
println("units: $units")
111+
println("fx: $fx")
112+
val sellAmount = Fiat.tokenBalance(quarks.toLong(), token = token)
113+
println("sellAmount: ${sellAmount.formatted(formatting = Fiat.Formatting.Length(10))}")
114+
}
115+
105116
return LocalFiat(
106117
underlyingTokenAmount = Fiat(quarks.toLong(), CurrencyCode.USD),
107118
nativeAmount = amount,
@@ -126,9 +137,26 @@ fun Iterable<LocalFiat>.sum(): LocalFiat {
126137
acc
127138
}
128139

129-
base.copy(
130-
underlyingTokenAmount = base.underlyingTokenAmount + localFiat.underlyingTokenAmount,
131-
nativeAmount = base.nativeAmount + localFiat.nativeAmount,
132-
)
140+
base + localFiat
133141
}
134142
}
143+
144+
operator fun LocalFiat.minus(other: LocalFiat): LocalFiat {
145+
if (mint != other.mint) throw IllegalArgumentException("Mint is mismatched")
146+
if (rate.currency != other.rate.currency) throw IllegalArgumentException("Currency is mismatched")
147+
148+
return copy(
149+
underlyingTokenAmount = underlyingTokenAmount - other.underlyingTokenAmount,
150+
nativeAmount = nativeAmount - other.nativeAmount
151+
)
152+
}
153+
154+
operator fun LocalFiat.plus(other: LocalFiat): LocalFiat {
155+
if (mint != other.mint) throw IllegalArgumentException("Mint is mismatched")
156+
if (rate.currency != other.rate.currency) throw IllegalArgumentException("Currency is mismatched")
157+
158+
return copy(
159+
underlyingTokenAmount = underlyingTokenAmount + other.underlyingTokenAmount,
160+
nativeAmount = nativeAmount + other.nativeAmount
161+
)
162+
}

0 commit comments

Comments
 (0)