Skip to content

Commit 0a7b696

Browse files
committed
feat: update give/grab flow to passthrough VerifiedExchangeData
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent fda941c commit 0a7b696

16 files changed

Lines changed: 150 additions & 33 deletions

File tree

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/internal/bill/BillController.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.flipcash.app.core.internal.bill
22

33
import com.flipcash.app.core.bill.BillState
4+
import com.flipcash.services.user.UserManager
45
import com.getcode.opencode.model.accounts.AccountCluster
56
import com.getcode.opencode.managers.BillTransactionManager
67
import com.getcode.opencode.model.accounts.GiftCardAccount
@@ -17,6 +18,7 @@ import javax.inject.Singleton
1718
@Singleton
1819
class BillController @Inject constructor(
1920
private val transactionManager: BillTransactionManager,
21+
private val userManager: UserManager,
2022
) {
2123
private val _state = MutableStateFlow(BillState.Default)
2224
val state: StateFlow<BillState>
@@ -41,7 +43,16 @@ class BillController @Inject constructor(
4143
onGrabbed: suspend (LocalFiat) -> Unit,
4244
onTimeout: () -> Unit,
4345
onError: (Throwable) -> Unit,
44-
) = transactionManager.awaitGrabFromRecipient(token, amount, owner, present, onGrabbed, onTimeout, onError)
46+
) = transactionManager.awaitGrabFromRecipient(
47+
token = token,
48+
amount = amount,
49+
owner = owner,
50+
billExchangeDataTimeout = userManager.userFlags?.billExchangeDataTimeout,
51+
present = present,
52+
onGrabbed = onGrabbed,
53+
onTimeout = onTimeout,
54+
onError = onError,
55+
)
4556

4657
fun cancelAwaitForGrab() = transactionManager.cancelAwaitForGrab()
4758

definitions/flipcash/protos/src/main/proto/account/v1/flipcash_account_service.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ option go_package = "github.com/code-payments/flipcash2-protobuf-api/generated/g
44
option java_package = "com.codeinc.flipcash.gen.account.v1";
55
option objc_class_prefix = "FPBAccountV1";
66
import "common/v1/common.proto";
7+
import "google/protobuf/duration.proto";
78
import "google/protobuf/timestamp.proto";
89

910
service Account {
@@ -105,4 +106,6 @@ message UserFlags {
105106
// The minumum build number for this user. If their build number is less than the
106107
// provided value, client should show a forced upgrade screen.
107108
uint32 min_build_number = 6;
109+
// Exchange data timeout for sequential give/grabs for bills
110+
google.protobuf.Duration bill_exchange_data_timeout = 7;
108111
}

definitions/opencode/protos/src/main/proto/messaging/v1/messaging_service.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ option go_package = "github.com/code-payments/ocp-protobuf-api/generated/go/mess
44
option java_package = "com.codeinc.opencode.gen.messaging.v1";
55
option objc_class_prefix = "CPBMessagingV1";
66
import "common/v1/model.proto";
7+
import "transaction/v1/transaction_service.proto";
78

89
service Messaging {
910
// OpenMessageStream opens a stream of messages. Messages are routed using the
@@ -147,6 +148,10 @@ message RequestToGrabBill {
147148
message RequestToGiveBill {
148149
// The mint that the bill will be received in
149150
common.v1.SolanaAccountId mint = 1;
151+
// The validated exchange data that was used to compute the fiat value of the give
152+
// to support subsequent gives. Clients should be aware of timeouts and dismiss a
153+
// bill if the threshold is met.
154+
transaction.v1.VerifiedExchangeData exchange_data = 2;
150155
}
151156
message Message {
152157
// MessageId is the Id of the message. This ID is generated by the

services/flipcash/src/main/kotlin/com/flipcash/services/internal/domain/UserFlagsMapper.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import com.flipcash.services.internal.model.account.UserFlags
66
import com.flipcash.services.internal.model.thirdparty.OnRampProvider
77
import com.flipcash.services.internal.model.thirdparty.OnRampType
88
import javax.inject.Inject
9+
import kotlin.time.DurationUnit
10+
import kotlin.time.toDuration
911

1012
internal class UserFlagsMapper @Inject constructor():
1113
Mapper<FlipcashAccountService.UserFlags, UserFlags> {
@@ -16,7 +18,8 @@ internal class UserFlagsMapper @Inject constructor():
1618
requiresIapForRegistration = from.requiresIapForRegistration,
1719
preferredOnRampProvider = from.preferredOnRampProvider.toDomain().takeIf { it != OnRampProvider.Unknown },
1820
supportedOnRampProviders = from.supportedOnRampProvidersList.map { it.toDomain() },
19-
minimumVersion = from.minBuildNumber
21+
minimumVersion = from.minBuildNumber,
22+
billExchangeDataTimeout = from.billExchangeDataTimeout.seconds.toDuration(DurationUnit.SECONDS),
2023
)
2124
}
2225
}

services/flipcash/src/main/kotlin/com/flipcash/services/internal/model/account/UserFlags.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.flipcash.services.internal.model.account
22

33
import com.flipcash.services.internal.model.thirdparty.OnRampProvider
4+
import kotlin.time.Duration
45

56
data class UserFlags(
67
val isStaff: Boolean,
@@ -9,4 +10,5 @@ data class UserFlags(
910
val preferredOnRampProvider: OnRampProvider?,
1011
val supportedOnRampProviders: List<OnRampProvider>,
1112
val minimumVersion: Int?,
13+
val billExchangeDataTimeout: Duration?,
1214
)

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

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

33
import com.codeinc.opencode.gen.messaging.v1.MessagingService
4+
import com.codeinc.opencode.gen.messaging.v1.exchangeDataOrNull
5+
import com.codeinc.opencode.gen.transaction.v1.TransactionService
6+
import com.codeinc.opencode.gen.transaction.v1.exchangeData
47
import com.getcode.ed25519.Ed25519.KeyPair
58
import com.getcode.opencode.internal.extensions.toPublicKey
9+
import com.getcode.opencode.internal.manager.VerifiedState
10+
import com.getcode.opencode.internal.network.extensions.asProtobufExchangeData
611
import com.getcode.opencode.internal.network.extensions.asSolanaAccountId
712
import com.getcode.opencode.internal.network.extensions.toMint
13+
import com.getcode.opencode.internal.network.extensions.toModel
814
import com.getcode.opencode.internal.network.extensions.toPublicKey
915
import com.getcode.opencode.internal.network.services.OcpMessageStreamReference
1016
import com.getcode.opencode.model.core.OpenCodePayload
11-
import com.getcode.opencode.model.financial.Token
17+
import com.getcode.opencode.model.transactions.ExchangeData
1218
import com.getcode.opencode.model.transactions.GiveRequest
1319
import com.getcode.opencode.model.transactions.GrabRequest
1420
import com.getcode.opencode.repositories.MessagingRepository
15-
import com.getcode.opencode.utils.flowInterval
1621
import com.getcode.solana.keys.Mint
1722
import com.getcode.solana.keys.PublicKey
1823
import com.getcode.utils.TraceType
1924
import com.getcode.utils.trace
2025
import kotlinx.coroutines.CoroutineScope
2126
import kotlinx.coroutines.Dispatchers
2227
import kotlinx.coroutines.delay
23-
import kotlinx.coroutines.flow.catch
24-
import kotlinx.coroutines.flow.filter
25-
import kotlinx.coroutines.flow.firstOrNull
26-
import kotlinx.coroutines.flow.fold
27-
import kotlinx.coroutines.flow.map
28-
import kotlinx.coroutines.flow.mapNotNull
29-
import kotlinx.coroutines.flow.takeWhile
3028
import kotlinx.coroutines.launch
3129
import kotlinx.coroutines.suspendCancellableCoroutine
3230
import kotlinx.coroutines.sync.Mutex
3331
import kotlinx.coroutines.sync.withLock
3432
import kotlinx.coroutines.withContext
35-
import java.util.concurrent.atomic.AtomicBoolean
36-
import java.util.concurrent.atomic.AtomicInteger
3733
import javax.inject.Inject
3834
import javax.inject.Singleton
3935
import kotlin.coroutines.resume
@@ -159,7 +155,7 @@ class MessagingController @Inject constructor(
159155

160156
suspend fun pollForGiveRequest(
161157
rendezvous: KeyPair,
162-
): Result<Pair<PublicKey, Mint>>{
158+
): Result<GiveRequest>{
163159

164160
return repository.pollMessages(rendezvous)
165161
.map { messages ->
@@ -170,17 +166,25 @@ class MessagingController @Inject constructor(
170166
}.mapCatching { messages ->
171167
val message = messages.firstOrNull() ?: throw IllegalStateException("No message found")
172168
val mint = message.requestToGiveBill.mint.toMint()
169+
val exchangeData = message.requestToGiveBill.exchangeData.toModel()
173170

174-
(message.id.toPublicKey() to mint)
171+
GiveRequest(
172+
messageId = message.id.toPublicKey(),
173+
mint = mint,
174+
exchangeData = exchangeData
175+
)
175176
}
176177
}
177178

178179
suspend fun sendRequestToGiveBill(
179180
tokenMint: Mint,
180181
rendezvous: KeyPair,
182+
exchangeRate: ExchangeData.Verified,
181183
): Result<PublicKey> {
182184
val paymentRequest = MessagingService.RequestToGiveBill.newBuilder()
183185
.setMint(tokenMint.asSolanaAccountId())
186+
.setExchangeData(exchangeRate.asProtobufExchangeData())
187+
.build()
184188

185189
val message = MessagingService.Message.newBuilder()
186190
.setRequestToGiveBill(paymentRequest)

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.getcode.opencode.model.financial.Limits
2020
import com.getcode.opencode.model.financial.LocalFiat
2121
import com.getcode.opencode.model.financial.Token
2222
import com.getcode.opencode.model.transactions.AirdropType
23+
import com.getcode.opencode.model.transactions.ExchangeData
2324
import com.getcode.opencode.model.transactions.SwapFundingSource
2425
import com.getcode.opencode.model.transactions.SwapMetadata
2526
import com.getcode.opencode.model.transactions.SwapRequest
@@ -120,17 +121,15 @@ class TransactionController @Inject constructor(
120121
destination: PublicKey,
121122
rendezvous: PublicKey,
122123
scope: CoroutineScope = this.scope,
124+
exchangeData: ExchangeData.Verified,
123125
): Result<IntentType> {
124-
val verifiedState = verifiedStateManager.getVerifiedStateFor(amount.rate.currency, mint)
125-
?: return Result.failure(SwapError.Other(IllegalStateException("No verified state found")))
126-
127126
val intent = IntentTransfer.create(
128127
amount = amount,
129128
mint = mint,
130129
sourceCluster = source,
131130
destination = destination,
132131
rendezvous = rendezvous,
133-
verifiedState = verifiedState,
132+
exchangeData = exchangeData,
134133
)
135134

136135
return submitIntent(scope, intent, source.authority.keyPair)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.getcode.opencode.internal.extensions
2+
3+
import com.getcode.opencode.internal.manager.VerifiedState
4+
import com.getcode.opencode.model.financial.LocalFiat
5+
import com.getcode.opencode.model.transactions.ExchangeData
6+
import com.getcode.solana.keys.Mint
7+
import com.getcode.utils.TraceType
8+
import com.getcode.utils.trace
9+
import kotlin.time.Duration
10+
11+
fun VerifiedState.exchangeDataFor(
12+
amount: LocalFiat,
13+
mint: Mint,
14+
billExchangeDataTimeout: Duration?
15+
): ExchangeData.Verified? {
16+
if (billExchangeDataTimeout == null) {
17+
trace(
18+
tag = "Transactor::Give",
19+
message = "No bill exchange data timeout provided. This bill is not giveable.",
20+
type = TraceType.Error
21+
)
22+
return null
23+
}
24+
return ExchangeData.Verified(
25+
mint = mint,
26+
nativeAmount = amount.nativeAmount.decimalValue,
27+
quarks = amount.underlyingTokenAmount.quarks,
28+
verifiedState = this,
29+
)
30+
}

services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/api/intents/IntentTransfer.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.getcode.opencode.internal.network.api.intents
22

33
import com.codeinc.opencode.gen.transaction.v1.TransactionService
4+
import com.codeinc.opencode.gen.transaction.v1.exchangeData
45
import com.getcode.opencode.internal.manager.VerifiedState
56
import com.getcode.opencode.internal.model.VerifiedResponseData
67
import com.getcode.opencode.internal.network.api.intents.actions.ActionPublicTransfer
@@ -9,6 +10,7 @@ import com.getcode.opencode.internal.network.extensions.asSolanaAccountId
910
import com.getcode.opencode.internal.network.extensions.asExchangeData
1011
import com.getcode.opencode.internal.network.extensions.asProtobufMetadata
1112
import com.getcode.opencode.model.financial.LocalFiat
13+
import com.getcode.opencode.model.transactions.ExchangeData
1214
import com.getcode.opencode.model.transactions.TransactionMetadata
1315
import com.getcode.opencode.solana.intents.ActionGroup
1416
import com.getcode.opencode.solana.intents.IntentType
@@ -31,7 +33,7 @@ internal class IntentTransfer(
3133
sourceCluster: AccountCluster,
3234
destination: PublicKey,
3335
rendezvous: PublicKey,
34-
verifiedState: VerifiedState,
36+
exchangeData: ExchangeData.Verified,
3537
): IntentTransfer {
3638
val transfer = ActionPublicTransfer.newInstance(
3739
owner = sourceCluster.authority.keyPair,
@@ -50,7 +52,7 @@ internal class IntentTransfer(
5052
mint = mint,
5153
isRemoteSend = false,
5254
isWithdrawal = false,
53-
verifiedState = verifiedState,
55+
exchangeData = exchangeData,
5456
),
5557
actionGroup = ActionGroup().apply {
5658
actions = listOf(transfer)

services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/extensions/LocalToProtobuf.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ internal fun TransferRequest.asProtobufMessage(): MessagingService.Message {
225225
MessagingService.RequestToGiveBill
226226
.newBuilder()
227227
.setMint(mint.asSolanaAccountId())
228+
.setExchangeData(exchangeData.asProtobufExchangeData())
228229
).build()
229230

230231
is GrabRequest -> MessagingService.Message

0 commit comments

Comments
 (0)