import { ProviderType } from '@masknet/web3-shared-evm' ; import { EthereumAddress } from 'wallet.ts' ; import { getTransactionCount } from './network' ; import { currentMaskWalletChainIdSettings } from '../../../plugins/Wallet/settings' ; class NonceManager { constructor ( private address ) { } private nonce = NonceManager . INITIAL_NONCE ; private locked = false ; private tasks : ( ( ) => void ) [ ] = [ ] ; private lock ( ) { this . locked = true ; } private unlock ( ) { this . locked = false ; } private continue ( ) { if ( ! this . locked ) this . tasks . shift ( ) ?. ( ) ; } private async getRemoteNonce ( ) { return new Promise < number > ( async ( resolve , reject ) => { const callback = ( e , nonce ? ) => { if ( e ) reject ( e ) ; else resolve ( nonce ?? 0 ) ; this . unlock ( ) ; this . continue ( ) ; } ; const run = async ( ) => { try { this . lock ( ) ; callback ( null , await getTransactionCount ( this . address , { providerType : ProviderType . MaskWallet , chainId : currentMaskWalletChainIdSettings . value , } ) ) ; } catch ( error ) { callback ( error ) ; } } ; if ( this . locked ) this . tasks . push ( run ) ; else run ( ) ; } ) ; } private async setLocalNonce ( nonce ) { return new Promise < void > ( async ( resolve , reject ) => { const callback = ( e ) => { if ( e ) reject ( e ) ; else resolve ( ) ; this . unlock ( ) ; this . continue ( ) ; } ; const run = async ( ) => { this . lock ( ) ; this . nonce = nonce ; callback ( null ) ; } ; if ( this . locked ) this . tasks . push ( run ) ; else run ( ) ; } ) ; } public async getNonce ( ) { const nonce = this . nonce === NonceManager . INITIAL_NONCE ? await this . getRemoteNonce ( ) : this . nonce ; await this . setLocalNonce ( nonce ) ; return nonce ; } public async setNonce ( nonce ) { await this . setLocalNonce ( nonce ) ; } public async resetNonce ( ) { const nonce = await this . getRemoteNonce ( ) ; await this . setLocalNonce ( nonce ) ; } static INITIAL_NONCE = - 1 ; } const cache = new Map < string , NonceManager > ( ) ; param address export function getNonce ( address_ ) { const address = EthereumAddress . checksumAddress ( address_ ) ; if ( ! cache . has ( address ) ) cache . set ( address , new NonceManager ( address ) ) ; return cache . get ( address ) ! . getNonce ( ) ; } param address export async function commitNonce ( address_ ) { const address = EthereumAddress . checksumAddress ( address_ ) ; if ( ! cache . has ( address ) ) cache . set ( address , new NonceManager ( address ) ) ; return setNonce ( address , ( await cache . get ( address ) ! . getNonce ( ) ) + 1 ) ; } param address param nonce export function setNonce ( address_ , nonce ) { const address = EthereumAddress . checksumAddress ( address_ ) ; if ( ! cache . has ( address ) ) cache . set ( address , new NonceManager ( address ) ) ; return cache . get ( address ) ! . setNonce ( nonce ) ; } param address export function resetNonce ( address_ ) { const address = EthereumAddress . checksumAddress ( address_ ) ; if ( ! cache . has ( address ) ) cache . set ( address , new NonceManager ( address ) ) ; return cache . get ( address ) ! . resetNonce ( ) ; } /**
 * Sync all nonces
 */ export async function resetAllNonce ( ) { await Promise . all ( Array . from ( cache . values ( ) ) . map ( ( m ) => m . resetNonce ( ) ) ) ; }