@@ -13,7 +13,8 @@ import com.getcode.opencode.providers.TokenMetadataProvider
1313import com.getcode.opencode.model.core.errors.SubmitIntentError
1414import com.getcode.utils.CodeServerError
1515import com.getcode.utils.NotifiableError
16- import com.getcode.utils.timedTraceSuspend
16+ import com.getcode.utils.payment.PaymentTraceRegistry
17+ import com.getcode.utils.payment.spanSuspendOrRun
1718import kotlinx.coroutines.CoroutineScope
1819import kotlinx.coroutines.cancel
1920
@@ -102,69 +103,59 @@ internal class GrabBillTransactor(
102103 private suspend fun handleMultiMintScan (
103104 ownerKey : AccountCluster ,
104105 data : OpenCodePayload
105- ): Result <TransactionMetadata .SendPublicPayment > = timedTraceSuspend(
106- message = " handleMultiMintScan" ,
107- tag = tag,
108- ) { onStep ->
106+ ): Result <TransactionMetadata .SendPublicPayment > {
107+ val trace = PaymentTraceRegistry .get(data.rendezvous.publicKey)
108+
109109 // 1. Wait for the give request from the sender so we can determine what mint we are operating on
110- val (messageId, giveRequestMint, exchangeData, mintMetadata) = messagingController.pollForGiveRequest(data.rendezvous)
111- .getOrNull()
112- ? : run {
113- onStep(" pollForGiveRequest" )
114- return @timedTraceSuspend logAndFail(GrabTransactorError .GiveRequestNotFound ())
115- }
116- onStep(" pollForGiveRequest" )
110+ val (messageId, giveRequestMint, exchangeData, mintMetadata) = trace.spanSuspendOrRun(" pollForGiveRequest" ) {
111+ messagingController.pollForGiveRequest(data.rendezvous).getOrNull()
112+ } ? : return logAndFail(GrabTransactorError .GiveRequestNotFound ())
117113
118114 // 2. Utilize the mint from the give request to get the Token metadata
119- val token = mintMetadata
120- ? : (tokenProvider.getTokenMetadata(giveRequestMint)
121- .getOrNull()?.token
122- ? : run {
123- onStep(" tokenMetadata" )
124- return @timedTraceSuspend logAndFail(GrabTransactorError .Other (message = " No token found for proposed mint" ))
125- })
126- onStep(" tokenMetadata" )
115+ val token = trace.spanSuspendOrRun(" tokenMetadata" ) {
116+ mintMetadata ? : tokenProvider.getTokenMetadata(giveRequestMint).getOrNull()?.token
117+ } ? : return logAndFail(GrabTransactorError .Other (message = " No token found for proposed mint" ))
127118
128119 val tokenizedCluster = ownerKey.withTimelockForToken(token)
129120
130121 // 3. create an account if we don't currently have one for this token
131122 val needsAccount = ! accountController.hasAccountFor(token.address)
132123 if (needsAccount) {
133- accountController.createUserAccount(
134- ownerForMint = tokenizedCluster,
135- mint = token.address
136- ).recoverCatching { error ->
137- if (error is SubmitIntentError .Denied && error.isUnexpectedOwnerAccount) {
138- // Safety net: PR #660 mitigates the upstream cause (getUserFlags
139- // failure preventing account bootstrap), but if the core account
140- // still isn't set up we recover here by triggering the normal
141- // bootstrap path before retrying.
142- accountController.refreshAccountState()
143- // Retry the original non-core mint account
144- accountController.createUserAccount(
145- ownerForMint = tokenizedCluster,
146- mint = token.address
147- ).getOrThrow()
148- } else {
149- throw error
124+ val creation = trace.spanSuspendOrRun(" createUserAccount" , metadata = mapOf (" needed" to true )) {
125+ accountController.createUserAccount(
126+ ownerForMint = tokenizedCluster,
127+ mint = token.address
128+ ).recoverCatching { error ->
129+ if (error is SubmitIntentError .Denied && error.isUnexpectedOwnerAccount) {
130+ // Safety net: PR #660 mitigates the upstream cause (getUserFlags
131+ // failure preventing account bootstrap), but if the core account
132+ // still isn't set up we recover here by triggering the normal
133+ // bootstrap path before retrying.
134+ accountController.refreshAccountState()
135+ // Retry the original non-core mint account
136+ accountController.createUserAccount(
137+ ownerForMint = tokenizedCluster,
138+ mint = token.address
139+ ).getOrThrow()
140+ } else {
141+ throw error
142+ }
150143 }
151- }.onFailure {
152- onStep(" createUserAccount (needed=true)" )
153- return @timedTraceSuspend handleGrabError(it)
154144 }
145+ creation.exceptionOrNull()?.let { return handleGrabError(it) }
155146 }
156- onStep(" createUserAccount (needed=$needsAccount )" )
157147
158148 // 4. Send grab request and wait for confirmation
159- val result = requestGrab<TransactionMetadata .SendPublicPayment >(tokenizedCluster, data)
160- .map { it.copy(verifiedExchangeData = exchangeData) }
161- .onSuccess {
162- // 5. Ack the receipt of the give request to clear it from the stream
163- messagingController.ackMessages(data.rendezvous, listOf (messageId))
164- }
165- onStep(" requestGrab+ack" )
149+ val result = trace.spanSuspendOrRun(" requestGrab" ) {
150+ requestGrab<TransactionMetadata .SendPublicPayment >(tokenizedCluster, data)
151+ .map { it.copy(verifiedExchangeData = exchangeData) }
152+ .onSuccess {
153+ // 5. Ack the receipt of the give request to clear it from the stream
154+ messagingController.ackMessages(data.rendezvous, listOf (messageId))
155+ }
156+ }
166157
167- result
158+ return result
168159 }
169160
170161 private fun handleGrabError (error : Throwable ): Result <Nothing > {
0 commit comments