Skip to content

Commit e0d8293

Browse files
committed
fix: correct value exchange math at low amounts; correct balance mutations
Signed-off-by: Brandon McAnsh <[email protected]>
1 parent ef9ac1f commit e0d8293

File tree

6 files changed

+45
-24
lines changed

6 files changed

+45
-24
lines changed

libs/currency-math/src/main/kotlin/com/flipcash/libs/currency/math/Units.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ package com.flipcash.libs.currency.math
33
import com.flipcash.libs.currency.math.internal.DefaultMintQuarksPerUnit
44
import java.math.BigDecimal
55

6-
fun BigDecimal.units(): BigDecimal = this.divide(BigDecimal(DefaultMintQuarksPerUnit), mc)
6+
fun BigDecimal.units(): BigDecimal = this.divide(BigDecimal(DefaultMintQuarksPerUnit), mc)
7+
fun BigDecimal.divideWithHighPrecision(other: BigDecimal): BigDecimal = this.divide(other, mc)

services/opencode/src/main/kotlin/com/getcode/opencode/ControllerFactory.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ object ControllerFactory {
4848
accountController = createAccountController(context, config),
4949
currencyController = createCurrencyController(context, config),
5050
networkObserver = NetworkFactory.createNetworkObserver(context),
51+
exchange = ExchangeFactory.createOpenCodeExchange(context, config),
5152
)
5253
}
5354
}

services/opencode/src/main/kotlin/com/getcode/opencode/controllers/TokenController.kt

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.getcode.opencode.controllers
22

3+
import com.getcode.opencode.exchange.Exchange
34
import com.getcode.opencode.model.accounts.AccountCluster
45
import com.getcode.opencode.model.accounts.AccountFilter
56
import com.getcode.opencode.model.accounts.AccountType
@@ -37,6 +38,7 @@ class TokenController @Inject constructor(
3738
private val accountController: AccountController,
3839
private val currencyController: CurrencyController,
3940
private val networkObserver: NetworkConnectivityListener,
41+
private val exchange: Exchange,
4042
) {
4143
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
4244

@@ -87,24 +89,25 @@ class TokenController @Inject constructor(
8789
return mintBalances.map { it[tokenAddress] ?: Fiat.Zero }
8890
}
8991

90-
suspend fun add(token: Mint, fiat: LocalFiat) {
91-
val balance = mintBalances.value[token] ?: Fiat.Zero
92+
private suspend fun modifyBalance(token: Token, operation: (Fiat) -> Fiat) {
93+
val balance = mintBalances.value[token.address] ?: Fiat.Zero
9294
if (balance.doubleValue == 0.0) {
93-
// attempt to fetch prior to append
94-
fetchBalanceForToken(token)
95+
// attempt to fetch prior to modifying balance
96+
fetchBalanceForToken(token.address)
9597
} else {
96-
mintBalances.update { it + (token to (balance + fiat.nativeAmount)) }
98+
val updatedBalance = operation(balance)
99+
mintBalances.update { it + (token.address to updatedBalance) }
97100
}
98101
}
99102

100-
suspend fun subtract(token: Mint, fiat: LocalFiat) {
101-
val balance = mintBalances.value[token] ?: Fiat.Zero
102-
if (balance.doubleValue == 0.0) {
103-
// attempt to fetch prior to append
104-
fetchBalanceForToken(token)
105-
} else {
106-
mintBalances.update { it + (token to (balance - fiat.nativeAmount)) }
107-
}
103+
suspend fun add(token: Token, fiat: LocalFiat) {
104+
val balanceAdditionAmount = fiat.nativeAmount.convertingTo(exchange.rateToUsd(fiat.rate.currency)!!)
105+
modifyBalance(token) { it + balanceAdditionAmount }
106+
}
107+
108+
suspend fun subtract(token: Token, fiat: LocalFiat) {
109+
val balanceReductionAmount = fiat.nativeAmount.convertingTo(exchange.rateToUsd(fiat.rate.currency)!!)
110+
modifyBalance(token) { it - balanceReductionAmount }
108111
}
109112

110113
suspend fun update() {

services/opencode/src/main/kotlin/com/getcode/opencode/internal/exchange/OpenCodeExchange.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ internal class OpenCodeExchange @Inject constructor(
190190
trace(
191191
tag = "Background",
192192
message = "Updated the local currency: $balanceCurrency, " +
193-
"Staleness ${System.currentTimeMillis() - rates.dateMillis} ms, " +
193+
"Staleness ${(System.currentTimeMillis() - rates.dateMillis).minutes} mins, " +
194194
"Date: ${Date(rates.dateMillis)}",
195195
type = TraceType.Process
196196
)

services/opencode/src/main/kotlin/com/getcode/opencode/managers/BillTransactionManager.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class BillTransactionManager @Inject constructor(
8181
.onSuccess {
8282
childScope.cancel()
8383
onGrabbed()
84-
tokenController.subtract(token.address, LocalFiat(it.exchangeData))
84+
tokenController.subtract(token, LocalFiat(it.exchangeData))
8585
transactionController.updateLimits(owner, force = true)
8686
}.onFailure {
8787
onError(it)
@@ -135,7 +135,7 @@ class BillTransactionManager @Inject constructor(
135135
message = "Grabbed ${amount.nativeAmount.formatted()} of ${token.symbol} from sender"
136136
)
137137
onGrabbed(token, amount)
138-
tokenController.add(mint, amount)
138+
tokenController.add(token, amount)
139139
transactionController.updateLimits(owner, force = true)
140140
}.onFailure {
141141
onError(it)
@@ -163,7 +163,7 @@ class BillTransactionManager @Inject constructor(
163163
transactor.start()
164164
.onSuccess {
165165
onFunded(amount)
166-
tokenController.subtract(token.address, amount)
166+
tokenController.subtract(token, amount)
167167
transactionController.updateLimits(owner, force = true)
168168
}.onFailure {
169169
ErrorUtils.handleError(it)
@@ -198,7 +198,7 @@ class BillTransactionManager @Inject constructor(
198198
receiveTransactor?.start(claimIfOwned)
199199
?.onSuccess { (token, amount) ->
200200
onReceived(token, amount)
201-
tokenController.add(token.address, amount)
201+
tokenController.add(token, amount)
202202
}?.onFailure {
203203
onError(it)
204204
transactor.dispose()

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package com.getcode.opencode.model.financial
22

33
import com.flipcash.libs.currency.math.Estimator
4+
import com.flipcash.libs.currency.math.divideWithHighPrecision
45
import com.flipcash.libs.currency.math.units
56
import com.getcode.opencode.model.transactions.ExchangeData
67
import com.getcode.solana.keys.Mint
8+
import com.getcode.utils.TraceType
9+
import com.getcode.utils.trace
710
import kotlinx.serialization.Serializable
11+
import java.math.BigDecimal
812
import javax.annotation.concurrent.Immutable
913

1014
typealias Usd = Fiat
@@ -80,11 +84,11 @@ data class LocalFiat(
8084
// determine the "full units" of the token being exchanged
8185
val units = estimatedTokens.units()
8286
// determine the exchange rate (native amount / units of token) (USD based)
83-
val usdFx = usdValue.decimalValue / units.toDouble()
87+
val usdFx = BigDecimal(usdValue.decimalValue).divideWithHighPrecision(units)
8488

8589
// determine the relative exchange rate of the token in the currency selected
8690
// USD is a 1:1 fx so we can be blind here
87-
val fx = rate.fx * usdFx
91+
val fx = rate.fx * usdFx.toDouble()
8892

8993
return LocalFiat(
9094
underlyingTokenAmount = Fiat(estimatedTokens.toLong(), CurrencyCode.USD),
@@ -98,9 +102,21 @@ data class LocalFiat(
98102

99103
fun Iterable<LocalFiat>.sum(): LocalFiat {
100104
return this.fold(LocalFiat.Zero) { acc, localFiat ->
101-
acc.copy(
102-
underlyingTokenAmount = acc.underlyingTokenAmount + localFiat.underlyingTokenAmount,
103-
nativeAmount = acc.nativeAmount + localFiat.nativeAmount,
105+
val base = if (acc == LocalFiat.Zero) {
106+
// update to the currency of the incoming localFiat
107+
LocalFiat(
108+
Fiat.Zero,
109+
Fiat.Zero.copy(currencyCode = localFiat.rate.currency),
110+
localFiat.rate,
111+
localFiat.mint
112+
)
113+
} else {
114+
acc
115+
}
116+
117+
base.copy(
118+
underlyingTokenAmount = base.underlyingTokenAmount + localFiat.underlyingTokenAmount,
119+
nativeAmount = base.nativeAmount + localFiat.nativeAmount,
104120
)
105121
}
106122
}

0 commit comments

Comments
 (0)