@@ -5,14 +5,23 @@ import { ZodTypeProvider } from 'fastify-type-provider-zod';
5
5
import { CKBTransaction , Cell , IsomorphicTransaction , Script , XUDTBalance } from './types' ;
6
6
import z from 'zod' ;
7
7
import { Env } from '../../env' ;
8
- import { buildPreLockArgs , getXudtTypeScript , isScriptEqual , isTypeAssetSupported } from '@rgbpp-sdk/ckb' ;
9
- import { groupBy } from 'lodash' ;
8
+ import {
9
+ isScriptEqual ,
10
+ buildPreLockArgs ,
11
+ getRgbppLockScript ,
12
+ getXudtTypeScript ,
13
+ isTypeAssetSupported ,
14
+ } from '@rgbpp-sdk/ckb' ;
15
+ import { groupBy , uniq } from 'lodash' ;
10
16
import { BI } from '@ckb-lumos/lumos' ;
11
17
import { UTXO } from '../../services/bitcoin/schema' ;
12
18
import { Transaction as BTCTransaction } from '../bitcoin/types' ;
13
19
import { TransactionWithStatus } from '../../services/ckb' ;
14
20
import { computeScriptHash } from '@ckb-lumos/lumos/utils' ;
15
21
import { filterCellsByTypeScript , getTypeScript } from '../../utils/typescript' ;
22
+ import { unpackRgbppLockArgs } from '@rgbpp-sdk/btc/lib/ckb/molecule' ;
23
+ import { TestnetTypeMap } from '../../constants' ;
24
+ import { remove0x } from '@rgbpp-sdk/btc' ;
16
25
17
26
const addressRoutes : FastifyPluginCallback < Record < never , never > , Server , ZodTypeProvider > = ( fastify , _ , done ) => {
18
27
const env : Env = fastify . container . resolve ( 'env' ) ;
@@ -52,6 +61,18 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
52
61
return cells ;
53
62
}
54
63
64
+ /**
65
+ * Filter RgbppLock cells by cells
66
+ */
67
+ function getRgbppLockCellsByCells ( cells : Cell [ ] ) : Cell [ ] {
68
+ const rgbppLockScript = getRgbppLockScript ( env . NETWORK === 'mainnet' , TestnetTypeMap [ env . NETWORK ] ) ;
69
+ return cells . filter (
70
+ ( cell ) =>
71
+ rgbppLockScript . codeHash === cell . cellOutput . lock . codeHash &&
72
+ rgbppLockScript . hashType === cell . cellOutput . lock . hashType ,
73
+ ) ;
74
+ }
75
+
55
76
fastify . get (
56
77
'/:btc_address/assets' ,
57
78
{
@@ -147,13 +168,14 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
147
168
throw fastify . httpErrors . badRequest ( 'Unsupported type asset' ) ;
148
169
}
149
170
150
- const utxos = await getUxtos ( btc_address , no_cache ) ;
151
171
const xudtBalances : Record < string , XUDTBalance > = { } ;
172
+ const utxos = await getUxtos ( btc_address , no_cache ) ;
152
173
153
- let cells = await getRgbppAssetsCells ( btc_address , utxos , no_cache ) ;
154
- cells = typeScript ? filterCellsByTypeScript ( cells , typeScript ) : cells ;
155
-
156
- const availableXudtBalances = await fastify . rgbppCollector . getRgbppBalanceByCells ( cells ) ;
174
+ // Find confirmed RgbppLock Xudt assets
175
+ const confirmedUtxos = utxos . filter ( ( utxo ) => utxo . status . confirmed ) ;
176
+ const confirmedCells = await getRgbppAssetsCells ( btc_address , confirmedUtxos , no_cache ) ;
177
+ const confirmedTargetCells = filterCellsByTypeScript ( confirmedCells , typeScript ) ;
178
+ const availableXudtBalances = await fastify . rgbppCollector . getRgbppBalanceByCells ( confirmedTargetCells ) ;
157
179
Object . keys ( availableXudtBalances ) . forEach ( ( key ) => {
158
180
const { amount, ...xudtInfo } = availableXudtBalances [ key ] ;
159
181
xudtBalances [ key ] = {
@@ -164,6 +186,7 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
164
186
} ;
165
187
} ) ;
166
188
189
+ // Find all unconfirmed RgbppLock Xudt outputs
167
190
const pendingUtxos = utxos . filter (
168
191
( utxo ) =>
169
192
! utxo . status . confirmed ||
@@ -172,19 +195,14 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
172
195
) ;
173
196
const pendingUtxosGroup = groupBy ( pendingUtxos , ( utxo ) => utxo . txid ) ;
174
197
const pendingTxids = Object . keys ( pendingUtxosGroup ) ;
175
-
176
198
const pendingOutputCellsGroup = await Promise . all (
177
199
pendingTxids . map ( async ( txid ) => {
178
200
const cells = await fastify . transactionProcessor . getPendingOutputCellsByTxid ( txid ) ;
179
201
const lockArgsSet = new Set ( pendingUtxosGroup [ txid ] . map ( ( utxo ) => buildPreLockArgs ( utxo . vout ) ) ) ;
180
202
return cells . filter ( ( cell ) => lockArgsSet . has ( cell . cellOutput . lock . args ) ) ;
181
203
} ) ,
182
204
) ;
183
- let pendingOutputCells = pendingOutputCellsGroup . flat ( ) ;
184
- if ( typeScript ) {
185
- pendingOutputCells = filterCellsByTypeScript ( pendingOutputCells , typeScript ) ;
186
- }
187
-
205
+ const pendingOutputCells = filterCellsByTypeScript ( pendingOutputCellsGroup . flat ( ) , typeScript ) ;
188
206
const pendingXudtBalances = await fastify . rgbppCollector . getRgbppBalanceByCells ( pendingOutputCells ) ;
189
207
Object . values ( pendingXudtBalances ) . forEach ( ( { amount, type_hash, ...xudtInfo } ) => {
190
208
if ( ! xudtBalances [ type_hash ] ) {
@@ -200,6 +218,49 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
200
218
xudtBalances [ type_hash ] . pending_amount = BI . from ( xudtBalances [ type_hash ] . pending_amount )
201
219
. add ( BI . from ( amount ) )
202
220
. toHexString ( ) ;
221
+ } ) ;
222
+
223
+ // Find spent RgbppLock Xudt assets in unconfirmed transactions' inputs
224
+ const allTxs = await fastify . bitcoin . getAddressTxs ( { address : btc_address } ) ;
225
+ const unconfirmedTxids = allTxs . filter ( ( tx ) => ! tx . status . confirmed ) . map ( ( tx ) => tx . txid ) ;
226
+ const spendingInputCellsGroup = await Promise . all (
227
+ unconfirmedTxids . map ( async ( txid ) => {
228
+ const inputCells = await fastify . transactionProcessor . getPendingInputCellsByTxid ( txid ) ;
229
+ const inputRgbppCells = getRgbppLockCellsByCells ( filterCellsByTypeScript ( inputCells , typeScript ) ) ;
230
+ const inputCellLockArgs = inputRgbppCells . map ( ( cell ) => unpackRgbppLockArgs ( cell . cellOutput . lock . args ) ) ;
231
+
232
+ const txids = uniq ( inputCellLockArgs . map ( ( args ) => remove0x ( args . btcTxid ) ) ) ;
233
+ const txs = await Promise . all ( txids . map ( ( txid ) => fastify . bitcoin . getTx ( { txid } ) ) ) ;
234
+ const txsMap = txs . reduce (
235
+ ( sum , tx , index ) => {
236
+ const txid = txids [ index ] ;
237
+ sum [ txid ] = tx ?? null ;
238
+ return sum ;
239
+ } ,
240
+ { } as Record < string , BTCTransaction | null > ,
241
+ ) ;
242
+
243
+ return inputRgbppCells . filter ( ( cell , index ) => {
244
+ const lockArgs = inputCellLockArgs [ index ] ;
245
+ const tx = txsMap [ remove0x ( lockArgs . btcTxid ) ] ;
246
+ const utxo = tx ?. vout [ lockArgs . outIndex ] ;
247
+ return utxo ?. scriptpubkey_address === btc_address ;
248
+ } ) ;
249
+ } ) ,
250
+ ) ;
251
+ const spendingInputCells = spendingInputCellsGroup . flat ( ) ;
252
+ const spendingXudtBalances = await fastify . rgbppCollector . getRgbppBalanceByCells ( spendingInputCells ) ;
253
+ Object . values ( spendingXudtBalances ) . forEach ( ( { amount, type_hash, ...xudtInfo } ) => {
254
+ if ( ! xudtBalances [ type_hash ] ) {
255
+ xudtBalances [ type_hash ] = {
256
+ ...xudtInfo ,
257
+ type_hash,
258
+ total_amount : '0x0' ,
259
+ available_amount : '0x0' ,
260
+ pending_amount : '0x0' ,
261
+ } ;
262
+ }
263
+
203
264
xudtBalances [ type_hash ] . total_amount = BI . from ( xudtBalances [ type_hash ] . total_amount )
204
265
. add ( BI . from ( amount ) )
205
266
. toHexString ( ) ;
@@ -322,18 +383,18 @@ const addressRoutes: FastifyPluginCallback<Record<never, never>, Server, ZodType
322
383
} as const ;
323
384
}
324
385
325
- const inputOutpoints = isomorphicTx . ckbRawTx ?. inputs || isomorphicTx . ckbTx ?. inputs || [ ] ;
326
- const inputs = await fastify . ckb . getInputCellsByOutPoint (
327
- inputOutpoints . map ( ( input ) => input . previousOutput ) as CKBComponents . OutPoint [ ] ,
328
- ) ;
386
+ const inputs = isomorphicTx . ckbRawTx ?. inputs || isomorphicTx . ckbTx ?. inputs || [ ] ;
387
+ const inputCells = await fastify . ckb . getInputCellsByOutPoint ( inputs . map ( ( input ) => input . previousOutput ! ) ) ;
388
+ const inputCellOutputs = inputCells . map ( ( cell ) => cell . cellOutput ) ;
389
+
329
390
const outputs = isomorphicTx . ckbRawTx ?. outputs || isomorphicTx . ckbTx ?. outputs || [ ] ;
330
391
331
392
return {
332
393
btcTx,
333
394
isRgbpp : true ,
334
395
isomorphicTx : {
335
396
...isomorphicTx ,
336
- inputs,
397
+ inputs : inputCellOutputs ,
337
398
outputs,
338
399
} ,
339
400
} as const ;
0 commit comments