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

GoodDollar / GoodServer / 17411668733

01 Sep 2025 09:03AM UTC coverage: 49.555%. Remained the same
17411668733

push

github

web-flow
Xdc update (#503)

* add: xdc wallet support

* add: make verifyIdentifier uses correct chainid to support smartwallets

* add: xdc bridge fees, small chains refactoring

* fix: missing network configs

---------

Co-authored-by: LewisB <lewis@gooddollar.org>

617 of 1518 branches covered (40.65%)

Branch coverage included in aggregate %.

22 of 51 new or added lines in 7 files covered. (43.14%)

392 existing lines in 5 files now uncovered.

1888 of 3537 relevant lines covered (53.38%)

7.34 hits per line

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

24.71
/src/server/blockchain/MultiWallet.js
1
// @flow
2
import { assign, every, forOwn, isEmpty, isError, map, some } from 'lodash'
3
import AdminWallet from './AdminWallet'
4
import { CeloAdminWallet } from './CeloAdminWallet'
5
import { BaseAdminWallet } from './BaseAdminWallet'
6
import { XdcAdminWallet } from './XdcAdminWallet'
7

8
import conf from '../server.config'
9
import logger from '../../imports/logger'
10
import { DefenderRelayer } from './DefenderRelayer'
11
const multiLogger = logger.child({ from: 'MultiWallet' })
5✔
12

13
class MultiWallet {
14
  mainWallet = null
5✔
15
  otherWallets = []
5✔
16
  wallets = []
5✔
17
  walletsMap = {}
5✔
18
  defaultChainId = null
5✔
19
  signer: DefenderRelayer = null
5✔
20

21
  get ready() {
22
    return Promise.all(map(this.wallets, 'ready')).then(() => this.mainWallet.addresses)
3✔
23
  }
24

25
  constructor(walletsMap) {
26
    let mainWallet
27
    let defaultChainId
28
    this.signer = DefenderRelayer.getInstance()
5✔
29

30
    forOwn(walletsMap, (wallet, chainId) => {
5✔
31
      this.wallets.push(wallet)
5✔
32

33
      if (mainWallet) {
5!
UNCOV
34
        this.otherWallets.push(wallet)
×
35
      } else {
36
        mainWallet = wallet
5✔
37
        defaultChainId = chainId
5✔
38
      }
39
    })
40

41
    multiLogger.debug('MultiWallet constructor:', {
5✔
42
      wallets: Object.keys(walletsMap),
43
      mainWallet: mainWallet.networkId,
UNCOV
44
      otherWallets: this.otherWallets.map(_ => _.networkId)
×
45
    })
46

47
    assign(this, { walletsMap, mainWallet, defaultChainId })
5✔
48
  }
49

50
  signMessage(message: string) {
UNCOV
51
    return this.signer.signMessage(message)
×
52
  }
53

54
  async banInFaucet(account, chainId = 'all', log = multiLogger) {
×
UNCOV
55
    const runTx = wallet => wallet.banInFaucet(account, log)
×
56

UNCOV
57
    log.debug('MultiWallet: banInFaucet request:', { account, chainId })
×
58
    if (chainId === 'all') {
×
59
      // ban on chains that have ubi
UNCOV
60
      const results = await Promise.all(
×
61
        this.wallets.filter(_ => Number(_.UBIContract?._address) > 0).map(wallet => runTx(wallet).catch(e => e))
×
62
      )
UNCOV
63
      const error = results.find(isError)
×
64

UNCOV
65
      if (error) {
×
66
        throw error
×
67
      }
68

UNCOV
69
      return results
×
70
    }
71

UNCOV
72
    const { walletsMap } = this
×
73

UNCOV
74
    if (chainId in walletsMap) return runTx(walletsMap[chainId])
×
75

UNCOV
76
    throw new Error(`unsupported chain ${chainId}`)
×
77
  }
78

79
  async topWallet(account, chainId = null, log = multiLogger) {
×
UNCOV
80
    const runTx = wallet => wallet.topWallet(account, log)
×
81

82
    log.debug('MultiWallet: topWallet request:', { account, chainId })
83
    if (chainId === 'all') {
UNCOV
84
      // topwallet on chains that have ubi
×
85
      const results = await Promise.all(
86
        this.wallets.filter(_ => Number(_.UBIContract?._address) > 0).map(wallet => runTx(wallet).catch(e => e))
87
      )
UNCOV
88
      const error = results.find(isError)
×
89

×
90
      if (error) {
91
        throw error
92
      }
93

UNCOV
94
      return results
×
95
    }
96

97
    const { walletsMap } = this
UNCOV
98

×
99
    if (chainId in walletsMap) return runTx(walletsMap[chainId])
100

101
    throw new Error(`unsupported chain ${chainId}`)
×
UNCOV
102
  }
×
103

×
104
  async whitelistUser(account, did, chainId = null, log = multiLogger) {
UNCOV
105
    return Promise.all(this.wallets.map(wallet => wallet.whitelistUser(account, did, chainId, 0, log)))
×
106
  }
UNCOV
107

×
108
  async removeWhitelisted(account) {
×
109
    return Promise.all(this.wallets.map(wallet => wallet.removeWhitelisted(account)))
110
  }
UNCOV
111

×
112
  async verifiedStatus(account) {
UNCOV
113
    return Promise.all(
×
114
      this.wallets.map(wallet => wallet.isVerified(account).then(status => ({ chainId: wallet.networkId, status })))
×
115
    )
×
116
  }
UNCOV
117

×
118
  async isVerified(account) {
UNCOV
119
    return this.mainWallet.isVerified(account)
×
120
  }
UNCOV
121

×
122
  async lastAuthenticated(account) {
UNCOV
123
    return this.mainWallet.getLastAuthenticated(account)
×
124
  }
×
125

×
126
  async syncWhitelist(account, log = multiLogger) {
127
    try {
UNCOV
128
      const isVerified = await Promise.all(this.wallets.map(wallet => wallet.isVerified(account)))
×
129

130
      log.debug('syncwhitelist isVerified:', { account, isVerified })
131

UNCOV
132
      if (isEmpty(isVerified) || every(isVerified) || !some(isVerified)) {
×
133
        return false
UNCOV
134
      }
×
135

×
136
      const mainWallet = this.wallets[isVerified.findIndex(_ => _)]
137

138
      const [did, lastAuthenticated] = await Promise.all([
139
        mainWallet.getDID(account).catch(() => account),
UNCOV
140
        mainWallet.getLastAuthenticated(account).catch(() => 0)
×
141
      ])
142
      const chainId = mainWallet.networkId
143

×
144
      log.debug('syncwhitelist did:', { account, did, lastAuthenticated, chainId })
1✔
145

146
      await Promise.all(
147
        isVerified.map(async (status, index) => {
148
          log.debug('syncwhitelist whitelisting on wallet:', { status, index, account })
5✔
149
          if (status) {
5!
UNCOV
150
            return
×
151
          }
×
152

153
          await this.wallets[index].whitelistUser(account, did, chainId, lastAuthenticated, log)
154
        })
155
      )
156

157
      return true
158
    } catch (e) {
159
      log.error('syncwhitelist failed:', e.message, e, { account })
160
      throw e
161
    }
162
  }
163

164
  async getAuthenticationPeriod() {
165
    return this.mainWallet.getAuthenticationPeriod()
166
  }
167

168
  async registerRedtent(account: string, countryCode: string, log = multiLogger) {
169
    return Promise.all(this.wallets.map(wallet => wallet.registerRedtent(account, countryCode, log)))
170
  }
171
}
172

173
let otherWallets = {}
174
if (conf.env !== 'test') {
175
  const celoWallet = new CeloAdminWallet()
176
  otherWallets = {
177
    42220: celoWallet,
178
    8453: new BaseAdminWallet({}, celoWallet),
179
    50: new XdcAdminWallet({})
180
  }
181
}
182

183
export default new MultiWallet({
184
  122: AdminWallet, // "main" wallet goes first
185
  ...otherWallets
186
})
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