mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-08-11 08:59:18 +00:00
Send large ecash tokens in batches, performance improvements for large wallets.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "minibits_wallet",
|
||||
"version": "0.1.9-beta.23",
|
||||
"version": "0.1.9-beta.24",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android:clean": "cd android && ./gradlew clean",
|
||||
|
||||
@@ -37,6 +37,7 @@ import { LogLevel } from '../services/log/logTypes'
|
||||
import { getSnapshot } from 'mobx-state-tree'
|
||||
import { delay } from '../utils/delay'
|
||||
import RNExitApp from 'react-native-exit-app'
|
||||
import { TransactionStatus } from '../models/Transaction'
|
||||
|
||||
// refresh
|
||||
|
||||
@@ -47,7 +48,7 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
|
||||
onLeftPress: () => navigation.goBack(),
|
||||
})
|
||||
|
||||
const {transactionsStore, userSettingsStore} = useStores()
|
||||
const {transactionsStore, userSettingsStore, proofsStore} = useStores()
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [rnVersion, setRnVersion] = useState<string>('')
|
||||
@@ -75,7 +76,7 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
|
||||
init()
|
||||
}, [])
|
||||
|
||||
// Reset of whole model state and reload from DB
|
||||
// Reset of transaction model state and reload from DB
|
||||
const syncTransactionsFromDb = async function () {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
@@ -105,6 +106,48 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
|
||||
}
|
||||
|
||||
|
||||
const deletePending = async function () {
|
||||
Alert.alert(
|
||||
translate("common.confirmAlertTitle"),
|
||||
"This action can not be undone. Use only in development or testing.",
|
||||
[
|
||||
{
|
||||
text: translate('common.cancel'),
|
||||
style: 'cancel',
|
||||
onPress: () => { /* Action canceled */ },
|
||||
},
|
||||
{
|
||||
text: translate('common.confirm'),
|
||||
onPress: async () => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const rows = Database.deleteTransactionsByStatus(TransactionStatus.PENDING)
|
||||
|
||||
const pending = proofsStore.allPendingProofs
|
||||
const pendingCount = proofsStore.pendingProofsCount.valueOf()
|
||||
|
||||
if(pendingCount > 0) {
|
||||
proofsStore.removeProofs(pending, true, false) // remove pending proofs and spend the in db
|
||||
}
|
||||
|
||||
if(rows?.length && rows.length > 0) {
|
||||
await syncTransactionsFromDb()
|
||||
}
|
||||
|
||||
setIsLoading(false)
|
||||
setInfo(`Removed ${rows?.length || 0} transactions from the database and ${pendingCount} proofs from the wallet state`)
|
||||
|
||||
} catch (e: any) {
|
||||
handleError(e)
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
const toggleLogLevelSelector = () =>
|
||||
setIsLogLevelSelectorVisible(previousState => !previousState)
|
||||
|
||||
@@ -180,9 +223,31 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
|
||||
style={{color: headerTitle}}
|
||||
/>
|
||||
</View>
|
||||
<View style={$contentContainer}>
|
||||
<View style={$contentContainer}>
|
||||
<Card
|
||||
style={$card}
|
||||
style={[$card]}
|
||||
HeadingComponent={
|
||||
<ListItem
|
||||
tx="developerScreen.info"
|
||||
subText={`Environment: ${APP_ENV}
|
||||
Native version: ${NATIVE_VERSION_ANDROID}
|
||||
JS Bundle version: ${JS_BUNDLE_VERSION}
|
||||
DB version: ${dbVersion}
|
||||
State size: ${walletStateSize.toLocaleString()} bytes
|
||||
React Native: ${rnVersion}
|
||||
Commit: ${COMMIT}
|
||||
Sentry id: ${userSettingsStore.userSettings.walletId}
|
||||
`}
|
||||
leftIcon='faInfoCircle'
|
||||
leftIconColor={colors.palette.iconGreen300}
|
||||
leftIconInverse={true}
|
||||
RightComponent={<View style={$rightContainer} />}
|
||||
style={$item}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Card
|
||||
style={[$card, {marginTop: spacing.medium}]}
|
||||
HeadingComponent={
|
||||
<>
|
||||
<ListItem
|
||||
@@ -211,30 +276,21 @@ export const DeveloperScreen: FC<SettingsStackScreenProps<'Developer'>> = observ
|
||||
}
|
||||
/>
|
||||
<Card
|
||||
style={[$card, {marginTop: spacing.medium}]}
|
||||
label='Danger zone'
|
||||
labelStyle={{marginTop: spacing.medium}}
|
||||
style={[$card]}
|
||||
HeadingComponent={
|
||||
<>
|
||||
<ListItem
|
||||
tx="developerScreen.info"
|
||||
subText={`Environment: ${APP_ENV}
|
||||
Native version: ${NATIVE_VERSION_ANDROID}
|
||||
JS Bundle version: ${JS_BUNDLE_VERSION}
|
||||
DB version: ${dbVersion}
|
||||
State size: ${walletStateSize.toLocaleString()} bytes
|
||||
React Native: ${rnVersion}
|
||||
Commit: ${COMMIT}
|
||||
Sentry id: ${userSettingsStore.userSettings.walletId}
|
||||
`}
|
||||
leftIcon='faInfoCircle'
|
||||
leftIconColor={colors.palette.iconGreen300}
|
||||
leftIconInverse={true}
|
||||
text="Force delete pending"
|
||||
subText="Removes ecash from pending state and deletes all pending transactions."
|
||||
leftIcon='faClock'
|
||||
leftIconColor={colors.palette.accent400}
|
||||
leftIconInverse={true}
|
||||
RightComponent={<View style={$rightContainer} />}
|
||||
style={$item}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Card
|
||||
style={[$card, {marginTop: spacing.medium}]}
|
||||
HeadingComponent={
|
||||
style={$item}
|
||||
onPress={deletePending}
|
||||
/>
|
||||
<ListItem
|
||||
tx="developerScreen.reset"
|
||||
subTx="developerScreen.resetDescription"
|
||||
@@ -244,7 +300,9 @@ Sentry id: ${userSettingsStore.userSettings.walletId}
|
||||
RightComponent={<View style={$rightContainer} />}
|
||||
style={$item}
|
||||
onPress={factoryReset}
|
||||
topSeparator
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -42,7 +42,7 @@ import { verticalScale } from '@gocodingnow/rn-size-matters'
|
||||
|
||||
interface ExportBackupScreenProps extends SettingsStackScreenProps<'ExportBackup'> {}
|
||||
|
||||
const OPTIMIZE_FROM_PROOFS_COUNT = 100
|
||||
const OPTIMIZE_FROM_PROOFS_COUNT = 5
|
||||
|
||||
export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
function ExportBackup(_props) {
|
||||
@@ -65,6 +65,7 @@ export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [isSendAllSentToQueue, setIsSendAllSentToQueue] = useState<boolean>(false)
|
||||
const [isReceiveBatchSentToQueue, setIsReceiveBatchSentToQueue] = useState<boolean>(false)
|
||||
const [totalProofsCount, setTotalProofsCount] = useState<number>(0)
|
||||
const [totalSentProofsCount, setTotalSentProofsCount] = useState<number>(0)
|
||||
const [totalReceiveErrorCount, setTotalReceiveErrorCount] = useState<number>(0)
|
||||
const [totalReceiveCompleteCount, setTotalReceiveCompleteCount] = useState<number>(0)
|
||||
@@ -85,12 +86,14 @@ export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
await proofsStore.loadProofsFromDatabase()
|
||||
|
||||
const orphaned = proofsStore.allProofs.filter(proof => !proof.mintUrl)
|
||||
|
||||
if (orphaned.length > 0) {
|
||||
setOrphanedProofs(orphaned)
|
||||
setInfo(`Found ${orphaned.length} orphaned proofs not belonging to any active mint. Those won't be included to the backup, but you can copy them separately.`)
|
||||
}
|
||||
|
||||
// log.trace('[loadProofs]', {refreshedProofs: proofsStore.proofs})
|
||||
setTotalProofsCount(proofsStore.proofsCount)
|
||||
setIsLoading(false)
|
||||
} catch (e: any) {
|
||||
handleError(e)
|
||||
@@ -135,7 +138,7 @@ export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
}
|
||||
|
||||
return () => {
|
||||
EventEmitter.off('ev_sendTask_result', handleSendAllResult)
|
||||
EventEmitter.off('ev_sendTask_result', handleSendAllResult)
|
||||
}
|
||||
}, [isSendAllSentToQueue])
|
||||
|
||||
@@ -151,14 +154,18 @@ export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
if (error) {
|
||||
setTotalReceiveErrorCount(prev => prev + 1)
|
||||
} else {
|
||||
setTotalReceiveCompleteCount(prev => prev + 1)
|
||||
}
|
||||
setTotalReceiveCompleteCount(prev => prev + 1)
|
||||
}
|
||||
}
|
||||
|
||||
if(isReceiveBatchSentToQueue) {
|
||||
EventEmitter.on('ev_receiveTask_result', handleReceiveTaskResult)
|
||||
}
|
||||
|
||||
return () => {
|
||||
EventEmitter.off('ev_receiveTask_result', handleReceiveTaskResult)
|
||||
}
|
||||
|
||||
}, [isReceiveBatchSentToQueue])
|
||||
|
||||
|
||||
@@ -169,12 +176,12 @@ export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
|
||||
setIsLoading(false)
|
||||
|
||||
const currentProofsCount = proofsStore.proofsCount
|
||||
const currentProofsCount = proofsStore.proofsCount
|
||||
|
||||
let message = `Original proofs count: ${totalSentProofsCount}, Optimized proofs count: ${currentProofsCount}`
|
||||
|
||||
if (totalReceiveErrorCount > 0) {
|
||||
message += `, errors: ${totalReceiveErrorCount}`
|
||||
message += `, Errors: ${totalReceiveErrorCount}`
|
||||
}
|
||||
|
||||
setResultModalInfo({
|
||||
@@ -185,15 +192,18 @@ export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
setIsResultModalVisible(true)
|
||||
}
|
||||
|
||||
if(totalReceiveCompleteCount > 0 || totalReceiveCompleteCount > 0) {
|
||||
if(totalReceiveCompleteCount > 0 || totalReceiveErrorCount > 0) {
|
||||
showProofOptimizationResult()
|
||||
}
|
||||
|
||||
}, [totalReceiveErrorCount, totalReceiveCompleteCount])
|
||||
|
||||
|
||||
const toggleResultModal = () =>
|
||||
setIsResultModalVisible(previousState => !previousState)
|
||||
const toggleResultModal = () => {
|
||||
setIsResultModalVisible(previousState => !previousState)
|
||||
WalletTask.syncPendingStateWithMints()
|
||||
}
|
||||
|
||||
|
||||
|
||||
const toggleBackupEcashSwitch = () =>
|
||||
@@ -221,7 +231,9 @@ export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
{
|
||||
text: translate('common.confirm'),
|
||||
onPress: async () => {
|
||||
// Moves all wallet proofs to pending in transactions split by mints and by units and in offline mode
|
||||
// Moves all wallet proofs to pending in transactions
|
||||
// split by mints and by units and in offline mode.
|
||||
// Supports batching in case proofs count is above limit.
|
||||
setIsLoading(true)
|
||||
setIsSendAllSentToQueue(true)
|
||||
WalletTask.sendAll()
|
||||
@@ -578,27 +590,27 @@ export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
<Card
|
||||
ContentComponent={
|
||||
<>
|
||||
{proofsStore.proofsCount + proofsStore.pendingProofsCount > 0 && (
|
||||
{totalProofsCount > 0 && (
|
||||
<ListItem
|
||||
text="Ecash proofs"
|
||||
subText={`Number of proofs: ${proofsStore.proofsCount + proofsStore.pendingProofsCount}`}
|
||||
RightComponent={
|
||||
<View style={$rightContainer}>
|
||||
{proofsStore.proofsCount > OPTIMIZE_FROM_PROOFS_COUNT && (
|
||||
<Button
|
||||
preset='secondary'
|
||||
onPress={optimizeProofAmountsStart}
|
||||
textStyle={{lineHeight: verticalScale(16), fontSize: verticalScale(14)}}
|
||||
style={{minHeight: verticalScale(40), paddingVertical: verticalScale(spacing.tiny)}}
|
||||
text={'Optimize'}
|
||||
/>
|
||||
)}
|
||||
<Switch
|
||||
onValueChange={toggleBackupEcashSwitch}
|
||||
value={isEcashInBackup}
|
||||
text="Ecash proofs"
|
||||
subText={`Number of proofs: ${proofsStore.proofsCount}`}
|
||||
RightComponent={
|
||||
<View style={$rightContainer}>
|
||||
{proofsStore.proofsCount > OPTIMIZE_FROM_PROOFS_COUNT && (
|
||||
<Button
|
||||
preset='secondary'
|
||||
onPress={optimizeProofAmountsStart}
|
||||
textStyle={{lineHeight: verticalScale(16), fontSize: verticalScale(14)}}
|
||||
style={{minHeight: verticalScale(40), paddingVertical: verticalScale(spacing.tiny)}}
|
||||
text={'Optimize'}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
)}
|
||||
<Switch
|
||||
onValueChange={toggleBackupEcashSwitch}
|
||||
value={isEcashInBackup}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{mintsStore.mintCount > 0 && (
|
||||
@@ -613,7 +625,7 @@ export const ExportBackupScreen: FC<ExportBackupScreenProps> =
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
topSeparator={proofsStore.proofsCount + proofsStore.pendingProofsCount > 0 ? true : false}
|
||||
topSeparator={totalProofsCount > 0 ? true : false}
|
||||
/>
|
||||
)}
|
||||
{contactsStore.count > 0 && (
|
||||
|
||||
@@ -1060,7 +1060,7 @@ const getPendingAmount = function () {
|
||||
}
|
||||
|
||||
|
||||
const deleteTransactionsByStatus = async function (status: TransactionStatus) {
|
||||
const deleteTransactionsByStatus = function (status: TransactionStatus) {
|
||||
try {
|
||||
const query = `
|
||||
DELETE FROM transactions
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
MintKeyset,
|
||||
} from '@cashu/cashu-ts'
|
||||
import { getDefaultAmountPreference, isObj } from '@cashu/cashu-ts/src/utils'
|
||||
import { TransactionTaskResult, WalletTask } from '../walletService'
|
||||
import { MAX_SWAP_INPUT_SIZE, TransactionTaskResult, WalletTask } from '../walletService'
|
||||
import { MintBalance, MintProofsCounter } from '../../models/Mint'
|
||||
import { Proof } from '../../models/Proof'
|
||||
import { poller } from '../../utils/poller'
|
||||
@@ -242,7 +242,17 @@ export const sendFromMint = async function (
|
||||
|
||||
if(selectedProofsAmount > 0) {
|
||||
if(amountToSend !== selectedProofsAmount) { // failsafe for some unknown ecash selection UX error
|
||||
throw new AppError(Err.VALIDATION_ERROR, 'Requested amount to send does not equal sum of ecash denominations provided.')
|
||||
throw new AppError(
|
||||
Err.VALIDATION_ERROR,
|
||||
'Requested amount to send does not equal sum of ecash denominations provided.'
|
||||
)
|
||||
}
|
||||
|
||||
if(selectedProofs.length > MAX_SWAP_INPUT_SIZE) {
|
||||
throw new AppError(
|
||||
Err.VALIDATION_ERROR,
|
||||
`Number of proofs is above max of ${MAX_SWAP_INPUT_SIZE}. Visit Backup to optimize, then try again.`
|
||||
)
|
||||
}
|
||||
|
||||
for (const proof of selectedProofs) {
|
||||
@@ -434,25 +444,17 @@ export const sendFromMint = async function (
|
||||
lockedProofsCounter.resetInFlight(transactionId)
|
||||
|
||||
} else if (returnedAmount === 0) {
|
||||
/*
|
||||
* SWAP not needed, all selected proofs will be sent
|
||||
* In this case we might make sure upfront those are not spent - not used now
|
||||
*/
|
||||
/* const syncResult = await WalletTask.syncStateWithMintSync(
|
||||
{
|
||||
proofsToSync: proofsToSendFrom,
|
||||
mintUrl,
|
||||
isPending: false
|
||||
}
|
||||
)
|
||||
|
||||
if(syncResult.transactionStateUpdates.find(stateUpdate =>
|
||||
stateUpdate.spentByMintAmount &&
|
||||
stateUpdate.spentByMintAmount > 0)) {
|
||||
throw new AppError(Err.VALIDATION_ERROR, 'Spent ecash has been used as an input for this transaction, try again.')
|
||||
}*/
|
||||
|
||||
log.debug('[sendFromMint] Swap is not necessary, all proofsToSendFrom will be sent.')
|
||||
|
||||
// If we selected whole balance, check if it is not above limit acceptable by wallet and mints.
|
||||
if(proofsToSendFrom.length > MAX_SWAP_INPUT_SIZE) {
|
||||
throw new AppError(
|
||||
Err.VALIDATION_ERROR,
|
||||
`Number of proofs is above max of ${MAX_SWAP_INPUT_SIZE}. Visit Backup to optimize, then try again.`
|
||||
)
|
||||
}
|
||||
|
||||
proofsToSend = [...proofsToSendFrom]
|
||||
|
||||
} else {
|
||||
|
||||
@@ -31,7 +31,8 @@ import { MintUnit, formatCurrency, getCurrency } from './wallet/currency'
|
||||
import { MinibitsClient } from './minibitsService'
|
||||
|
||||
|
||||
export const MAX_SWAP_INPUT_SIZE = 50
|
||||
export const MAX_SWAP_INPUT_SIZE = 100
|
||||
export const MAX_SYNC_INPUT_SIZE = 200 // 1000 hard mint limit
|
||||
|
||||
type WalletTaskService = {
|
||||
syncPendingStateWithMints: () => Promise<void>
|
||||
@@ -339,20 +340,41 @@ const sendAll = async function (): Promise<void> {
|
||||
|
||||
// Move all proofs by mint units to pending as in offline mode (do not ask for swap)
|
||||
for (const mint of mintsStore.allMints) {
|
||||
// Do not create a pending transaction above mint's spent sync (check) limit as it becomes stuck pending
|
||||
// As well keep tokens reasonably sized so that a device can keep related transaction in the state / load it from DB
|
||||
const maxBatchSize = MAX_SWAP_INPUT_SIZE
|
||||
|
||||
for (const unit of mint.units) {
|
||||
const proofsToOptimize = proofsStore.getByMint(mint.mintUrl, { isPending: false, unit })
|
||||
const proofsAmount = CashuUtils.getProofsAmount(proofsToOptimize)
|
||||
const mintBalance = mint.balances
|
||||
const proofsToOptimize = proofsStore.getByMint(mint.mintUrl, { isPending: false, unit })
|
||||
const totalProofsCount = proofsToOptimize.length
|
||||
const mintBalance = mint.balances
|
||||
|
||||
if (totalProofsCount > maxBatchSize) {
|
||||
let index = 0
|
||||
for (let i = 0; i < totalProofsCount; i += maxBatchSize) {
|
||||
index++
|
||||
const batch = proofsToOptimize.slice(i, i + maxBatchSize)
|
||||
const batchAmount = CashuUtils.getProofsAmount(batch)
|
||||
|
||||
// Queued WalletTask.send
|
||||
send(
|
||||
mintBalance!,
|
||||
proofsAmount,
|
||||
unit,
|
||||
`Optimize proof amounts`,
|
||||
proofsToOptimize // forces offline mode
|
||||
)
|
||||
send(
|
||||
mintBalance!,
|
||||
batchAmount,
|
||||
unit,
|
||||
`Optimize ecash #${index}`,
|
||||
batch // forces offline mode
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// If the length is less than or equal to limit, run with all proofs.
|
||||
const proofsAmount = CashuUtils.getProofsAmount(proofsToOptimize)
|
||||
send(
|
||||
mintBalance!,
|
||||
proofsAmount,
|
||||
unit,
|
||||
`Optimize ecash`,
|
||||
proofsToOptimize // forces offline mode
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,6 +416,7 @@ const syncPendingStateWithMints = async function (): Promise<void> {
|
||||
}
|
||||
|
||||
const isPending = true
|
||||
// const maxBatchSize = MAX_CHECK_INPUT_SIZE
|
||||
|
||||
// group proofs by mint so that we do max one call per mint
|
||||
for (const mint of mintsStore.allMints) {
|
||||
@@ -403,8 +426,9 @@ const syncPendingStateWithMints = async function (): Promise<void> {
|
||||
continue
|
||||
}
|
||||
|
||||
const proofsToSync = proofsStore.getByMint(mint.mintUrl, {isPending})
|
||||
syncStateWithMint({proofsToSync, mintUrl: mint.mintUrl, isPending}) // isPending = true
|
||||
const proofsToSync = proofsStore.getByMint(mint.mintUrl, {isPending})
|
||||
const totalProofs = proofsToSync.length
|
||||
syncStateWithMint({ proofsToSync, mintUrl: mint.mintUrl, isPending })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,23 +444,22 @@ const syncSpendableStateWithMints = async function (): Promise<void> {
|
||||
}
|
||||
|
||||
const isPending = false
|
||||
const maxBatchSize = 50
|
||||
const maxBatchSize = MAX_SYNC_INPUT_SIZE
|
||||
|
||||
// group proofs by mint so that we do max one call per mint
|
||||
// does not depend on unit, process in batches by 100
|
||||
for (const mint of mintsStore.allMints) {
|
||||
|
||||
for (const mint of mintsStore.allMints) {
|
||||
const proofsToSync = proofsStore.getByMint(mint.mintUrl, { isPending })
|
||||
const totalProofs = proofsToSync.length
|
||||
const totalProofsCount = proofsToSync.length
|
||||
|
||||
if (totalProofs > maxBatchSize) {
|
||||
for (let i = 0; i < totalProofs; i += maxBatchSize) {
|
||||
if (totalProofsCount > maxBatchSize) {
|
||||
for (let i = 0; i < totalProofsCount; i += maxBatchSize) {
|
||||
const batch = proofsToSync.slice(i, i + maxBatchSize)
|
||||
syncStateWithMint({ proofsToSync: batch, mintUrl: mint.mintUrl, isPending: false });
|
||||
syncStateWithMint({ proofsToSync: batch, mintUrl: mint.mintUrl, isPending });
|
||||
}
|
||||
} else {
|
||||
// If the length is less than or equal to 100, run syncStateWithMint with all proofs.
|
||||
syncStateWithMint({ proofsToSync, mintUrl: mint.mintUrl, isPending: false });
|
||||
syncStateWithMint({ proofsToSync, mintUrl: mint.mintUrl, isPending });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user