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

GoodDollar / GoodDAPP / 10042557444

22 Jul 2024 02:15PM UTC coverage: 40.848% (-0.06%) from 40.912%
10042557444

push

github

sirpy
add: improve rpc error logs

2325 of 6933 branches covered (33.54%)

Branch coverage included in aggregate %.

3 of 4 new or added lines in 2 files covered. (75.0%)

10 existing lines in 2 files now uncovered.

5633 of 12549 relevant lines covered (44.89%)

67.96 hits per line

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

54.02
/src/lib/wallet/MultipleHttpProvider.js
1
// @flow
2

3
import Web3 from 'web3'
4
import { assign, has, shuffle } from 'lodash'
5
import { fallback, makePromiseWrapper, retry } from '../utils/async'
6
import logger, { isConnectionError, isRateLimitError } from '../logger/js-logger'
7
import { isDuplicateTxError } from './utils'
8

9
const { providers } = Web3
119✔
10
const { HttpProvider } = providers
119✔
11
const log = logger.child({ from: 'MultipleHttpProvider' })
119✔
12

13
const isTxError = message => isDuplicateTxError(message) || message?.search(/reverted|gas/i) >= 0
119!
14

15
export class MultipleHttpProvider extends HttpProvider {
16
  static loggedProviders = new Map()
119✔
17

18
  constructor(endpoints, config) {
19
    const [{ provider, options }] = endpoints // init with first endpoint config
1,956✔
20
    const { strategy = 'random', retries = 1 } = config || {} // or 'random'
1,956!
21

22
    log.debug('Setting default endpoint', { provider, config })
1,956✔
23
    super(provider, options)
1,956✔
24

25
    log.debug('Initialized', { endpoints, strategy })
1,956✔
26

27
    assign(this, {
1,956✔
28
      endpoints,
29
      strategy,
30
      retries,
31
    })
32
  }
33

34
  send(payload, callback) {
35
    const { endpoints, strategy, retries } = this
1,253✔
36
    const { loggedProviders } = MultipleHttpProvider
1,253✔
37

38
    // shuffle peers if random strategy chosen
39
    const peers = strategy === 'random' ? shuffle(endpoints) : endpoints
1,253!
40

41
    // eslint-disable-next-line require-await
42
    const calls = peers.map(item => async () => {
1,274✔
43
      const { provider, options } = item
1,253✔
44

45
      log.trace('Picked up peer', { provider, options }, payload.id)
1,253✔
46

47
      // calling ctor as fn with this context, to re-apply ALL settings
48
      // as ctor is defined as function, not as class this hack will work
49
      // see node_modules/web3-providers-http/src/index.js
50
      HttpProvider.call(this, provider, options)
1,253✔
51

52
      try {
1,253✔
53
        log.trace('Sending request to peer', { payload })
1,253✔
54
        return await this._sendRequest(payload)
1,253✔
55
      } catch (exception) {
56
        // log error to analytics if last peer failed, ie all rpcs failed
57
        if (!isTxError(exception) && !loggedProviders.has(provider) && peers[peers.length - 1] === item) {
2!
UNCOV
58
          loggedProviders.set(provider, true)
×
59

UNCOV
60
          const { message: originalMessage } = exception
×
UNCOV
61
          const errorMessage = 'Failed all RPCs' // so in analytics all errors are grouped under same message
×
62

63
          // log.exception bypass network error filtering
UNCOV
64
          log.exception('MultiHttpProvider:', errorMessage, exception, { provider, originalMessage })
×
UNCOV
65
        } else if (isRateLimitError(exception)) {
×
66
          log.warn('MultiHttpProvider rate limit error', exception.message, exception, { provider })
×
67
          endpoints.splice(endpoints.indexOf(item, 1))
×
68
          setTimeout(() => endpoints.push(item), 60000)
×
69
        } else {
UNCOV
70
          log.warn('MultiHttpProvider failed to send:', exception.message, exception, { provider })
×
71
        }
72

UNCOV
73
        throw exception
×
74
      }
75
    })
76

77
    const onSuccess = result => {
1,253✔
78
      log.trace('Success, got result', { result })
160✔
79
      callback(null, result)
160✔
80
    }
81

82
    const onFailed = error => {
1,253✔
NEW
83
      log.warn('Failed RPC call', error.message, error, payload.id)
×
84

85
      callback(error, null)
×
86
    }
87

88
    // if not connection issue - stop fallback, throw error
89
    const onFallback = error => {
1,253✔
90
      const { message, code } = error
×
91

92
      const txError = isTxError(message)
×
93
      const conError = isConnectionError(message)
×
94

95
      // retry if not tx issue and network error or if rpc responded with error (error.error)
96
      const willFallback = !txError && !!(code || error.error || !message || conError)
×
97

98
      if (!willFallback) {
×
99
        log.warn('send: got error without fallback', { message, error, willFallback, txError, conError })
×
100
      }
101

102
      return willFallback
×
103
    }
104

105
    log.trace('send: exec over peers', { peers, strategy, calls })
1,253✔
106

107
    retry(() => fallback(calls, onFallback), retries, 0)
1,253✔
108
      .then(onSuccess)
109
      .catch(onFailed)
110
  }
111

112
  /**
113
   * Promisifies HttpProvider.send to be compatible with fallback() util
114
   * @private
115
   * */
116
  // eslint-disable-next-line require-await
117
  async _sendRequest(payload) {
118
    const { promise, callback: pcallback } = makePromiseWrapper()
1,253✔
119

120
    const checkRpcError = (error, response) => {
1,253✔
121
      //regular network error
122
      if (error) {
162✔
123
        return pcallback(error)
2✔
124
      }
125

126
      //rpc responded with error or no result
127
      if (response.error || has(response, 'result') === false) {
160!
128
        return pcallback(response)
×
129
      }
130

131
      //response ok
132
      return pcallback(null, response)
160✔
133
    }
134

135
    super.send(payload, checkRpcError)
1,253✔
136
    return promise
1,253✔
137
  }
138
}
139

140
export const { WebsocketProvider } = providers
119✔
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