From f56995db4f69fe58f8d728581c6a00967508fd4f Mon Sep 17 00:00:00 2001 From: yanguoyu <841185308@qq.com> Date: Sat, 19 Nov 2022 15:23:26 +0800 Subject: [PATCH 1/4] fix: Use batch rpc to request result. Optimization sql search. 1. For the nerovs dao page, there are many rpc requests, use batch request. 2. Because some users have many relations with others cells, left join twice to search inputs and outputs. 3. Find unlock transactions with the status that equal Dead or Pending. --- .../src/components/NervosDAO/hooks.ts | 141 ++++++++++++------ packages/neuron-wallet/src/services/cells.ts | 112 +++++++------- .../src/services/tx/transaction-service.ts | 8 +- 3 files changed, 164 insertions(+), 97 deletions(-) diff --git a/packages/neuron-ui/src/components/NervosDAO/hooks.ts b/packages/neuron-ui/src/components/NervosDAO/hooks.ts index cc306e03d5..b96cda18af 100644 --- a/packages/neuron-ui/src/components/NervosDAO/hooks.ts +++ b/packages/neuron-ui/src/components/NervosDAO/hooks.ts @@ -20,8 +20,9 @@ import { generateDaoDepositTx, generateDaoClaimTx, } from 'services/remote' -import { ckbCore, getHeaderByNumber, calculateDaoMaximumWithdraw } from 'services/chain' +import { ckbCore, getHeaderByNumber } from 'services/chain' import { isErrorWithI18n } from 'exceptions' +import { calculateMaximumWithdraw } from '@nervosnetwork/ckb-sdk-utils' const { MIN_AMOUNT, @@ -91,7 +92,7 @@ export const useInitData = ({ updateNervosDaoData({ walletID: wallet.id })(dispatch) const intervalId = setInterval(() => { updateNervosDaoData({ walletID: wallet.id })(dispatch) - }, 3000) + }, 10000) updateDepositValue( `${ BigInt(wallet.balance) > BigInt(CKBToShannonFormatter(`${MIN_DEPOSIT_AMOUNT}`)) @@ -436,35 +437,79 @@ export const useUpdateWithdrawList = ({ setWithdrawList: React.Dispatch>> }) => useEffect(() => { - Promise.all( - records.map(async ({ outPoint, depositOutPoint, blockHash }) => { - if (!tipBlockHash) { - return null - } - const withdrawBlockHash = depositOutPoint ? blockHash : tipBlockHash - const formattedDepositOutPoint = depositOutPoint - ? { - txHash: depositOutPoint.txHash, - index: `0x${BigInt(depositOutPoint.index).toString(16)}`, - } - : { - txHash: outPoint.txHash, - index: `0x${BigInt(outPoint.index).toString(16)}`, - } - return calculateDaoMaximumWithdraw(formattedDepositOutPoint, withdrawBlockHash).catch(() => null) - }) - ) - .then(res => { - const withdrawList = new Map() - if (tipBlockHash) { - records.forEach((record, idx) => { - const key = getRecordKey(record) - withdrawList.set(key, res[idx]) + if (!tipBlockHash) { + setWithdrawList(new Map()) + return + } + const depositOutPointHashes = records.map(v => v.depositOutPoint?.txHash ?? v.outPoint.txHash) + const txMap = new Map() + ckbCore.rpc + .createBatchRequest<'getTransaction', string[], CKBComponents.TransactionWithStatus[]>( + depositOutPointHashes.map(v => ['getTransaction', v]) + ) + .exec() + .then(txs => { + const committedTx = txs.filter(v => v.txStatus.status === 'committed') + committedTx.forEach((tx, idx) => { + if (tx.txStatus.status === 'committed') { + txMap.set(depositOutPointHashes[idx], tx) + } + }) + const blockHashes = [ + ...(committedTx.map(v => v.txStatus.blockHash).filter(v => !!v) as string[]), + ...(records.map(v => (v.depositOutPoint ? v.blockHash : null)).filter(v => !!v) as string[]), + tipBlockHash, + ] + const hashHeaderMap = new Map() + return ckbCore.rpc + .createBatchRequest<'getHeader', string[], CKBComponents.BlockHeader[]>( + blockHashes.map(v => ['getHeader', v]) + ) + .exec() + .then(blockHeaders => { + blockHeaders.forEach((header, idx) => { + if (header?.dao) { + hashHeaderMap.set(blockHashes[idx], header.dao) + } + }) + const withdrawList = new Map() + records.forEach(record => { + const key = getRecordKey(record) + const withdrawBlockHash = record.depositOutPoint ? record.blockHash : tipBlockHash + const formattedDepositOutPoint = record.depositOutPoint + ? { + txHash: record.depositOutPoint.txHash, + index: `0x${BigInt(record.depositOutPoint.index).toString(16)}`, + } + : { + txHash: record.outPoint.txHash, + index: `0x${BigInt(record.outPoint.index).toString(16)}`, + } + const tx = txMap.get(formattedDepositOutPoint.txHash) + if (!tx) { + return + } + const depositDAO = hashHeaderMap.get(tx.txStatus.blockHash!) + const withdrawDAO = hashHeaderMap.get(withdrawBlockHash) + if (!depositDAO || !withdrawDAO) { + return + } + withdrawList.set( + key, + calculateMaximumWithdraw( + tx.transaction.outputs[+formattedDepositOutPoint.index], + tx.transaction.outputsData[+formattedDepositOutPoint.index], + depositDAO, + withdrawDAO + ) + ) + }) + setWithdrawList(withdrawList) }) - } - setWithdrawList(withdrawList) }) - .catch(console.error) + .catch(() => { + setWithdrawList(new Map()) + }) }, [records, tipBlockHash, setWithdrawList]) export const useUpdateDepositEpochList = ({ @@ -478,24 +523,28 @@ export const useUpdateDepositEpochList = ({ }) => useEffect(() => { if (connectionStatus === 'online') { - Promise.all( - records.map(({ daoData, depositOutPoint, blockNumber }) => { - const depositBlockNumber = depositOutPoint ? ckbCore.utils.toUint64Le(daoData) : blockNumber - if (!depositBlockNumber) { - return null - } - return getHeaderByNumber(BigInt(depositBlockNumber)) - .then(header => header.epoch) - .catch(() => null) - }) - ).then(res => { - const epochList = new Map() - records.forEach((record, idx) => { - const key = getRecordKey(record) - epochList.set(key, res[idx]) - }) - setDepositEpochList(epochList) + const recordKeyIdxMap = new Map() + const batchParams: ['getHeaderByNumber', bigint][] = [] + records.forEach((record, idx) => { + const depositBlockNumber = record.depositOutPoint + ? ckbCore.utils.toUint64Le(record.daoData) + : record.blockNumber + if (depositBlockNumber) { + batchParams.push(['getHeaderByNumber', BigInt(depositBlockNumber)]) + recordKeyIdxMap.set(getRecordKey(record), idx) + } }) + ckbCore.rpc + .createBatchRequest<'getHeaderByNumber', any, CKBComponents.BlockHeader[]>(batchParams) + .exec() + .then(res => { + const epochList = new Map() + records.forEach(record => { + const key = getRecordKey(record) + epochList.set(key, recordKeyIdxMap.get(key) ? res[recordKeyIdxMap.get(key)!]?.epoch : null) + }) + setDepositEpochList(epochList) + }) } }, [records, setDepositEpochList, connectionStatus]) diff --git a/packages/neuron-wallet/src/services/cells.ts b/packages/neuron-wallet/src/services/cells.ts index 51468c3c9e..072962d0b4 100644 --- a/packages/neuron-wallet/src/services/cells.ts +++ b/packages/neuron-wallet/src/services/cells.ts @@ -131,6 +131,65 @@ export default class CellsService { return uniqueLockArgs } + private static async addUnLockInfo(cells: Cell[]): Promise { + // find unlock info + const unlockTxHashes: string[] = cells + .filter(v => v.outPoint && (v.status === OutputStatus.Dead || v.status === OutputStatus.Pending)) + .map(o => o.outPoint!.txHash) + const inputs: InputEntity[] = await getConnection() + .getRepository(InputEntity) + .createQueryBuilder('input') + .leftJoinAndSelect('input.transaction', 'tx') + .where({ + outPointTxHash: In(unlockTxHashes) + }) + .getMany() + const unlockTxMap = new Map() + inputs.forEach(i => { + const key = i.outPointTxHash + ':' + i.outPointIndex + unlockTxMap.set(key, i.transaction!) + }) + cells.forEach(cell => { + // if unlocked, set unlockInfo + const key = cell.outPoint?.txHash + ':' + cell.outPoint?.index + const unlockTx = key ? unlockTxMap.get(key) : undefined + if (unlockTx && (cell.status === OutputStatus.Dead || cell.status === OutputStatus.Pending)) { + cell.setUnlockInfo({ + txHash: unlockTx.hash, + timestamp: unlockTx.timestamp! + }) + } + }) + return cells + } + + private static async addDepositInfo(cells: Cell[]): Promise { + // find deposit info + const depositTxHashes = cells.map(cells => cells.depositOutPoint?.txHash).filter(hash => !!hash) + const depositTxs = await getConnection() + .getRepository(TransactionEntity) + .createQueryBuilder('tx') + .where({ + hash: In(depositTxHashes) + }) + .getMany() + const depositTxMap = new Map() + depositTxs.forEach(tx => { + depositTxMap.set(tx.hash, tx) + }) + cells.forEach(cell => { + if (cell.depositOutPoint?.txHash && depositTxMap.has(cell.depositOutPoint.txHash)) { + const depositTx = depositTxMap.get(cell.depositOutPoint.txHash)! + cell.setDepositTimestamp(depositTx.timestamp!) + cell.setDepositInfo({ + txHash: depositTx.hash, + timestamp: depositTx.timestamp! + }) + } + }) + return cells + } + public static async getDaoCells(walletId: string): Promise { const outputs: OutputEntity[] = await getConnection() .getRepository(OutputEntity) @@ -169,36 +228,6 @@ export default class CellsService { .addOrderBy('tx.timestamp', 'ASC') .getMany() - // find deposit info - const depositTxHashes = outputs.map(output => output.depositTxHash).filter(hash => !!hash) - const depositTxs = await getConnection() - .getRepository(TransactionEntity) - .createQueryBuilder('tx') - .where({ - hash: In(depositTxHashes) - }) - .getMany() - const depositTxMap = new Map() - depositTxs.forEach(tx => { - depositTxMap.set(tx.hash, tx) - }) - - // find unlock info - const unlockTxKeys: string[] = outputs.map(o => o.outPointTxHash + ':' + o.outPointIndex) - const inputs: InputEntity[] = await getConnection() - .getRepository(InputEntity) - .createQueryBuilder('input') - .leftJoinAndSelect('input.transaction', 'tx') - .where(`input.outPointTxHash || ':' || input.outPointIndex IN (:...infos)`, { - infos: unlockTxKeys - }) - .getMany() - const unlockTxMap = new Map() - inputs.forEach(i => { - const key = i.outPointTxHash + ':' + i.outPointIndex - unlockTxMap.set(key, i.transaction!) - }) - const cells: Cell[] = outputs.map(output => { const cell = output.toModel() if (!output.depositTxHash) { @@ -208,35 +237,18 @@ export default class CellsService { timestamp: output.transaction!.timestamp! }) } else { - // if not deposit cell, set deposit timestamp info, depositInfo, withdrawInfo - const depositTx = depositTxMap.get(output.depositTxHash)! - cell.setDepositTimestamp(depositTx.timestamp!) - - cell.setDepositInfo({ - txHash: depositTx.hash, - timestamp: depositTx.timestamp! - }) - + // if not deposit cell, set withdrawInfo const withdrawTx = output.transaction cell.setWithdrawInfo({ txHash: withdrawTx!.hash, timestamp: withdrawTx!.timestamp! }) - - if (output.status === OutputStatus.Dead || output.status === OutputStatus.Pending) { - // if unlocked, set unlockInfo - const key = output.outPointTxHash + ':' + output.outPointIndex - const unlockTx = unlockTxMap.get(key)! - cell.setUnlockInfo({ - txHash: unlockTx.hash, - timestamp: unlockTx.timestamp! - }) - } } - return cell }) + await Promise.all([CellsService.addDepositInfo(cells), CellsService.addUnLockInfo(cells)]) + return cells } diff --git a/packages/neuron-wallet/src/services/tx/transaction-service.ts b/packages/neuron-wallet/src/services/tx/transaction-service.ts index de860e6639..d978bc03a3 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-service.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-service.ts @@ -484,16 +484,22 @@ export class TransactionsService { .createQueryBuilder('transaction') .where('transaction.hash is :hash', { hash }) .leftJoinAndSelect('transaction.inputs', 'input') - .leftJoinAndSelect('transaction.outputs', 'output') .orderBy({ 'input.id': 'ASC' }) .getOne() + const txwithOutput = await getConnection() + .getRepository(TransactionEntity) + .createQueryBuilder('transaction') + .where('transaction.hash is :hash', { hash }) + .leftJoinAndSelect('transaction.outputs', 'output') + .getOne() if (!tx) { return undefined } + tx.outputs = txwithOutput?.outputs || [] return tx.toModel() } From fa1ffd3e7bdb115b37286065eb5e3d293e4dec3f Mon Sep 17 00:00:00 2001 From: yanguoyu <841185308@qq.com> Date: Tue, 29 Nov 2022 15:13:23 +0800 Subject: [PATCH 2/4] fix: Add test case and optimize some logic --- .../src/components/NervosDAO/hooks.ts | 8 +- packages/neuron-wallet/src/services/cells.ts | 4 +- .../src/services/tx/transaction-service.ts | 15 +- .../tests/services/cells.test.ts | 149 ++++++++++++++++++ 4 files changed, 161 insertions(+), 15 deletions(-) diff --git a/packages/neuron-ui/src/components/NervosDAO/hooks.ts b/packages/neuron-ui/src/components/NervosDAO/hooks.ts index b96cda18af..f757989706 100644 --- a/packages/neuron-ui/src/components/NervosDAO/hooks.ts +++ b/packages/neuron-ui/src/components/NervosDAO/hooks.ts @@ -451,9 +451,7 @@ export const useUpdateWithdrawList = ({ .then(txs => { const committedTx = txs.filter(v => v.txStatus.status === 'committed') committedTx.forEach((tx, idx) => { - if (tx.txStatus.status === 'committed') { - txMap.set(depositOutPointHashes[idx], tx) - } + txMap.set(depositOutPointHashes[idx], tx) }) const blockHashes = [ ...(committedTx.map(v => v.txStatus.blockHash).filter(v => !!v) as string[]), @@ -468,9 +466,7 @@ export const useUpdateWithdrawList = ({ .exec() .then(blockHeaders => { blockHeaders.forEach((header, idx) => { - if (header?.dao) { - hashHeaderMap.set(blockHashes[idx], header.dao) - } + hashHeaderMap.set(blockHashes[idx], header.dao) }) const withdrawList = new Map() records.forEach(record => { diff --git a/packages/neuron-wallet/src/services/cells.ts b/packages/neuron-wallet/src/services/cells.ts index 072962d0b4..7b44e9ce5b 100644 --- a/packages/neuron-wallet/src/services/cells.ts +++ b/packages/neuron-wallet/src/services/cells.ts @@ -131,7 +131,7 @@ export default class CellsService { return uniqueLockArgs } - private static async addUnLockInfo(cells: Cell[]): Promise { + private static async addUnlockInfo(cells: Cell[]): Promise { // find unlock info const unlockTxHashes: string[] = cells .filter(v => v.outPoint && (v.status === OutputStatus.Dead || v.status === OutputStatus.Pending)) @@ -247,7 +247,7 @@ export default class CellsService { return cell }) - await Promise.all([CellsService.addDepositInfo(cells), CellsService.addUnLockInfo(cells)]) + await Promise.all([CellsService.addDepositInfo(cells), CellsService.addUnlockInfo(cells)]) return cells } diff --git a/packages/neuron-wallet/src/services/tx/transaction-service.ts b/packages/neuron-wallet/src/services/tx/transaction-service.ts index d978bc03a3..6f936a2e8a 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-service.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-service.ts @@ -488,18 +488,19 @@ export class TransactionsService { 'input.id': 'ASC' }) .getOne() - const txwithOutput = await getConnection() - .getRepository(TransactionEntity) - .createQueryBuilder('transaction') - .where('transaction.hash is :hash', { hash }) - .leftJoinAndSelect('transaction.outputs', 'output') - .getOne() + const txOutpus = await getConnection() + .getRepository(OutputEntity) + .createQueryBuilder() + .where({ + outPointTxHash: hash + }) + .getMany() if (!tx) { return undefined } - tx.outputs = txwithOutput?.outputs || [] + tx.outputs = txOutpus return tx.toModel() } diff --git a/packages/neuron-wallet/tests/services/cells.test.ts b/packages/neuron-wallet/tests/services/cells.test.ts index 33a56bce48..96cf6bfb88 100644 --- a/packages/neuron-wallet/tests/services/cells.test.ts +++ b/packages/neuron-wallet/tests/services/cells.test.ts @@ -952,6 +952,155 @@ describe('CellsService', () => { }) }) + describe('#addUnlockInfo', () => { + const depositData = '0x0000000000000000' + const withdrawData = '0x000000000000000a' + const generateTx = (hash: string, timestamp: string) => { + const tx = new TransactionEntity() + tx.hash = hash + tx.version = '0x0' + tx.timestamp = timestamp + tx.status = TransactionStatus.Success + tx.witnesses = [] + tx.blockNumber = '1' + tx.blockHash = '0x' + '10'.repeat(32) + return tx + } + + const withdrawTxHash = '0x' + '2'.repeat(64) + + const unlockTxHash = '0x' + '4'.repeat(64) + const unlockTx = Transaction.fromObject({ + hash: unlockTxHash, + version: '0x0', + timestamp: '1572862777483', + status: TransactionStatus.Success, + witnesses: [], + blockNumber: '3', + blockHash: '0x' + '5'.repeat(64), + inputs: [ + Input.fromObject({ + previousOutput: new OutPoint(withdrawTxHash, '0'), + since: '0' + }) + ], + outputs: [ + Output.fromObject({ + capacity: '1000', + lock: bob.lockScript + }) + ] + }) + + const tx1 = generateTx('0x1234', '1572862777481') + + it('output cells is not cost', async () => { + const cells = [ + generateCell(toShannon('1000'), OutputStatus.Live, false, null, bob, depositData, tx1).toModel() + ] + //@ts-ignore private property + await CellsService.addUnlockInfo(cells) + expect(cells[0].unlockInfo).toBeUndefined() + }) + + it('output cells no transaction', async () => { + const cells = [ + generateCell(toShannon('1000'), OutputStatus.Dead, false, null, bob, depositData, tx1).toModel() + ] + //@ts-ignore private property + await CellsService.addUnlockInfo(cells) + expect(cells[0].unlockInfo).toBeUndefined() + }) + + it('output cells has cost', async () => { + await TransactionPersistor.saveFetchTx(unlockTx) + const outputs = Output.fromObject({ + capacity: '1000', + daoData: withdrawData, + lock: bob.lockScript, + type: SystemScriptInfo.generateDaoScript(), + outPoint: new OutPoint(withdrawTxHash, '0'), + status: OutputStatus.Dead + }) + //@ts-ignore private property + await CellsService.addUnlockInfo([outputs]) + expect(outputs.unlockInfo?.txHash).toEqual(unlockTxHash) + }) + }) + + describe('#addDepositInfo', () => { + const depositData = '0x0000000000000000' + const withdrawData = '0x000000000000000a' + const generateTx = (hash: string, timestamp: string) => { + const tx = new TransactionEntity() + tx.hash = hash + tx.version = '0x0' + tx.timestamp = timestamp + tx.status = TransactionStatus.Success + tx.witnesses = [] + tx.blockNumber = '1' + tx.blockHash = '0x' + '10'.repeat(32) + return tx + } + + const depositTxHash = '0x' + '0'.repeat(64) + const depositTx = Transaction.fromObject({ + hash: depositTxHash, + version: '0x0', + timestamp: '1572862777481', + status: TransactionStatus.Success, + witnesses: [], + blockNumber: '1', + blockHash: '0x' + '1'.repeat(64), + inputs: [], + outputs: [ + Output.fromObject({ + capacity: '1000', + daoData: depositData, + lock: bob.lockScript, + type: SystemScriptInfo.generateDaoScript() + }) + ] + }) + + const tx1 = generateTx('0x1234', '1572862777481') + + it('output cells is not deposit', async () => { + const cells = [ + generateCell(toShannon('1000'), OutputStatus.Live, false, null, bob, depositData, tx1).toModel() + ] + //@ts-ignore private property + await CellsService.addDepositInfo(cells) + expect(cells[0].depositInfo).toBeUndefined() + }) + + it('output cells no transaction', async () => { + const cells = [ + generateCell(toShannon('1000'), OutputStatus.Dead, false, null, bob, depositData, tx1).toModel() + ] + cells[0].depositOutPoint = new OutPoint('0x' + '0'.repeat(64), '0x0') + //@ts-ignore private property + await CellsService.addDepositInfo(cells) + expect(cells[0].depositInfo).toBeUndefined() + }) + + it('output cells has cost', async () => { + await TransactionPersistor.saveFetchTx(depositTx) + const outputs = Output.fromObject({ + capacity: '1000', + daoData: withdrawData, + lock: bob.lockScript, + type: SystemScriptInfo.generateDaoScript(), + depositOutPoint: new OutPoint(depositTx.hash!, '0'), + status: OutputStatus.Dead + }) + //@ts-ignore + await CellsService.addDepositInfo([outputs]) + expect(outputs.depositInfo?.txHash).toEqual(depositTxHash) + }) + }) + + describe('#usedByAnyoneCanPayBlake160s', () => { const fakeArgs1 = '0x1' const fakeArgs2 = '0x2' From 3c4fc2640e9f061b8e99e29b2d8b1c38c9408dda Mon Sep 17 00:00:00 2001 From: yanguoyu <841185308@qq.com> Date: Fri, 2 Dec 2022 14:38:36 +0800 Subject: [PATCH 3/4] fix: Fix typo. --- packages/neuron-wallet/src/services/tx/transaction-service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/neuron-wallet/src/services/tx/transaction-service.ts b/packages/neuron-wallet/src/services/tx/transaction-service.ts index 6f936a2e8a..45cf6298b0 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-service.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-service.ts @@ -488,7 +488,7 @@ export class TransactionsService { 'input.id': 'ASC' }) .getOne() - const txOutpus = await getConnection() + const txOutputs = await getConnection() .getRepository(OutputEntity) .createQueryBuilder() .where({ @@ -500,7 +500,7 @@ export class TransactionsService { return undefined } - tx.outputs = txOutpus + tx.outputs = txOutputs return tx.toModel() } From 95d3bbe5a9d6bcaa507fa6fe5f651b9ddb06285b Mon Sep 17 00:00:00 2001 From: yanguoyu <841185308@qq.com> Date: Wed, 4 Jan 2023 10:30:18 +0800 Subject: [PATCH 4/4] fix: Define variable nearby use. --- packages/neuron-ui/src/components/NervosDAO/hooks.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/neuron-ui/src/components/NervosDAO/hooks.ts b/packages/neuron-ui/src/components/NervosDAO/hooks.ts index f757989706..58a66ea222 100644 --- a/packages/neuron-ui/src/components/NervosDAO/hooks.ts +++ b/packages/neuron-ui/src/components/NervosDAO/hooks.ts @@ -442,7 +442,6 @@ export const useUpdateWithdrawList = ({ return } const depositOutPointHashes = records.map(v => v.depositOutPoint?.txHash ?? v.outPoint.txHash) - const txMap = new Map() ckbCore.rpc .createBatchRequest<'getTransaction', string[], CKBComponents.TransactionWithStatus[]>( depositOutPointHashes.map(v => ['getTransaction', v]) @@ -450,24 +449,27 @@ export const useUpdateWithdrawList = ({ .exec() .then(txs => { const committedTx = txs.filter(v => v.txStatus.status === 'committed') - committedTx.forEach((tx, idx) => { - txMap.set(depositOutPointHashes[idx], tx) - }) const blockHashes = [ ...(committedTx.map(v => v.txStatus.blockHash).filter(v => !!v) as string[]), ...(records.map(v => (v.depositOutPoint ? v.blockHash : null)).filter(v => !!v) as string[]), tipBlockHash, ] - const hashHeaderMap = new Map() return ckbCore.rpc .createBatchRequest<'getHeader', string[], CKBComponents.BlockHeader[]>( blockHashes.map(v => ['getHeader', v]) ) .exec() .then(blockHeaders => { + const hashHeaderMap = new Map() blockHeaders.forEach((header, idx) => { hashHeaderMap.set(blockHashes[idx], header.dao) }) + const txMap = new Map() + txs.forEach((tx, idx) => { + if (tx.txStatus.status === 'committed') { + txMap.set(depositOutPointHashes[idx], tx) + } + }) const withdrawList = new Map() records.forEach(record => { const key = getRecordKey(record)