Skip to content

Conversation

@kasumi-1
Copy link
Contributor

@kasumi-1 kasumi-1 commented Oct 5, 2025

Summary by CodeRabbit

  • New Features

    • Optional random reward system with in-chat winner notification and credits; Telegram-integrated deposit monitoring and wallet/payment flows with automatic refunds on failures; new Kasumi Telegram bot with commands, live task progress, and health/status reporting.
  • Configuration

    • New env options for reward chance/amount, winner image URL, and gas buffer percent; centralized app constants.
  • Improvements

    • Dynamic gas estimation with configurable buffer and fallbacks.
  • Tests

    • Large expansion of integration/unit tests across blockchain, gas estimation, deposits, IPFS, and error recovery.
  • Chores

    • Added data/ to .gitignore; dependency and CI action version bumps.

…ments

- Calculate required balance as shortfall plus 10% buffer
- Update error message to show detailed balance requirements
- Prevent transactions when balance is insufficient for buffered stake
- Moved /status logic into handleStatus method
- Updated /kasumi command to use handleStatus
- Dynamic examples in help using predefined prompts
- Removed duplicate /status command registration
- Integrate payment system with user wallet linking and AIUS token deposits
- Implement dynamic gas estimation with configurable buffer percentages
- Add user balance validation before task processing
- Include random reward system for completed tasks
- Display real-time progress updates during task processing
- Add comprehensive test coverage for gas estimation logic
- Handle task refunds and balance adjustments automatically
…dling

- Added extensive test suites for BlockchainService, DepositMonitor, HealthCheckServer, and related components
- Implemented edge case testing for transaction processing, deposit handling, and input validation
- Added tests for IPFS integration including timeout and retry scenarios
- Expanded coverage for task processing edge cases and payment finalization flows
- Included unicode, special character, and boundary condition testing
- Added concurrent access and error recovery test scenarios
@vercel
Copy link

vercel bot commented Oct 5, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
arbius Ready Ready Preview Comment Oct 5, 2025 8:11pm
arbius-docs Ready Ready Preview Comment Oct 5, 2025 8:11pm
arbius-explorer Ready Ready Preview Comment Oct 5, 2025 8:11pm
arbius-staging Ready Ready Preview Comment Oct 5, 2025 8:11pm

@coderabbitai
Copy link

coderabbitai bot commented Oct 5, 2025

Walkthrough

Integrates a payment system and Telegram-aware rewards/notifications into Kasumi3, centralizes constants (including gas buffer and rewards), implements buffered dynamic gas estimation with fallbacks, propagates telegramId and progress through TaskJob lifecycle, adds bot-backed deposit notifications, expands tests, and bumps several deps and CI actions.

Changes

Cohort / File(s) Summary
Env & Ignore
bots/kasumi-3/.env.example, bots/kasumi-3/.gitignore
Adds REWARD_CHANCE, REWARD_AMOUNT, WINNER_IMAGE_URL, GAS_BUFFER_PERCENT to env example; adds data/ to .gitignore.
App Constants
bots/kasumi-3/src/constants.ts
New centralized constants for timeouts, rate limits, job queue, blockchain defaults (gas buffers, fallbacks, nonce settings), health, deposits, rewards, gas estimation, IPFS/Arbius, and startup.
Bot Init & Wiring
bots/kasumi-3/src/index.ts, bots/kasumi-3/src/initPaymentSystem.ts
Rewires startup: create Telegraf bot, initialize payment system (DepositMonitor now receives poll interval and bot), pass payment userService/gasAccounting into TaskProcessor and Kasumi3Bot constructor.
Kasumi3 Bot Implementation
bots/kasumi-3/src/bot/Kasumi3Bot.ts
New exported Kasumi3Bot class accepting pre-initialized Telegraf bot and optional userService; implements commands, rate-limiting, status handling, submission flow, progress captions, winner notification, launch/shutdown APIs.
Blockchain Service
bots/kasumi-3/src/services/BlockchainService.ts
Adds GAS_BUFFER_PERCENT, FALLBACK_GAS_LIMITS, estimateGasWithBuffer helper; applies buffered estimates to submitTask/signalCommitment/submitSolution; enhanced nonce retry/cache, provider helpers, stake shortfall logic, commitment delay.
Deposit Monitor
bots/kasumi-3/src/services/DepositMonitor.ts
DepositMonitor now accepts optional Telegraf bot and DEPOSIT_MONITOR.POLL_INTERVAL_MS; sends Telegram notifications on credited deposits when bot present.
Task Processing & Types
bots/kasumi-3/src/services/TaskProcessor.ts, bots/kasumi-3/src/types/index.ts
Propagates telegramId in job payloads, adds progress updates, refunds on errors, random reward awarding via UserService.adminCredit controlled by REWARD_* env/constants; TaskJob type extended with telegramId, progress, wonReward.
Model Handler Timeout
bots/kasumi-3/src/services/ModelHandler.ts
Replaced hardcoded timeouts with centralized TIMEOUTS.REPLICATE_API.
Startup Tests / Init Payment
bots/kasumi-3/tests/services/initPaymentSystem.test.ts
Updated test expectation to include new poll interval and mock bot parameter.
Extensive Tests Added/Updated
bots/kasumi-3/tests/...
Large set of new and expanded tests: errorRecovery, BlockchainService comprehensive + dynamic gas tests, DepositMonitor, HealthCheckServer, DynamicGasEstimation docs/tests, InputValidation, TaskProcessor reward/payment edge cases, IPFS suites, and related adjustments.
IPFS Oracle
bots/ipfs-oracle/src/index.js, bots/ipfs-oracle/tests/*
Migrate to ethers v6 APIs (JsonRpcProvider, getBytes, SigningKey.sign, Signature.from), export main, add autoStart (no-op in test), and add startup/port/timeout tests.
Package & CI bumps
bots/kasumi-3/package.json, bots/ipfs-oracle/package.json, explorer/package.json, website2/package.json, .github/workflows/*
Dependency version updates (including ethers v6 in ipfs-oracle, ipfs-http-client, react, etc.) and CI actions/checkout & setup-node bumped v4 → v5.
Minor Tests Cleanup
bots/kasumi-3/tests/services/ipfs.test.ts, website2/...AAWalletProvider.test.tsx
Minor comment and window-handling test adjustments.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as Telegram User
  participant T as Telegraf Bot
  participant K as Kasumi3Bot
  participant P as TaskProcessor
  participant B as BlockchainService
  participant US as UserService
  participant PS as PaymentSystem

  U->>T: /task <prompt>
  T->>K: update/command
  K->>US: ensure wallet / validate
  K->>P: submitAndQueueTask({prompt, telegramId})
  P->>B: submitTask(...) (estimateGasWithBuffer)
  B->>B: estimateGasWithBuffer → gasLimit (estimator or FALLBACK)
  B-->>P: tx hash/receipt
  P->>K: progress updates (caption edits)
  P->>B: signalCommitment -> submitSolution (uses gasLimit)
  B-->>P: receipt
  alt success
    P->>US: maybe adminCredit (REWARD_CHANCE)
    P-->>K: completed {wonReward?}
    K-->>U: result + optional WINNER_IMAGE_URL
  else failure
    P->>US: refundTask
    P-->>K: failed
    K-->>U: failure + refund notice
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Kasumi3 updates, forge test, ci/cd, refactor #151 — Strong overlap with kasumi-3 payment wiring, TaskJob/type changes, gas estimation helpers, DepositMonitor bot param and reward/refund logic.
  • V6 prod #145 — Related to startup/init sequencing and task submission/status handling in kasumi-3 index/init modules.

Poem

🥕 I hopped through code with a twitch and a grin,
I buffered your gas and let rewards slip in.
Captions march forward, telegrams softly chime,
Deposits now ping, and tests run in time.
A rabbit’s small patch — stitched neatly, in code and in rhyme.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request lacks any description and does not follow the repository’s required template, which mandates “## Summary” and “## Test” sections. Without these sections, reviewers cannot easily understand the rationale behind the changes or how to verify them. The absence of a structured summary and testing instructions makes it difficult to assess the correctness and completeness of the work. Please add a pull request description using the repository’s template, including a detailed “## Summary” of the changes and a “## Test” section outlining how to verify the new functionality.
Title Check ❓ Inconclusive The title “Kasumi3 continued work” is overly generic and does not convey the primary changes or new functionality introduced in this pull request, such as the payment integration, gas estimation improvements, or reward system additions. It fails to highlight any specific feature or improvement that reviewers need to focus on. Because it lacks descriptive context, a teammate scanning the history would not understand the main purpose of this changeset. Please update the title to a concise, descriptive summary that highlights the most significant change (for example, “Integrate payment system and reward logic into Kasumi3 bot” or similar).
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- Upgrade actions/checkout from v4 to v5
- Upgrade actions/setup-node from v4 to v5

This ensures compatibility with the latest features and security updates
in GitHub Actions while maintaining existing workflow functionality.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
bots/kasumi-3/tests/services/InputValidation.test.ts (2)

472-482: Consider simplifying the address comparison.

The assertion on line 481 uses ethers.getAddress on both sides, which works but is verbose. Since the database stores lowercase addresses, a simpler comparison would be more direct.

-        expect(ethers.getAddress(result2.user?.wallet_address!)).toBe(ethers.getAddress(address2));
+        expect(result2.user?.wallet_address).toBe(address2.toLowerCase());

691-733: Thorough length limit testing.

The tests verify that prompts of various lengths (up to 100k characters) are accepted and preserved correctly. Note that no upper length limit is currently enforced by the validation logic - consider whether downstream systems (IPFS upload, model processing) might benefit from explicit length constraints.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de750ab and 4ac2bbc.

📒 Files selected for processing (17)
  • bots/kasumi-3/.env.example (1 hunks)
  • bots/kasumi-3/.gitignore (1 hunks)
  • bots/kasumi-3/src/index.ts (11 hunks)
  • bots/kasumi-3/src/services/BlockchainService.ts (7 hunks)
  • bots/kasumi-3/src/services/TaskProcessor.ts (6 hunks)
  • bots/kasumi-3/src/types/index.ts (1 hunks)
  • bots/kasumi-3/tests/integration/errorRecovery.test.ts (1 hunks)
  • bots/kasumi-3/tests/services/BlockchainService.comprehensive.test.ts (1 hunks)
  • bots/kasumi-3/tests/services/BlockchainService.test.ts (1 hunks)
  • bots/kasumi-3/tests/services/DepositMonitor.comprehensive.test.ts (1 hunks)
  • bots/kasumi-3/tests/services/DynamicGasEstimation.README.md (1 hunks)
  • bots/kasumi-3/tests/services/DynamicGasEstimation.test.ts (1 hunks)
  • bots/kasumi-3/tests/services/HealthCheckServer.comprehensive.test.ts (1 hunks)
  • bots/kasumi-3/tests/services/InputValidation.test.ts (1 hunks)
  • bots/kasumi-3/tests/services/TaskProcessor.test.ts (4 hunks)
  • bots/kasumi-3/tests/services/ipfs.comprehensive.test.ts (1 hunks)
  • bots/kasumi-3/tests/services/ipfs.test.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (10)
bots/kasumi-3/tests/services/BlockchainService.test.ts (1)
bots/kasumi-3/src/services/BlockchainService.ts (2)
  • BlockchainService (9-444)
  • estimateGasWithBuffer (115-130)
bots/kasumi-3/tests/services/InputValidation.test.ts (2)
bots/kasumi-3/src/services/DatabaseService.ts (1)
  • DatabaseService (55-455)
bots/kasumi-3/src/services/UserService.ts (1)
  • UserService (8-558)
bots/kasumi-3/tests/services/HealthCheckServer.comprehensive.test.ts (1)
bots/kasumi-3/src/services/HealthCheckServer.ts (1)
  • HealthCheckServer (20-205)
bots/kasumi-3/tests/services/BlockchainService.comprehensive.test.ts (1)
bots/kasumi-3/src/services/BlockchainService.ts (3)
  • BlockchainService (9-444)
  • getNonceWithRetry (78-110)
  • executeTransaction (135-176)
bots/kasumi-3/tests/services/DynamicGasEstimation.test.ts (1)
bots/kasumi-3/src/services/BlockchainService.ts (2)
  • BlockchainService (9-444)
  • estimateGasWithBuffer (115-130)
bots/kasumi-3/tests/services/TaskProcessor.test.ts (1)
bots/kasumi-3/src/services/TaskProcessor.ts (1)
  • TaskProcessor (15-331)
bots/kasumi-3/tests/integration/errorRecovery.test.ts (4)
bots/kasumi-3/src/services/BlockchainService.ts (1)
  • BlockchainService (9-444)
bots/kasumi-3/src/services/JobQueue.ts (1)
  • JobQueue (6-211)
bots/kasumi-3/src/services/UserService.ts (1)
  • UserService (8-558)
bots/kasumi-3/src/services/DatabaseService.ts (1)
  • DatabaseService (55-455)
bots/kasumi-3/tests/services/DepositMonitor.comprehensive.test.ts (3)
bots/kasumi-3/src/services/DepositMonitor.ts (1)
  • DepositMonitor (10-206)
bots/kasumi-3/src/services/UserService.ts (1)
  • UserService (8-558)
bots/kasumi-3/src/services/DatabaseService.ts (1)
  • DatabaseService (55-455)
bots/kasumi-3/src/index.ts (5)
bots/kasumi-3/src/services/BlockchainService.ts (1)
  • BlockchainService (9-444)
bots/kasumi-3/src/services/ModelRegistry.ts (1)
  • ModelRegistry (6-126)
bots/kasumi-3/src/services/JobQueue.ts (1)
  • JobQueue (6-211)
bots/kasumi-3/src/services/TaskProcessor.ts (1)
  • TaskProcessor (15-331)
bots/kasumi-3/src/initPaymentSystem.ts (1)
  • initializePaymentSystem (43-89)
bots/kasumi-3/tests/services/ipfs.comprehensive.test.ts (1)
bots/kasumi-3/src/ipfs.ts (1)
  • fetchFromIPFS (29-62)
🪛 dotenv-linter (3.3.0)
bots/kasumi-3/.env.example

[warning] 30-30: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 31-31: [UnorderedKey] The REWARD_AMOUNT key should go before the REWARD_CHANCE key

(UnorderedKey)


[warning] 31-31: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 32-32: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 35-35: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)

🪛 markdownlint-cli2 (0.18.1)
bots/kasumi-3/tests/services/DynamicGasEstimation.README.md

128-128: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: lighthouse (website2, http://localhost:3000)
  • GitHub Check: coverage (website2)
  • GitHub Check: lighthouse (explorer, http://localhost:3001)
  • GitHub Check: coverage (explorer)
  • GitHub Check: coverage (contract)
🔇 Additional comments (4)
bots/kasumi-3/tests/services/InputValidation.test.ts (3)

7-386: Excellent comprehensive test coverage for input validation.

The test suite thoroughly covers required fields, type validation, range checks, enum validation, defaults, edge cases, and special character handling. Well-organized with clear test descriptions.


486-643: Solid command parsing and sanitization tests.

Comprehensive coverage of prompt extraction, model name normalization, ID validation, and amount parsing with appropriate edge cases. The tests correctly verify string manipulation, regex patterns, and ethers utility functions.


645-689: Good security-focused validation.

The tests correctly verify that potentially malicious input (XSS, SQL injection, null bytes) is stored as-is without execution. The comments appropriately note that SQL injection prevention occurs at the database layer through parameterized queries, which is the correct approach.

bots/kasumi-3/tests/services/ipfs.comprehensive.test.ts (1)

21-167: Excellent expanded coverage on fetchFromIPFS edge cases.

The parallel gateway, timeout, and large-payload scenarios significantly strengthen confidence around the new Promise.any implementation and error surfacing. Nicely done.

Comment on lines 292 to 312
ctx.reply(
`🔍 Kasumi-3 Status\n\n` +
`${healthStatus}\n\n` +
`**Wallet**\n` +
`Address: \`${address.slice(0, 10)}...${address.slice(-8)}\`\n` +
`AIUS: ${ethers.formatEther(arbiusBalance)} ${hasEnoughAius ? '✅' : '⚠️'}\n` +
`ETH: ${ethers.formatEther(ethBalance)} ${hasEnoughGas ? '✅' : '⚠️'}\n` +
`Staked: ${ethers.formatEther(validatorStaked)} / ${ethers.formatEther(validatorMinimum)} ${isStakedEnough ? '✅' : '⚠️'}\n\n` +
`**Job Queue**\n` +
`Total: ${queueStats.total}\n` +
`Pending: ${queueStats.pending}\n` +
`Processing: ${queueStats.processing} ${queueHealthy ? '✅' : '⚠️'}\n` +
`Completed: ${queueStats.completed}\n` +
`Failed: ${queueStats.failed}\n\n` +
`**System**\n` +
`Uptime: ${uptimeHours}h ${uptimeMinutes % 60}m\n` +
`Active Users: ${rateLimiterStats.activeUsers}\n` +
`Models: ${this.modelRegistry.getAllModels().length}\n` +
`Rate Limit: ${rateLimiterStats.config.maxRequests} req/${rateLimiterStats.config.windowMs / 1000}s` +
warningsText,
{ parse_mode: 'Markdown' }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix Markdown formatting in /kasumi status reply

We send the status block with parse_mode: 'Markdown', but the text uses **bold** markers. Telegram’s legacy Markdown parser only understands single *bold*, so the user will literally see **Wallet**, **Job Queue**, etc. Please switch to single-asterisk formatting (or upgrade to MarkdownV2 + proper escaping) so the diagnostics render correctly for users.

🤖 Prompt for AI Agents
In bots/kasumi-3/src/index.ts around lines 292 to 312, the status reply uses
double-asterisk bold markers while sending parse_mode: 'Markdown' (Telegram
legacy), which causes the `**bold**` to render verbatim; change all
double-asterisk bold markers to single asterisks (e.g. `**Wallet**` ->
`*Wallet*`, `**Job Queue**` -> `*Job Queue*`, `**System**` -> `*System*`) so
headings render correctly, or alternatively set parse_mode to 'MarkdownV2' and
properly escape all special characters — pick one approach and apply
consistently across the template.

Comment on lines 109 to 128
// Random reward system: 1 in X chance to win reward
if (this.userService && job.chatId) {
const rewardChance = parseInt(process.env.REWARD_CHANCE || '20'); // Default 1 in 20
const rewardAmount = ethers.parseEther(process.env.REWARD_AMOUNT || '1'); // Default 1 AIUS

const randomNum = Math.floor(Math.random() * rewardChance);
if (randomNum === 0) {
// Winner!
const telegramId = (job as any).telegramId;
if (telegramId) {
const success = this.userService.adminCredit(
telegramId,
rewardAmount,
`Lucky reward for task ${job.taskid}`
);
if (success) {
log.info(`🎉 User ${telegramId} won ${ethers.formatEther(rewardAmount)} AIUS reward!`);
this.jobQueue.updateJobStatus(job.id, 'completed', { cid, wonReward: true });
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against zero/invalid reward chance before handing out credits

Math.floor(Math.random() * rewardChance) will always yield 0 whenever REWARD_CHANCE resolves to 0 (a very common value to disable rewards). That makes the if (randomNum === 0) branch fire on every successful task, silently crediting every user. Please treat non-positive/invalid configurations as “rewards disabled” instead of letting them fall through to the payout logic. While you are here you can rely on the newly typed job.telegramId rather than casting to any.

Apply something along these lines:

-        const rewardChance = parseInt(process.env.REWARD_CHANCE || '20'); // Default 1 in 20
-        const rewardAmount = ethers.parseEther(process.env.REWARD_AMOUNT || '1'); // Default 1 AIUS
-
-        const randomNum = Math.floor(Math.random() * rewardChance);
-        if (randomNum === 0) {
+        const rewardChanceRaw = parseInt(process.env.REWARD_CHANCE || '20', 10); // Default 1 in 20
+        if (!Number.isFinite(rewardChanceRaw) || rewardChanceRaw <= 0) {
+          log.warn(`Skipping reward payout: invalid REWARD_CHANCE="${process.env.REWARD_CHANCE}"`);
+        } else if (Math.floor(Math.random() * rewardChanceRaw) === 0) {
+          const rewardAmount = ethers.parseEther(process.env.REWARD_AMOUNT || '1'); // Default 1 AIUS
+          const telegramId = job.telegramId;
-          const telegramId = (job as any).telegramId;
           if (telegramId) {

This keeps rewards opt-in and prevents accidental “reward flood” misconfiguration, while making the type usage consistent.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In bots/kasumi-3/src/services/TaskProcessor.ts around lines 109 to 128, guard
against zero/invalid REWARD_CHANCE and use the typed job.telegramId: validate
rewardChance after parsing (treat NaN or <= 0 as rewards disabled and skip the
random/reward branch entirely), keep the default fallback but ensure parseInt
result is > 0 before calling Math.random(), and use job.telegramId (no any cast)
when calling userService.adminCredit; only call adminCredit and updateJobStatus
when rewardChance is valid and a winner is determined.

Comment on lines +126 to +127
jobQueue = new JobQueue(db);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix JobQueue constructor usage

JobQueue expects a numeric maxConcurrent (and optional callbacks/timeouts). Passing the DatabaseService instance as the first argument fails TypeScript checks and, even if coerced, leaves maxConcurrent as [object Object], breaking queue throttling logic. Construct it with a number (or rely on the default) and keep the database separate.

-    jobQueue = new JobQueue(db);
+    jobQueue = new JobQueue();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
jobQueue = new JobQueue(db);
});
jobQueue = new JobQueue();
});
🤖 Prompt for AI Agents
In bots/kasumi-3/tests/integration/errorRecovery.test.ts around lines 126-127,
the test calls new JobQueue(db) but JobQueue's first parameter is maxConcurrent
(a number), so passing the DatabaseService breaks TypeScript and queue
throttling; change the constructor call to provide a numeric maxConcurrent (or
omit it to use the default) and assign or inject the DatabaseService separately
(e.g., via a setter or by passing it in the proper options/parameters expected
by JobQueue) so the queue gets a valid number and the DB remains a separate
dependency.

Comment on lines +52 to +123
it('should cache nonce for 5 seconds', async () => {
const getNonceWithRetry = (blockchain as any).getNonceWithRetry.bind(blockchain);

// Mock provider getTransactionCount
const mockGetTxCount = vi.fn().mockResolvedValue(5);
vi.spyOn((blockchain as any).provider, 'getBlockNumber').mockResolvedValue(1000);

// First call should query RPC
const nonceCache = (blockchain as any).nonceCache;
expect(nonceCache).toBeNull();

// Manually set up the mock for multiple RPC calls
const originalRpcUrls = (blockchain as any).rpcUrls;
expect(originalRpcUrls).toBeDefined();
});

it('should increment cached nonce on subsequent calls', async () => {
// Set up nonce cache manually
(blockchain as any).nonceCache = { nonce: 10, timestamp: Date.now() };

const getNonceWithRetry = (blockchain as any).getNonceWithRetry.bind(blockchain);

const nonce1 = await getNonceWithRetry();
expect(nonce1).toBe(10);

const nonce2 = await getNonceWithRetry();
expect(nonce2).toBe(11);

const nonce3 = await getNonceWithRetry();
expect(nonce3).toBe(12);
});

it('should clear cache after 5 seconds', async () => {
const fiveSecondsAgo = Date.now() - 6000;
(blockchain as any).nonceCache = { nonce: 5, timestamp: fiveSecondsAgo };

const getNonceWithRetry = (blockchain as any).getNonceWithRetry.bind(blockchain);

// Should re-query because cache is stale
// This will fail in the test because we can't mock the RPC properly
// but demonstrates the cache expiry logic
const cache = (blockchain as any).nonceCache;
const isExpired = Date.now() - cache.timestamp >= 5000;
expect(isExpired).toBe(true);
});

it('should use highest nonce from multiple RPCs', async () => {
const multiRpcBlockchain = new BlockchainService(
'http://rpc1.com,http://rpc2.com,http://rpc3.com',
TEST_PRIVATE_KEY,
TEST_ARBIUS_ADDRESS,
TEST_ROUTER_ADDRESS,
TEST_TOKEN_ADDRESS
);

const rpcUrls = (multiRpcBlockchain as any).rpcUrls;
expect(rpcUrls).toHaveLength(3);
expect(rpcUrls[0]).toBe('http://rpc1.com');
expect(rpcUrls[1]).toBe('http://rpc2.com');
expect(rpcUrls[2]).toBe('http://rpc3.com');
});

it('should handle RPC failure when getting nonce', async () => {
(blockchain as any).nonceCache = null;

const getNonceWithRetry = (blockchain as any).getNonceWithRetry.bind(blockchain);

// This tests that the error handling exists
// In a real scenario, at least one RPC should succeed
expect(getNonceWithRetry).toBeDefined();
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Nonce cache tests never exercise the implementation

Both “should cache nonce for 5 seconds” and “should clear cache after 5 seconds” stop after grabbing getNonceWithRetry/nonceCache and inspecting fields; they never invoke the method or assert its behaviour. As written they will succeed even if getNonceWithRetry is completely broken, which gives us a false sense of coverage over the nonce cache/regression path. Please refactor these tests to actually drive getNonceWithRetry (e.g., by mocking ethers.JsonRpcProvider/getTransactionCount) or remove them until we can validate the behaviour for real.

Comment on lines +436 to +438
// DB stores checksummed address
expect(ethers.getAddress(result.user?.wallet_address!)).toBe(ethers.getAddress(lowercaseAddress));
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Misleading comment about address storage format.

The comment states "DB stores checksummed address" but according to the DatabaseService implementation, addresses are stored in lowercase (via .toLowerCase() in upsertUser). The assertion works because ethers.getAddress normalizes both sides to checksummed format, but it doesn't verify the actual storage format in the database.

Consider updating the comment and assertion to be more precise:

-      // DB stores checksummed address
-      expect(ethers.getAddress(result.user?.wallet_address!)).toBe(ethers.getAddress(lowercaseAddress));
+      // DB stores lowercase address; verify it matches the input (case-insensitive)
+      expect(result.user?.wallet_address).toBe(lowercaseAddress.toLowerCase());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// DB stores checksummed address
expect(ethers.getAddress(result.user?.wallet_address!)).toBe(ethers.getAddress(lowercaseAddress));
});
// DB stores lowercase address; verify it matches the input (case-insensitive)
expect(result.user?.wallet_address).toBe(lowercaseAddress.toLowerCase());
});
🤖 Prompt for AI Agents
In bots/kasumi-3/tests/services/InputValidation.test.ts around lines 436 to 438,
the comment incorrectly says "DB stores checksummed address" but the
DatabaseService lowercases addresses; update the comment to "DB stores lowercase
address" and change the assertion to verify the stored DB value is lowercase
(e.g. compare result.user?.wallet_address to lowercaseAddress.toLowerCase())
instead of normalizing both sides with ethers.getAddress so the test asserts the
actual storage format.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/coverage.yml (1)

16-25: Same issue: v5 tags are unavailable

This workflow has the same problem: actions/checkout@v5 and actions/setup-node@v5 are not published, so the run will error out before your jobs start. Revert to @v4 (or pin commits) to keep CI green.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ac2bbc and a409dd5.

⛔ Files ignored due to path filters (4)
  • bots/ipfs-oracle/package-lock.json is excluded by !**/package-lock.json
  • bots/kasumi-3/package-lock.json is excluded by !**/package-lock.json
  • explorer/package-lock.json is excluded by !**/package-lock.json
  • website2/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • .github/workflows/coverage.yml (1 hunks)
  • .github/workflows/lighthouse.yml (1 hunks)
  • bots/ipfs-oracle/package.json (1 hunks)
  • bots/ipfs-oracle/src/index.js (4 hunks)
  • bots/ipfs-oracle/tests/main.test.js (1 hunks)
  • bots/ipfs-oracle/tests/port-timeout.test.js (1 hunks)
  • bots/ipfs-oracle/tests/startup.test.js (1 hunks)
  • bots/kasumi-3/package.json (1 hunks)
  • explorer/package.json (2 hunks)
  • website2/package.json (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
bots/ipfs-oracle/tests/startup.test.js (2)
bots/ipfs-oracle/tests/main.test.js (1)
  • server (5-5)
bots/ipfs-oracle/src/index.js (4)
  • app (52-52)
  • app (52-52)
  • autoStart (102-106)
  • port (12-12)
bots/ipfs-oracle/tests/main.test.js (1)
bots/ipfs-oracle/src/index.js (4)
  • app (52-52)
  • app (52-52)
  • port (12-12)
  • main (55-62)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
🔇 Additional comments (1)
website2/package.json (1)

29-31: Peer dependencies support React 19.2.0 Next 15.5.4 and next-themes both allow React ^19, so the bump is safe.

Comment on lines +22 to 26
uses: actions/checkout@v5

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Broken by nonexistent v5 actions

actions/checkout@v5 and actions/setup-node@v5 do not exist (latest is v4), so the workflow will fail when GitHub tries to resolve these tags. Please stick with the published v4 release (or pin a specific commit) until v5 is available.

🤖 Prompt for AI Agents
.github/workflows/lighthouse.yml lines 22-26: the workflow references
nonexistent action versions actions/checkout@v5 and actions/setup-node@v5 which
will cause resolution failures; update those uses to the published v4 releases
(e.g., actions/checkout@v4 and actions/setup-node@v4) or pin to a specific
commit/tag, then test the workflow to ensure the actions resolve successfully.

Comment on lines +102 to +105
export function autoStart() {
if (process.env.NODE_ENV !== 'test') {
main();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Preserve the main() promise in autoStart.

Right now we call main() but drop the promise, so any startup failure becomes an unhandled rejection and callers (including the new tests) cannot reliably await completion. Return the promise so errors surface and await autoStart() actually waits.

 export function autoStart() {
   if (process.env.NODE_ENV !== 'test') {
-    main();
+    return main();
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function autoStart() {
if (process.env.NODE_ENV !== 'test') {
main();
}
export function autoStart() {
if (process.env.NODE_ENV !== 'test') {
return main();
}
}
🤖 Prompt for AI Agents
In bots/ipfs-oracle/src/index.js around lines 102 to 105, autoStart currently
calls main() without returning its promise which causes unhandled rejections and
prevents callers from awaiting startup; change autoStart to return the promise
from main() when starting (and return a resolved promise when skipping start for
NODE_ENV === 'test') so callers can await autoStart() and receive any errors.

},
"devDependencies": {
"@types/uuid": "^11.0.0",
"@types/uuid": "^10.0.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Revert the @types/uuid downgrade

Runtime uuid remains at ^13.0.0, but @types/uuid was rolled back to ^10.0.0. That version predates the pure-ESM/inline typings shipped with uuid@13 and still declares the old CommonJS default export. Keeping it alongside uuid@13 resurrects conflicting ambient declarations and breaks TypeScript builds (TS2307/TS1259). Please either drop the external typings altogether (preferred—uuid already ships its own) or restore the prior @types/uuid version that matches the ESM signature.

🤖 Prompt for AI Agents
In bots/kasumi-3/package.json around line 37, the project lists "@types/uuid":
"^10.0.0" while runtime "uuid" is still ^13.0.0, which reintroduces conflicting
CommonJS ambient typings and breaks TypeScript; remove the external
"@types/uuid" entry (preferred) so the built-in ESM/inline typings from uuid@13
are used, or alternatively restore the previously working @types/uuid version
that explicitly matches uuid@13's ESM typings, then reinstall deps (npm/yarn)
and verify TypeScript builds cleanly.

Improve test reliability by properly managing the window.ethereum mock lifecycle.
Only the ethereum property is deleted during cleanup instead of the entire window
object, preventing potential interference with other tests that might rely on
window properties.
…constants

This commit introduces a centralized constants module to manage application-wide configuration values. Key changes include:
- Moving hardcoded timeouts, rate limits, and other configuration values to src/constants.ts
- Refactoring BlockchainService, DepositMonitor, TaskProcessor and other services to use constants
- Updating IPFS gateway configuration and reward system parameters
- Improving type safety with const assertions for constant objects

The refactoring improves maintainability by centralizing configuration values and reducing hardcoded magic numbers throughout the codebase.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bots/kasumi-3/src/index.ts (1)

87-103: Temporary null assignment creates unsafe state

Line 90 assigns null as any to jobQueue in the TaskProcessor constructor, then line 103 reassigns it via (taskProcessor as any).jobQueue = jobQueue. This creates a temporary state where taskProcessor has an invalid jobQueue reference.

This pattern is fragile and could cause issues if TaskProcessor methods are called before line 103. Consider refactoring to avoid the circular dependency:

Option 1: Use a setter method

// In TaskProcessor
setJobQueue(jobQueue: JobQueue): void {
  this.jobQueue = jobQueue;
}

Option 2: Initialize TaskProcessor after JobQueue
If the TaskProcessor doesn't need to be passed to JobQueue constructor, reorder the initialization:

const jobQueue = new JobQueue(maxConcurrent, undefined, jobTimeoutMs);
const taskProcessor = new TaskProcessor(
  blockchain,
  miningConfig,
  jobQueue,
  paymentSystem.userService,
  paymentSystem.gasAccounting
);
// Set the processor callback after both are initialized
jobQueue.setOnJobComplete(async (job: TaskJob) => {
  try {
    await taskProcessor.processTask(job);
  } catch (err: any) {
    log.error(`Failed to process job ${job.id}: ${err.message}`);
  }
});

This would require changes to JobQueue to accept an optional callback and allow setting it after construction.

♻️ Duplicate comments (2)
bots/kasumi-3/src/services/TaskProcessor.ts (1)

111-129: Prevent invalid reward chance from auto-paying every task

A REWARD_CHANCE of 0 (common to disable rewards) makes Math.floor(Math.random() * rewardChance) always 0, so every successful task is paid out. This was already flagged earlier; please treat non‑positive/invalid configs as “rewards disabled” and only draw a random winner when the chance is valid. While adjusting, you can rely on the typed job.telegramId instead of casting.

-        const rewardChance = parseInt(process.env.REWARD_CHANCE || String(REWARDS.CHANCE_DEFAULT));
-        const rewardAmount = ethers.parseEther(process.env.REWARD_AMOUNT || REWARDS.AMOUNT_DEFAULT);
-
-        const randomNum = Math.floor(Math.random() * rewardChance);
-        if (randomNum === 0) {
-          // Winner!
-          const telegramId = (job as any).telegramId;
-          if (telegramId) {
-            const success = this.userService.adminCredit(
-              telegramId,
-              rewardAmount,
-              `Lucky reward for task ${job.taskid}`
-            );
+        const rewardChanceRaw = parseInt(process.env.REWARD_CHANCE || String(REWARDS.CHANCE_DEFAULT), 10);
+        if (!Number.isFinite(rewardChanceRaw) || rewardChanceRaw <= 0) {
+          log.warn(`Skipping reward payout: invalid REWARD_CHANCE="${process.env.REWARD_CHANCE}"`);
+        } else if (Math.floor(Math.random() * rewardChanceRaw) === 0) {
+          const rewardAmount = ethers.parseEther(process.env.REWARD_AMOUNT || REWARDS.AMOUNT_DEFAULT);
+          const telegramId = job.telegramId;
+          if (telegramId) {
+            const success = this.userService.adminCredit(
+              telegramId,
+              rewardAmount,
+              `Lucky reward for task ${job.taskid}`
+            );
bots/kasumi-3/src/bot/Kasumi3Bot.ts (1)

289-310: Fix Markdown formatting in status reply

The status reply uses double-asterisk **bold** markers with parse_mode: 'Markdown'. Telegram's legacy Markdown parser only recognizes single asterisks for bold (*text*), so the double asterisks will render literally as **Wallet**, **Job Queue**, etc.

Apply this diff to fix the formatting:

       ctx.reply(
         `🔍 Kasumi-3 Status\n\n` +
         `${healthStatus}\n\n` +
-        `**Wallet**\n` +
+        `*Wallet*\n` +
         `Address: \`${address.slice(0, 10)}...${address.slice(-8)}\`\n` +
         `AIUS: ${ethers.formatEther(arbiusBalance)} ${hasEnoughAius ? '✅' : '⚠️'}\n` +
         `ETH: ${ethers.formatEther(ethBalance)} ${hasEnoughGas ? '✅' : '⚠️'}\n` +
         `Staked: ${ethers.formatEther(validatorStaked)} / ${ethers.formatEther(validatorMinimum)} ${isStakedEnough ? '✅' : '⚠️'}\n\n` +
-        `**Job Queue**\n` +
+        `*Job Queue*\n` +
         `Total: ${queueStats.total}\n` +
         `Pending: ${queueStats.pending}\n` +
         `Processing: ${queueStats.processing} ${queueHealthy ? '✅' : '⚠️'}\n` +
         `Completed: ${queueStats.completed}\n` +
         `Failed: ${queueStats.failed}\n\n` +
-        `**System**\n` +
+        `*System*\n` +
         `Uptime: ${uptimeHours}h ${uptimeMinutes % 60}m\n` +
         `Active Users: ${rateLimiterStats.activeUsers}\n` +
         `Models: ${this.modelRegistry.getAllModels().length}\n` +
🧹 Nitpick comments (3)
bots/kasumi-3/src/bot/Kasumi3Bot.ts (2)

64-81: Consider extracting rate limit response message

The rate limit error message is constructed inline. Consider extracting it to a constant or helper function for consistency with other messages and easier maintenance.

You could extract the message construction to a helper:

+    const formatRateLimitMessage = (resetTime: number, limit: string) =>
+      `⏱️ Rate limit exceeded. Please wait ${resetTime} seconds before trying again.\n\n` +
+      `Limit: ${limit}`;
+
     this.bot.use(async (ctx, next) => {
       const userId = ctx.from?.id;
       if (!userId) {
         return next();
       }
 
       if (!this.rateLimiter.checkLimit(userId)) {
         const resetTime = this.rateLimiter.getResetTime(userId);
         log.debug(`User ${userId} rate limited, ${resetTime}s until reset`);
-        await ctx.reply(
-          `⏱️ Rate limit exceeded. Please wait ${resetTime} seconds before trying again.\n\n` +
-          `Limit: ${RATE_LIMITS.REQUESTS_PER_MINUTE_TEXT}`
-        );
+        await ctx.reply(formatRateLimitMessage(resetTime, RATE_LIMITS.REQUESTS_PER_MINUTE_TEXT));
         return;
       }

206-212: Fallback error handling could be more specific

The catch block at line 209 logs the error but uses a generic text fallback message. Consider providing more context about why the photo failed (e.g., network issue, invalid URL, Telegram API limits).

     } catch (e) {
-      log.debug(`Failed to send initial photo, using text fallback: ${e}`);
+      log.debug(`Failed to send initial photo (${e instanceof Error ? e.message : String(e)}), using text fallback`);
       responseCtx = await ctx.reply(`🔄 Processing with ${modelConfig.template.meta.title}...`);
     }
bots/kasumi-3/src/services/BlockchainService.ts (1)

226-240: Stake buffer calculation has potential precision loss

Line 232-233 compute the requiredBalance with a buffer, but the arithmetic could lose precision due to the intermediate conversion to Math.floor() and BigInt operations:

const bufferMultiplier = 1 + (BLOCKCHAIN.STAKE_BUFFER_PERCENT / 100);
const requiredBalance = shortfall * BigInt(Math.floor(bufferMultiplier * 100)) / 100n;

With STAKE_BUFFER_PERCENT = 10, this becomes:

  • bufferMultiplier = 1.1
  • Math.floor(1.1 * 100) = 110
  • requiredBalance = shortfall * 110n / 100n

This is correct, but consider whether the precision is sufficient for all cases.

For better clarity and avoiding floating-point operations, consider:

-      const bufferMultiplier = 1 + (BLOCKCHAIN.STAKE_BUFFER_PERCENT / 100);
-      const requiredBalance = shortfall * BigInt(Math.floor(bufferMultiplier * 100)) / 100n;
+      // Calculate required balance with buffer: shortfall * (100 + buffer%) / 100
+      const requiredBalance = shortfall * BigInt(100 + BLOCKCHAIN.STAKE_BUFFER_PERCENT) / 100n;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a409dd5 and 9bab5d2.

📒 Files selected for processing (10)
  • bots/kasumi-3/src/bot/Kasumi3Bot.ts (1 hunks)
  • bots/kasumi-3/src/constants.ts (1 hunks)
  • bots/kasumi-3/src/index.ts (3 hunks)
  • bots/kasumi-3/src/initPaymentSystem.ts (2 hunks)
  • bots/kasumi-3/src/services/BlockchainService.ts (13 hunks)
  • bots/kasumi-3/src/services/DepositMonitor.ts (3 hunks)
  • bots/kasumi-3/src/services/ModelHandler.ts (3 hunks)
  • bots/kasumi-3/src/services/TaskProcessor.ts (11 hunks)
  • bots/kasumi-3/tests/services/initPaymentSystem.test.ts (1 hunks)
  • website2/src/lib/arbius-wallet/__tests__/components/AAWalletProvider.test.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
bots/kasumi-3/src/services/DepositMonitor.ts (2)
bots/kasumi-3/src/services/UserService.ts (1)
  • UserService (8-558)
bots/kasumi-3/src/constants.ts (1)
  • DEPOSIT_MONITOR (54-57)
bots/kasumi-3/src/services/ModelHandler.ts (1)
bots/kasumi-3/src/constants.ts (1)
  • TIMEOUTS (6-13)
bots/kasumi-3/src/initPaymentSystem.ts (2)
bots/kasumi-3/src/services/DepositMonitor.ts (1)
  • DepositMonitor (12-227)
bots/kasumi-3/src/constants.ts (1)
  • DEPOSIT_MONITOR (54-57)
bots/kasumi-3/src/services/BlockchainService.ts (1)
bots/kasumi-3/src/constants.ts (1)
  • BLOCKCHAIN (30-44)
bots/kasumi-3/src/bot/Kasumi3Bot.ts (9)
bots/kasumi-3/src/services/BlockchainService.ts (1)
  • BlockchainService (10-467)
bots/kasumi-3/src/services/ModelRegistry.ts (1)
  • ModelRegistry (6-126)
bots/kasumi-3/src/services/JobQueue.ts (1)
  • JobQueue (6-211)
bots/kasumi-3/src/services/TaskProcessor.ts (1)
  • TaskProcessor (16-342)
bots/kasumi-3/src/types/index.ts (3)
  • MiningConfig (4-16)
  • ModelConfig (87-93)
  • TaskJob (104-120)
bots/kasumi-3/src/services/RateLimiter.ts (2)
  • RateLimiter (16-139)
  • cleanup (100-114)
bots/kasumi-3/src/services/HealthCheckServer.ts (1)
  • HealthCheckServer (20-205)
bots/kasumi-3/src/services/UserService.ts (1)
  • UserService (8-558)
bots/kasumi-3/src/constants.ts (7)
  • RATE_LIMITS (16-20)
  • STARTUP (73-75)
  • ARBIUS (83-85)
  • HEALTH_CHECK (47-51)
  • TIMEOUTS (6-13)
  • REWARDS (60-64)
  • JOB_QUEUE (23-27)
bots/kasumi-3/src/index.ts (5)
bots/kasumi-3/src/constants.ts (2)
  • JOB_QUEUE (23-27)
  • TIMEOUTS (6-13)
bots/kasumi-3/src/config.ts (1)
  • ConfigLoader (9-100)
bots/kasumi-3/src/initPaymentSystem.ts (1)
  • initializePaymentSystem (44-92)
bots/kasumi-3/src/services/TaskProcessor.ts (1)
  • TaskProcessor (16-342)
bots/kasumi-3/src/bot/Kasumi3Bot.ts (1)
  • Kasumi3Bot (21-605)
bots/kasumi-3/src/services/TaskProcessor.ts (1)
bots/kasumi-3/src/constants.ts (2)
  • REWARDS (60-64)
  • GAS_ESTIMATION (67-70)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
🔇 Additional comments (19)
website2/src/lib/arbius-wallet/__tests__/components/AAWalletProvider.test.tsx (2)

22-27: LGTM! Improved test isolation.

The change to preserve the existing window object instead of replacing it entirely prevents potential side effects between tests and follows defensive programming best practices. The pattern window = window || {} ensures safe initialization while preserving any existing properties.


30-35: LGTM! Precise teardown matches setup.

The targeted cleanup of only window.ethereum is consistent with the setup changes and prevents inadvertently removing other window properties that may be needed by subsequent tests. The safety check adds defensive robustness.

bots/kasumi-3/src/bot/Kasumi3Bot.ts (6)

1-17: LGTM: Clean imports with proper organization

The imports are well-organized with clear separation between external dependencies (telegraf, ethers, axios) and internal modules (services, utils, types, constants).


502-511: IPFS verification retry logic is solid

Good use of expretry with 3 retries and exponential backoff to handle transient IPFS gateway issues. The timeout of 60 seconds per attempt is reasonable for large files.


549-580: Health check server initialization is well-guarded

The health check server is only started if HEALTH_CHECK_PORT is configured and greater than 0. This is a good defensive pattern that avoids exposing the health endpoint unnecessarily.


567-574: Graceful shutdown handlers properly registered

The shutdown logic correctly uses process.once() to prevent duplicate handler execution and includes both SIGINT and SIGTERM for proper signal handling. The cleanup order (bot services → bot.stop) is correct.


34-60: Rate limit configuration precedence confirmed

Instantiation in index.ts omits the optional rateLimitConfig, so the constructor correctly falls back to environment variables, then to RATE_LIMITS constants. The intended precedence (parameter > env > constant) is implemented as designed.


139-161: Remove unnecessary conflict warning dynamic model commands run after explicit this.bot.command handlers, so /kasumi, /queue, /submit, and /process are handled first—and there are no built-in /help or /start commands.

Likely an incorrect or invalid review comment.

bots/kasumi-3/src/constants.ts (1)

1-85: LGTM: Well-organized centralized constants

The constants file provides excellent centralization of configuration values with:

  • Clear logical grouping (TIMEOUTS, RATE_LIMITS, BLOCKCHAIN, etc.)
  • Descriptive comments for time-based values
  • Proper use of as const for type safety
  • Reasonable default values (15-minute timeouts, 20% gas buffer, 1-hour cleanup intervals)

This centralization will improve maintainability and make it easier to adjust configuration across the application.

Based on learnings about the Telegraf and ethers libraries, these constants align well with recommended practices for timeout values and buffer percentages.

bots/kasumi-3/src/services/BlockchainService.ts (7)

5-5: LGTM: Constants properly imported and applied

The integration of BLOCKCHAIN constants from the centralized constants module is clean. The readonly fields ensure immutability of critical configuration values.

Also applies to: 18-22


77-109: Robust nonce management with multi-RPC fallback

The getNonceWithRetry implementation is well-designed:

  • Uses cached nonce for performance (5-second TTL)
  • Queries all RPC endpoints and uses the highest nonce to avoid out-of-sync issues
  • Falls back gracefully on individual RPC failures
  • Clear error handling if all RPCs fail

114-129: Gas estimation with buffer and fallback is sound

The estimateGasWithBuffer helper properly:

  • Attempts dynamic gas estimation first
  • Applies a configurable buffer percentage (default 20%)
  • Falls back to predefined limits on estimation failure
  • Logs both paths for debugging

This approach balances accuracy with reliability.


319-340: signalCommitment gas estimation follows the pattern

The gas estimation for signalCommitment (lines 322-326) correctly uses estimateGasWithBuffer and passes the computed gasLimit to the transaction. The error handling preserves the original behavior of logging and re-throwing.


347-385: submitSolution gas estimation and commitment delay are correct

Lines 363-368 apply gas estimation to submitSolution, and line 359 uses BLOCKCHAIN.COMMITMENT_DELAY_MS for the sleep between commitment signaling and solution submission. This delay helps avoid nonce conflicts.


402-450: findTransactionByTaskId uses proper block lookback

Line 406 correctly uses BLOCKCHAIN.BLOCK_LOOKBACK to constrain the search window for TaskSubmitted events, preventing excessive blockchain queries.


257-290: No action needed—inline comment clarifies task vs transaction gas limits The inline comment at line 287 already notes that the 200 000 parameter is for task execution, separate from the transaction gasLimit.

bots/kasumi-3/src/index.ts (3)

4-18: Clean import additions for new components

The new imports for Telegraf, initializePaymentSystem, JOB_QUEUE, TIMEOUTS, Kasumi3Bot, and path are properly organized and necessary for the refactored initialization flow.


106-114: Kasumi3Bot initialization is well-structured

The Kasumi3Bot constructor is now called with all necessary services properly initialized, including the pre-initialized bot instance and payment system's userService. This design allows for cleaner separation of concerns and easier testing.


68-82: DatabaseService handles directory creation The constructor checks and creates the parent data directory with fs.mkdirSync(dir, { recursive: true }) before opening the database.

Comment on lines +178 to +200
if (this.userService && ctx.from?.id) {
const user = this.userService.getUser(ctx.from.id);
if (!user) {
await ctx.reply(
'❌ Please link your wallet first using:\n/link <wallet_address>\n\n' +
'Then deposit AIUS tokens with /deposit'
);
return;
}

const balance = this.userService.getBalance(ctx.from.id);
// Estimate cost: model fee + gas (~0.5 AIUS)
const estimatedCost = ethers.parseEther('0.5');
if (balance < estimatedCost) {
await ctx.reply(
`❌ Insufficient balance\n\n` +
`Balance: ${ethers.formatEther(balance)} AIUS\n` +
`Estimated cost: ~${ethers.formatEther(estimatedCost)} AIUS\n\n` +
`Use /deposit to add funds`
);
return;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded gas estimation may be inaccurate

Line 190 uses a hardcoded gas estimate of 0.5 AIUS for user balance checks. This estimate may become inaccurate over time as gas prices fluctuate or model fees change. Consider using the actual gas estimation logic from GasAccountingService or making this configurable.

Consider using the gas estimation service:

-      // Estimate cost: model fee + gas (~0.5 AIUS)
-      const estimatedCost = ethers.parseEther('0.5');
+      // Get actual gas estimate from GasAccountingService
+      const modelFee = // get from modelConfig or blockchain
+      const gasEstimate = this.userService ? await this.gasAccounting?.estimateGasCost(...) : 0n;
+      const estimatedCost = modelFee + (gasEstimate || ethers.parseEther('0.5'));

Note: This requires access to gasAccounting service, which would need to be passed to the constructor.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +429 to +491
private async waitForJobCompletion(job: TaskJob, ctx: Context, responseCtx?: any): Promise<void> {
const maxWaitTime = parseInt(process.env.JOB_WAIT_TIMEOUT_MS || String(TIMEOUTS.JOB_WAIT_DEFAULT));
let lastProgress = '';

return new Promise((resolve) => {
const timeout = setTimeout(() => {
cleanup();
ctx.reply(`⏰ Task is taking longer than expected. Check back later with /queue`);
resolve();
}, maxWaitTime);

const onStatusChange = async (updatedJob: TaskJob) => {
if (updatedJob.id !== job.id) return;

// Update progress if changed
if (updatedJob.progress && updatedJob.progress !== lastProgress && responseCtx) {
lastProgress = updatedJob.progress;
try {
await this.bot.telegram.editMessageCaption(
responseCtx.chat.id,
responseCtx.message_id,
undefined,
`⏳ ${updatedJob.progress}`
);
} catch (e) {
log.debug(`Failed to update progress: ${e}`);
}
}

if (updatedJob.status === 'completed' && updatedJob.cid) {
cleanup();
await this.sendCompletedResult(ctx, responseCtx, updatedJob);
resolve();
} else if (updatedJob.status === 'failed') {
cleanup();
const errorMsg = updatedJob.error || 'Unknown error';
ctx.reply(`❌ Task failed: ${errorMsg}\n\n💰 Your balance has been refunded`);
resolve();
}
};

const cleanup = () => {
clearTimeout(timeout);
this.jobQueue.off('jobStatusChange', onStatusChange);
};

this.jobQueue.on('jobStatusChange', onStatusChange);

// Check if job is already completed (race condition)
const currentJob = this.jobQueue.getJob(job.id);
if (currentJob) {
if (currentJob.status === 'completed' && currentJob.cid) {
cleanup();
this.sendCompletedResult(ctx, responseCtx, currentJob).then(resolve);
} else if (currentJob.status === 'failed') {
cleanup();
const errorMsg = currentJob.error || 'Unknown error';
ctx.reply(`❌ Task failed: ${errorMsg}\n\n💰 Your balance has been refunded`);
resolve();
}
}
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Race condition in job completion check

Lines 477-489 attempt to handle a race condition by checking if the job is already completed after setting up the event listener. However, there's still a narrow window between lines 475 (setting up the listener) and 478 (checking current status) where a status change could be missed. The event could fire after line 475 but before the check at line 478, and then the check would see a non-terminal status, leading to waiting indefinitely (until timeout).

While this race is unlikely and protected by the timeout, consider restructuring to check status before attaching the listener:

+      // Check if job is already completed first (before attaching listener)
+      const currentJob = this.jobQueue.getJob(job.id);
+      if (currentJob) {
+        if (currentJob.status === 'completed' && currentJob.cid) {
+          this.sendCompletedResult(ctx, responseCtx, currentJob).then(resolve);
+          return;
+        } else if (currentJob.status === 'failed') {
+          const errorMsg = currentJob.error || 'Unknown error';
+          ctx.reply(`❌ Task failed: ${errorMsg}\n\n💰 Your balance has been refunded`);
+          resolve();
+          return;
+        }
+      }
+
       const cleanup = () => {
         clearTimeout(timeout);
         this.jobQueue.off('jobStatusChange', onStatusChange);
       };
 
       this.jobQueue.on('jobStatusChange', onStatusChange);
-
-      // Check if job is already completed (race condition)
-      const currentJob = this.jobQueue.getJob(job.id);
-      if (currentJob) {
-        if (currentJob.status === 'completed' && currentJob.cid) {
-          cleanup();
-          this.sendCompletedResult(ctx, responseCtx, currentJob).then(resolve);
-        } else if (currentJob.status === 'failed') {
-          cleanup();
-          const errorMsg = currentJob.error || 'Unknown error';
-          ctx.reply(`❌ Task failed: ${errorMsg}\n\n💰 Your balance has been refunded`);
-          resolve();
-        }
-      }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In bots/kasumi-3/src/bot/Kasumi3Bot.ts around lines 429 to 491, the race exists
between attaching the jobStatusChange listener and the subsequent currentJob
status check; to fix it, first fetch currentJob = this.jobQueue.getJob(job.id)
and if it is terminal (completed with cid or failed) run the corresponding
cleanup/response and resolve immediately; only if currentJob is non-terminal
attach the listener, then re-read currentJob once more immediately after
registering the listener and if it became terminal handle cleanup/response and
resolve — this guarantees no updates are missed between the checks and listener
registration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant