@@ -11,7 +11,7 @@ import { throwIf } from 'app/utils/throwIf'
11
11
import debug from 'debug'
12
12
import type { UserOperation } from 'permissionless'
13
13
import { getUserOperationHash } from 'permissionless/utils'
14
- import { withRetry } from 'viem'
14
+ import { type Hex , withRetry } from 'viem'
15
15
import { z } from 'zod'
16
16
import { createTRPCRouter , protectedProcedure } from '../trpc'
17
17
@@ -32,6 +32,8 @@ export const temporalRouter = createTRPCRouter({
32
32
session : { user } ,
33
33
} ,
34
34
} ) => {
35
+ const startTime = new Date ( Date . now ( ) )
36
+
35
37
const noteValidationError = note
36
38
? formFields . note . safeParse ( decodeURIComponent ( note ) ) . error
37
39
: null
@@ -81,47 +83,27 @@ export const temporalRouter = createTRPCRouter({
81
83
message : e . message ,
82
84
} )
83
85
} )
84
-
85
- log ( `Workflow Created: ${ workflowId } ` )
86
-
87
86
await withRetry (
88
87
async ( ) => {
89
- const [ initialized , receipt ] = await Promise . allSettled ( [
90
- lookupIntializedWorkflow ( workflowId )
91
- . then ( ( ) => true )
92
- . catch ( ( ) => false ) ,
93
- ( async ( ) => {
94
- // maybe bundler already has the receipt
95
- const receipt = await baseMainnetBundlerClient . getUserOperationReceipt ( {
96
- hash : userOpHash ,
97
- } )
98
- if ( ! receipt ) return null
99
- const supabase = createSupabaseAdminClient ( )
100
- // do not redirect unless it's been indexed into send_account_transfers
101
- const { data, error } = await supabase
102
- . from ( 'send_account_transfers' )
103
- . select ( '*' )
104
- . eq ( 'tx_hash' , hexToBytea ( receipt . receipt . transactionHash ) )
105
- . maybeSingle ( )
106
- throwIf ( error )
107
- return data
108
- } ) ( ) ,
109
- ] as const )
88
+ const initPromise = lookupInitializedWorkflow ( workflowId , startTime ) . catch ( ( e ) => {
89
+ log ( e )
90
+ return Promise . reject ( )
91
+ } )
110
92
111
- if ( initialized . status === 'fulfilled' && initialized . value ) {
112
- log ( 'transfer initialized' )
113
- return true
114
- }
93
+ const receiptPromise = lookupTransferReceipt ( userOpHash , startTime ) . catch ( ( e ) => {
94
+ log ( e )
95
+ return Promise . reject ( )
96
+ } )
115
97
116
- if ( receipt . status === 'fulfilled' && receipt . value ) {
117
- log ( 'userop already onchain' , receipt . value . tx_hash )
118
- return true
98
+ try {
99
+ return await Promise . any ( [ initPromise , receiptPromise ] )
100
+ } catch ( error ) {
101
+ throw new TRPCError ( {
102
+ code : 'PRECONDITION_FAILED' ,
103
+ message :
104
+ 'Pending transfer not found: Please manually verify whether the send was completed.' ,
105
+ } )
119
106
}
120
-
121
- throw new TRPCError ( {
122
- code : 'PRECONDITION_FAILED' ,
123
- message : 'Transfer not yet submitted' ,
124
- } )
125
107
} ,
126
108
{
127
109
retryCount : 20 ,
@@ -132,7 +114,7 @@ export const temporalRouter = createTRPCRouter({
132
114
} ,
133
115
shouldRetry ( { error : e } ) {
134
116
const error = e as unknown as PostgrestError
135
- if ( error . code === 'PGRST116' || error . message === 'Transfer not yet submitted ' ) {
117
+ if ( error . code === 'PGRST116' || error . code === 'PRECONDITION_FAILED ' ) {
136
118
return true // retry on no rows
137
119
}
138
120
return false
@@ -145,16 +127,38 @@ export const temporalRouter = createTRPCRouter({
145
127
) ,
146
128
} )
147
129
148
- async function lookupIntializedWorkflow ( workflowId : string ) : Promise < { status : string } > {
149
- // check if the transfer is initialized
130
+ async function lookupTransferReceipt ( userOpHash : Hex , startTime : Date ) : Promise < boolean > {
131
+ const blockTimeWindow = Math . floor ( startTime . getTime ( ) / 1000 ) - 5 * 60 // subtract 5 minutes as a buffer window
132
+ const receipt = await baseMainnetBundlerClient . getUserOperationReceipt ( {
133
+ hash : userOpHash ,
134
+ } )
135
+ assert ( ! ! receipt , 'Transfer not onchain' )
136
+
137
+ const supabase = createSupabaseAdminClient ( )
138
+ const { count, error } = await supabase
139
+ . from ( 'send_account_transfers' )
140
+ . select ( '*' , { count : 'exact' , head : true } )
141
+ . eq ( 'tx_hash' , hexToBytea ( receipt . receipt . transactionHash ) )
142
+ . gte ( 'block_time' , blockTimeWindow )
143
+
144
+ throwIf ( error )
145
+ assert ( count !== null && count > 0 , 'Transfer not indexed' )
146
+ log ( 'userop already onchain' , receipt . receipt . transactionHash )
147
+ return true
148
+ }
149
+
150
+ async function lookupInitializedWorkflow ( workflowId : string , startTime : Date ) : Promise < boolean > {
150
151
const supabaseAdmin = createSupabaseAdminClient ( )
151
152
const { data, error } = await supabaseAdmin
152
153
. from ( 'activity' )
153
154
. select ( 'data->>status' )
154
155
. eq ( 'event_id' , workflowId )
155
156
. eq ( 'event_name' , 'temporal_send_account_transfers' )
157
+ . gte ( 'created_at' , startTime . toISOString ( ) )
156
158
. single ( )
159
+
157
160
throwIf ( error )
158
161
assert ( ! ! data && data . status !== 'initialized' , 'Transfer not yet submitted' )
159
- return data
162
+ log ( `${ data . status } transfer found: ${ workflowId } ` )
163
+ return true
160
164
}
0 commit comments