5
5
Currency ,
6
6
EntityType ,
7
7
GetBalanceResponse ,
8
+ TransferStatus ,
8
9
TransferType ,
9
10
type BalanceChange ,
10
11
type TransferResult ,
@@ -25,7 +26,12 @@ import {
25
26
} from '../redis' ;
26
27
import { generateStorageKey , StorageKey , StorageTopic } from '../config' ;
27
28
import { coresBalanceExpirationSeconds } from './constants' ;
28
- import { ConflictError , NjordErrorMessages } from '../errors' ;
29
+ import {
30
+ ConflictError ,
31
+ NjordErrorMessages ,
32
+ throwUserTransactionError ,
33
+ TransferError ,
34
+ } from '../errors' ;
29
35
import { GarmrService } from '../integrations/garmr' ;
30
36
import { BrokenCircuitError } from 'cockatiel' ;
31
37
import type { EntityManager } from 'typeorm' ;
@@ -53,6 +59,9 @@ const garmNjordService = new GarmrService({
53
59
threshold : 0.1 ,
54
60
duration : 10 * 1000 ,
55
61
} ,
62
+ retryOpts : {
63
+ maxAttempts : 1 ,
64
+ } ,
56
65
} ) ;
57
66
58
67
export const getNjordClient = ( clientTransport = transport ) => {
@@ -146,27 +155,30 @@ export const transferCores = createAuthProtectedFn(
146
155
147
156
const njordClient = getNjordClient ( ) ;
148
157
149
- const transferResult = await garmNjordService . execute ( async ( ) => {
150
- if ( ! transaction . id ) {
151
- throw new Error ( 'No transaction id' ) ;
152
- }
158
+ if ( ! transaction . id ) {
159
+ throw new Error ( 'No transaction id' ) ;
160
+ }
153
161
154
- if ( ! transaction . senderId ) {
155
- throw new Error ( 'No sender id' ) ;
156
- }
162
+ if ( ! transaction . senderId ) {
163
+ throw new Error ( 'No sender id' ) ;
164
+ }
165
+
166
+ const senderId = transaction . senderId ;
167
+ const receiverId = transaction . receiverId ;
157
168
158
- const { results } = await njordClient . transfer ( {
169
+ const response = await garmNjordService . execute ( async ( ) => {
170
+ const response = await njordClient . transfer ( {
159
171
idempotencyKey : transaction . id ,
160
172
transfers : [
161
173
{
162
174
transferType : TransferType . TRANSFER ,
163
175
currency : Currency . CORES ,
164
176
sender : {
165
- id : transaction . senderId ,
177
+ id : senderId ,
166
178
type : EntityType . USER ,
167
179
} ,
168
180
receiver : {
169
- id : transaction . receiverId ,
181
+ id : receiverId ,
170
182
type : EntityType . USER ,
171
183
} ,
172
184
amount : transaction . value ,
@@ -176,47 +188,52 @@ export const transferCores = createAuthProtectedFn(
176
188
} ,
177
189
] ,
178
190
} ) ;
179
- // we always have single transfer
180
- const result = results . find (
181
- ( item ) => item . transferType === TransferType . TRANSFER ,
182
- ) ;
183
191
184
- if ( ! result ) {
185
- throw new Error ( 'No transfer result' ) ;
186
- }
192
+ return response ;
193
+ } ) ;
187
194
188
- await Promise . allSettled ( [
189
- [
190
- parseBalanceUpdate ( {
191
- balance : result . senderBalance ,
192
- userId : transaction . senderId ,
193
- } ) ,
194
- parseBalanceUpdate ( {
195
- balance : result . receiverBalance ,
196
- userId : transaction . receiverId ,
197
- } ) ,
198
- ] . map ( async ( balanceUpdate ) => {
199
- if ( ! balanceUpdate ) {
200
- return ;
201
- }
195
+ if ( response . status !== TransferStatus . SUCCESS ) {
196
+ throw new TransferError ( response ) ;
197
+ }
202
198
203
- await updateBalanceCache ( {
204
- ctx : {
205
- userId : balanceUpdate . userId ,
206
- } ,
207
- value : {
208
- amount : parseBigInt ( balanceUpdate . balance . newBalance ) ,
209
- } ,
210
- } ) ;
211
- } ) ,
212
- ] ) ;
199
+ const { results } = response ;
213
200
214
- // TODO feat/transactions error handling
201
+ // we always have single transfer
202
+ const result = results . find (
203
+ ( item ) => item . transferType === TransferType . TRANSFER ,
204
+ ) ;
215
205
216
- return result ;
217
- } ) ;
206
+ if ( ! result ) {
207
+ throw new Error ( 'No transfer result' ) ;
208
+ }
209
+
210
+ await Promise . allSettled ( [
211
+ [
212
+ parseBalanceUpdate ( {
213
+ balance : result . senderBalance ,
214
+ userId : transaction . senderId ,
215
+ } ) ,
216
+ parseBalanceUpdate ( {
217
+ balance : result . receiverBalance ,
218
+ userId : transaction . receiverId ,
219
+ } ) ,
220
+ ] . map ( async ( balanceUpdate ) => {
221
+ if ( ! balanceUpdate ) {
222
+ return ;
223
+ }
224
+
225
+ await updateBalanceCache ( {
226
+ ctx : {
227
+ userId : balanceUpdate . userId ,
228
+ } ,
229
+ value : {
230
+ amount : parseBigInt ( balanceUpdate . balance . newBalance ) ,
231
+ } ,
232
+ } ) ;
233
+ } ) ,
234
+ ] ) ;
218
235
219
- return transferResult ;
236
+ return result ;
220
237
} ,
221
238
) ;
222
239
@@ -225,22 +242,22 @@ export const purchaseCores = async ({
225
242
} : {
226
243
transaction : UserTransaction ;
227
244
} ) : Promise < TransferResult > => {
228
- const transferResult = await garmNjordService . execute ( async ( ) => {
229
- if ( ! transaction . id ) {
230
- throw new Error ( 'No transaction id' ) ;
231
- }
245
+ if ( ! transaction . id ) {
246
+ throw new Error ( 'No transaction id' ) ;
247
+ }
232
248
233
- if ( transaction . senderId ) {
234
- throw new Error ( 'Purchase cores transaction can not have sender' ) ;
235
- }
249
+ if ( transaction . senderId ) {
250
+ throw new Error ( 'Purchase cores transaction can not have sender' ) ;
251
+ }
236
252
237
- if ( transaction . productId ) {
238
- throw new Error ( 'Purchase cores transaction can not have product' ) ;
239
- }
253
+ if ( transaction . productId ) {
254
+ throw new Error ( 'Purchase cores transaction can not have product' ) ;
255
+ }
240
256
241
- const njordClient = getNjordClient ( ) ;
257
+ const njordClient = getNjordClient ( ) ;
242
258
243
- const { results } = await njordClient . transfer ( {
259
+ const response = await garmNjordService . execute ( async ( ) => {
260
+ const response = await njordClient . transfer ( {
244
261
idempotencyKey : transaction . id ,
245
262
transfers : [
246
263
{
@@ -261,43 +278,48 @@ export const purchaseCores = async ({
261
278
} ,
262
279
] ,
263
280
} ) ;
264
- // we always have single transfer
265
- const result = results . find (
266
- ( item ) => item . transferType === TransferType . TRANSFER ,
267
- ) ;
268
281
269
- if ( ! result ) {
270
- throw new Error ( 'No transfer result' ) ;
271
- }
282
+ return response ;
283
+ } ) ;
272
284
273
- await Promise . allSettled ( [
274
- [
275
- parseBalanceUpdate ( {
276
- balance : result . receiverBalance ,
277
- userId : transaction . receiverId ,
278
- } ) ,
279
- ] . map ( async ( balanceUpdate ) => {
280
- if ( ! balanceUpdate ) {
281
- return ;
282
- }
285
+ if ( response . status !== TransferStatus . SUCCESS ) {
286
+ throw new TransferError ( response ) ;
287
+ }
283
288
284
- await updateBalanceCache ( {
285
- ctx : {
286
- userId : balanceUpdate . userId ,
287
- } ,
288
- value : {
289
- amount : parseBigInt ( balanceUpdate . balance . newBalance ) ,
290
- } ,
291
- } ) ;
292
- } ) ,
293
- ] ) ;
289
+ const { results } = response ;
290
+
291
+ // we always have single transfer
292
+ const result = results . find (
293
+ ( item ) => item . transferType === TransferType . TRANSFER ,
294
+ ) ;
294
295
295
- // TODO feat/transactions error handling
296
+ if ( ! result ) {
297
+ throw new Error ( 'No transfer result' ) ;
298
+ }
296
299
297
- return result ;
298
- } ) ;
300
+ await Promise . allSettled ( [
301
+ [
302
+ parseBalanceUpdate ( {
303
+ balance : result . receiverBalance ,
304
+ userId : transaction . receiverId ,
305
+ } ) ,
306
+ ] . map ( async ( balanceUpdate ) => {
307
+ if ( ! balanceUpdate ) {
308
+ return ;
309
+ }
299
310
300
- return transferResult ;
311
+ await updateBalanceCache ( {
312
+ ctx : {
313
+ userId : balanceUpdate . userId ,
314
+ } ,
315
+ value : {
316
+ amount : parseBigInt ( balanceUpdate . balance . newBalance ) ,
317
+ } ,
318
+ } ) ;
319
+ } ) ,
320
+ ] ) ;
321
+
322
+ return result ;
301
323
} ;
302
324
303
325
export type GetBalanceProps = Pick < AuthContext , 'userId' > ;
@@ -482,12 +504,24 @@ export const awardUser = async (
482
504
note,
483
505
} ) ;
484
506
485
- const transfer = await transferCores ( {
486
- ctx,
487
- transaction,
488
- } ) ;
507
+ try {
508
+ const transfer = await transferCores ( {
509
+ ctx,
510
+ transaction,
511
+ } ) ;
512
+
513
+ return { transaction, transfer } ;
514
+ } catch ( error ) {
515
+ if ( error instanceof TransferError ) {
516
+ await throwUserTransactionError ( {
517
+ entityManager,
518
+ error,
519
+ transaction,
520
+ } ) ;
521
+ }
489
522
490
- return { transaction, transfer } ;
523
+ throw error ;
524
+ }
491
525
} ,
492
526
) ;
493
527
@@ -581,12 +615,28 @@ export const awardPost = async (
581
615
)
582
616
. execute ( ) ;
583
617
584
- const transfer = await transferCores ( {
585
- ctx,
586
- transaction,
587
- } ) ;
618
+ try {
619
+ const transfer = await transferCores ( {
620
+ ctx,
621
+ transaction,
622
+ } ) ;
623
+
624
+ return { transaction, transfer } ;
625
+ } catch ( error ) {
626
+ if ( error instanceof TransferError ) {
627
+ await entityManager . getRepository ( UserPost ) . delete ( {
628
+ awardTransactionId : transaction . id ,
629
+ } ) ;
630
+
631
+ await throwUserTransactionError ( {
632
+ entityManager,
633
+ error,
634
+ transaction,
635
+ } ) ;
636
+ }
588
637
589
- return { transaction, transfer } ;
638
+ throw error ;
639
+ }
590
640
} ,
591
641
) ;
592
642
@@ -679,10 +729,12 @@ export const awardComment = async (
679
729
)
680
730
. execute ( ) ;
681
731
732
+ let newComment : Comment | undefined ;
733
+
682
734
if ( note ) {
683
735
const post = await comment . post ;
684
736
685
- const newComment = entityManager . getRepository ( Comment ) . create ( {
737
+ newComment = entityManager . getRepository ( Comment ) . create ( {
686
738
id : await generateShortId ( ) ,
687
739
postId : comment . postId ,
688
740
parentId : comment . parentId || comment . id ,
@@ -709,12 +761,34 @@ export const awardComment = async (
709
761
await saveComment ( entityManager , newComment , post . sourceId ) ;
710
762
}
711
763
712
- const transfer = await transferCores ( {
713
- ctx,
714
- transaction,
715
- } ) ;
764
+ try {
765
+ const transfer = await transferCores ( {
766
+ ctx,
767
+ transaction,
768
+ } ) ;
769
+
770
+ return { transaction, transfer } ;
771
+ } catch ( error ) {
772
+ if ( error instanceof TransferError ) {
773
+ if ( newComment ) {
774
+ await entityManager . getRepository ( Comment ) . delete ( {
775
+ id : newComment . id ,
776
+ } ) ;
777
+ }
778
+
779
+ await entityManager . getRepository ( UserComment ) . delete ( {
780
+ awardTransactionId : transaction . id ,
781
+ } ) ;
716
782
717
- return { transaction, transfer } ;
783
+ await throwUserTransactionError ( {
784
+ entityManager,
785
+ error,
786
+ transaction,
787
+ } ) ;
788
+ }
789
+
790
+ throw error ;
791
+ }
718
792
} ,
719
793
) ;
720
794
0 commit comments