From 01f1182e76056812b09cc93ca04d9d178461a61c Mon Sep 17 00:00:00 2001 From: "goodbounties-nanoclaw-agent[bot]" <307944451+goodbounties-nanoclaw-agent[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 07:06:24 +0000 Subject: [PATCH 1/5] [gdpatchagent] fix: fall back to default admin wallet address when none are funded AdminWallet.init() left this.address undefined when every configured wallet was below the funding threshold, which later crashed the process with an invalid-address error on the next getBalance/getTransactionCount call. Fall back to the first configured address so the existing low-funds Slack alert can fire without taking the whole process down. Fixes #574 On-Behalf-Of: gdpatchagent[onecli] (yaskkeryodtdijpv) --- src/server/blockchain/Web3Wallet.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/server/blockchain/Web3Wallet.js b/src/server/blockchain/Web3Wallet.js index 326fc5f03..befb825db 100644 --- a/src/server/blockchain/Web3Wallet.js +++ b/src/server/blockchain/Web3Wallet.js @@ -400,7 +400,16 @@ export class Web3Wallet { break } } - // this.address = this.filledAddresses[0] + + // No configured wallet met the funding threshold above. Fall back to the first + // configured address instead of leaving `this.address` undefined, otherwise the + // getBalance/getTransactionCount calls below crash the process on an invalid + // (undefined) address even though the low-funds condition is already alerted on + // via Slack a few lines down. + if (!this.address) { + this.address = this.addresses[0] + } + this.proxyContract = new this.web3.eth.Contract(AdminWalletABI, adminWalletAddress, { from: this.address }) if (this.conf.topAdminsOnStartup) { From 0e80fbad48470e5fba230fa18681f8eff6444fb3 Mon Sep 17 00:00:00 2001 From: "goodbounties-nanoclaw-agent[bot]" <307944451+goodbounties-nanoclaw-agent[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 07:06:41 +0000 Subject: [PATCH 2/5] [gdpatchagent] test: cover adminWallet init fallback when no wallet is funded Regression test for #574 - verifies AdminWallet.init() no longer crashes the process (via process.exit) when every configured wallet is below the funding threshold, and that it falls back to the first configured address instead of leaving address undefined. On-Behalf-Of: gdpatchagent[onecli] (yaskkeryodtdijpv) --- .../blockchain/__tests__/adminWallet.test.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/server/blockchain/__tests__/adminWallet.test.js b/src/server/blockchain/__tests__/adminWallet.test.js index 0caa346d3..d76e32069 100644 --- a/src/server/blockchain/__tests__/adminWallet.test.js +++ b/src/server/blockchain/__tests__/adminWallet.test.js @@ -142,4 +142,23 @@ describe('adminwallet', () => { const { release } = await txManager.lock(unverifiedAddresses) await release() }) + + test('adminWallet init falls back to a default address instead of crashing when no wallet is funded', async () => { + const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {}) + const isVerifiedAdminSpy = jest.spyOn(AdminWallet, 'isVerifiedAdmin').mockResolvedValue(false) + + // init() appends to filledAddresses instead of resetting it, so clear out whatever + // the real (unmocked) init from beforeAll already populated before re-running it here. + AdminWallet.filledAddresses = [] + AdminWallet.address = undefined + + await AdminWallet.init() + + expect(AdminWallet.address).toBe(AdminWallet.addresses[0]) + expect(AdminWallet.filledAddresses).toEqual([]) + expect(exitSpy).not.toHaveBeenCalled() + + isVerifiedAdminSpy.mockRestore() + exitSpy.mockRestore() + }) }) From 2d9839b376deaac81fe348bb8fad47e36c50dec9 Mon Sep 17 00:00:00 2001 From: "goodbounties-nanoclaw-agent[bot]" <307944451+goodbounties-nanoclaw-agent[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 07:29:18 +0000 Subject: [PATCH 3/5] [gdpatchagent] fix: address sourcery-ai review feedback - Guard against an empty/misconfigured addresses list instead of silently falling back to an undefined address (throws a clear error and hits the existing catch/exit path). - Reset filledAddresses at the top of each init() run so re-running init() does not keep accumulating addresses from a previous run. On-Behalf-Of: gdpatchagent[onecli] (yaskkeryodtdijpv) --- src/server/blockchain/Web3Wallet.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/server/blockchain/Web3Wallet.js b/src/server/blockchain/Web3Wallet.js index befb825db..f157249a7 100644 --- a/src/server/blockchain/Web3Wallet.js +++ b/src/server/blockchain/Web3Wallet.js @@ -365,6 +365,9 @@ export class Web3Wallet { try { log.info('WalletInit: Obtained AdminWallet address', { adminWalletAddress, network: this.network }) + // Reset per-run so re-running init() doesn't keep stacking addresses found by a previous run. + this.filledAddresses = [] + const adminWalletContractBalance = await this.web3.eth.getBalance(adminWalletAddress) log.info(`WalletInit: AdminWallet contract balance`, { adminWalletContractBalance, adminWalletAddress }) @@ -405,8 +408,14 @@ export class Web3Wallet { // configured address instead of leaving `this.address` undefined, otherwise the // getBalance/getTransactionCount calls below crash the process on an invalid // (undefined) address even though the low-funds condition is already alerted on - // via Slack a few lines down. + // via Slack a few lines down. If there is no configured address at all, that's a + // genuine misconfiguration (no mnemonic/privateKey/KMS wallets set up) - fail loudly + // through the existing catch below instead of silently continuing with no address. if (!this.address) { + if (this.addresses.length === 0) { + throw new Error('WalletInit: no admin wallet addresses configured (missing mnemonic/privateKey/KMS wallets)') + } + this.address = this.addresses[0] } From abbf95919fb40d7347cc0b3d93b8fc76873ba2e5 Mon Sep 17 00:00:00 2001 From: "goodbounties-nanoclaw-agent[bot]" <307944451+goodbounties-nanoclaw-agent[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2026 07:29:34 +0000 Subject: [PATCH 4/5] [gdpatchagent] test: address sourcery-ai review feedback Wrap the regression test body in try/finally so the process.exit and isVerifiedAdmin mocks are always restored, even if an assertion throws, so they cannot leak into later tests. On-Behalf-Of: gdpatchagent[onecli] (yaskkeryodtdijpv) --- .../blockchain/__tests__/adminWallet.test.js | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/server/blockchain/__tests__/adminWallet.test.js b/src/server/blockchain/__tests__/adminWallet.test.js index d76e32069..0ea22e355 100644 --- a/src/server/blockchain/__tests__/adminWallet.test.js +++ b/src/server/blockchain/__tests__/adminWallet.test.js @@ -147,18 +147,19 @@ describe('adminwallet', () => { const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {}) const isVerifiedAdminSpy = jest.spyOn(AdminWallet, 'isVerifiedAdmin').mockResolvedValue(false) - // init() appends to filledAddresses instead of resetting it, so clear out whatever - // the real (unmocked) init from beforeAll already populated before re-running it here. - AdminWallet.filledAddresses = [] - AdminWallet.address = undefined - - await AdminWallet.init() - - expect(AdminWallet.address).toBe(AdminWallet.addresses[0]) - expect(AdminWallet.filledAddresses).toEqual([]) - expect(exitSpy).not.toHaveBeenCalled() - - isVerifiedAdminSpy.mockRestore() - exitSpy.mockRestore() + try { + // init() only sets this.address when a wallet qualifies, so clear the address the + // real (unmocked) init from beforeAll already found before re-running it here. + AdminWallet.address = undefined + + await AdminWallet.init() + + expect(AdminWallet.address).toBe(AdminWallet.addresses[0]) + expect(AdminWallet.filledAddresses).toEqual([]) + expect(exitSpy).not.toHaveBeenCalled() + } finally { + isVerifiedAdminSpy.mockRestore() + exitSpy.mockRestore() + } }) }) From 5a134fed90dcf0620500caedc2985834d4156d99 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:08:21 +0000 Subject: [PATCH 5/5] fix: replace process.exit(-1) with urgent Slack alert in AdminWallet.init() catch block Co-authored-by: L03TJ3 <6606028+L03TJ3@users.noreply.github.com> --- src/server/blockchain/Web3Wallet.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/server/blockchain/Web3Wallet.js b/src/server/blockchain/Web3Wallet.js index f157249a7..9a89d131c 100644 --- a/src/server/blockchain/Web3Wallet.js +++ b/src/server/blockchain/Web3Wallet.js @@ -516,9 +516,11 @@ export class Web3Wallet { } catch (e) { log.error('WalletInit: Error initializing wallet', e.message, e) - if (this.conf.env !== 'test' && this.conf.env !== 'development') { - process.exit(-1) - } + await sendSlackAlert({ + msg: `CRITICAL: AdminWallet init failed - ${e.message} ${this.name}` + }) + + return false } return true