• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GoodDollar / GoodServer / 18902836709

29 Oct 2025 09:18AM UTC coverage: 49.137% (-0.04%) from 49.176%
18902836709

push

github

web-flow
Dont use node later then 24, 25 crashes due to changing modules/imports (#507)

616 of 1530 branches covered (40.26%)

Branch coverage included in aggregate %.

1888 of 3566 relevant lines covered (52.94%)

7.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.03
/src/server/utils/tx-manager/queueMutex.js
1
import Mutex from 'await-mutex'
2
import logger from '../../../imports/logger'
3
const log = logger.child({ from: 'wallet queueMutex' })
9✔
4

5
export default class queueMutex {
6
  constructor() {
7
    this.wallets = {}
9✔
8
    this.getTransactionCount = () => 0
9✔
9
  }
10

11
  /**
12
   * Create object of wallets by addresses
13
   *
14
   * @param {array} addresses
15
   *
16
   * @returns {Promise<void>}
17
   */
18
  async createListIfNotExists(addresses) {
19
    for (let address of addresses) {
5✔
20
      if (!this.getWallet(address)) {
10✔
21
        const data = await this.createWallet(address)
5✔
22
        log.info('created mutex for address:', { address, nonce: data.nonce })
5✔
23
      }
24
    }
25
  }
26

27
  /**
28
   * Create object of wallet by address
29
   * @param address
30
   */
31
  async createWallet(address) {
32
    return (this.wallets[address] = {
7✔
33
      address,
34
      nonce: await this.getTransactionCount(address),
35
      mutex: new Mutex()
36
    })
37
  }
38

39
  /**
40
   * Get wallet by address
41
   * @param address
42
   * @returns {T}
43
   */
44
  getWallet(address) {
45
    return this.wallets[address]
44✔
46
  }
47

48
  /**
49
   * Unlock for queue
50
   *
51
   * @param {string} address
52
   * @param {string} netNonce
53
   *
54
   * @returns {Promise<void>}
55
   */
56
  async unlock(address, netNonce) {
57
    const wallet = this.getWallet(address)
×
58

59
    if (wallet && typeof wallet.lastFail === 'function') {
×
60
      wallet.lastFail(netNonce)
×
61
    }
62
  }
63

64
  /**
65
   * lock for queue
66
   *
67
   * @param {array} addresses
68
   *
69
   * @returns {Promise<any>}
70
   */
71
  async lock(addresses) {
72
    addresses = Array.isArray(addresses) ? addresses : [addresses]
17✔
73
    // await this.createListIfNotExists(addresses)
74
    log.debug('lock request', { addresses })
17✔
75
    const address = await this.getFirstFreeAddress(addresses)
17✔
76
    let wallet = this.getWallet(address)
17✔
77
    if (wallet === undefined) {
17✔
78
      wallet = await this.createWallet(address)
2✔
79
    }
80
    log.debug('lock: got wallet', { address, wallet })
17✔
81
    let release = await wallet.mutex.lock()
17✔
82
    log.debug('lock: acquired lock', { address })
17✔
83
    wallet.release = () => {
17✔
84
      wallet.nonce++
16✔
85
      release()
16✔
86
    }
87
    wallet.lastFail = netNonce => {
17✔
88
      if (netNonce) {
1!
89
        wallet.nonce = netNonce
×
90
      }
91
      release()
1✔
92
    }
93

94
    return {
17✔
95
      address,
96
      nonce: wallet.nonce,
97
      release: wallet.release,
98
      fail: wallet.lastFail
99
    }
100
  }
101

102
  /**
103
   * Get lock status for address
104
   *
105
   * @param {string} address
106
   *
107
   * @returns {Boolean}
108
   */
109
  isLocked(address) {
110
    const wallet = this.getWallet(address)
17✔
111
    if (wallet) {
17✔
112
      const res = wallet.mutex.isLocked()
15✔
113
      return res
15✔
114
    }
115
    return false
2✔
116
  }
117

118
  async getFirstFreeAddress(addresses) {
119
    return new Promise(resolve => {
17✔
120
      const interval = setInterval(() => {
17✔
121
        //use random to simulate real conditions, otherwise same address will be taken on single host
122
        const address = addresses[Math.floor(Math.random() * addresses.length)]
17✔
123
        // for (let address of addresses) {
124
        if (this.isLocked(address) === false) {
17!
125
          log.debug('getFirstFreeAddress: address not locked', { address })
17✔
126

127
          clearInterval(interval)
17✔
128
          return resolve(address)
17✔
129
        }
130
        log.debug('getFirstFreeAddress: address locked', { address })
×
131
        // }
132
      }, 100)
133
    })
134
  }
135
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc