Skip to content

fix: ensure managed wallet deployment close doesnot depend on authService#1932

Merged
stalniy merged 2 commits intomainfrom
fix/close-deployment-trial
Sep 17, 2025
Merged

fix: ensure managed wallet deployment close doesnot depend on authService#1932
stalniy merged 2 commits intomainfrom
fix/close-deployment-trial

Conversation

@stalniy
Copy link
Contributor

@stalniy stalniy commented Sep 17, 2025

Why

Otherwise we can't use it in background jobs because it requires authenticated user. CloseDeploymentJob failed with:

{
  "name": "Error",
  "stack": "Error: No context available\n    at ExecutionContextService.get context (webpack://akashnetwork/console-api/src/core/services/execution-context/execution-context.service.ts:22:13)\n    at ExecutionContextService.get (webpack://akashnetwork/console-api/src/core/services/execution-context/execution-context.service.ts:33:17)\n    at AuthService.get ability (webpack://akashnetwork/console-api/src/auth/services/auth.service.ts:26:41)\n    at ManagedSignerService.executeDecodedTxByUserId (webpack://akashnetwork/console-api/src/billing/services/managed-signer/managed-signer.service.ts:74:86)\n    at DeploymentWriterService.close (webpack://akashnetwork/console-api/src/deployment/services/deployment-writer/deployment-writer.service.ts:71:30)\n    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n    at async CloseTrialDeploymentHandler.handle (webpack://akashnetwork/console-api/src/app/services/close-trial-deployment/close-trial-deployment.handler.ts:61:5)\n    at async <anonymous> (webpack://akashnetwork/console-api/src/core/services/job-queue/job-queue.service.ts:106:13)\n    at async resolveWithinSeconds (webpack://akashnetwork/node_modules/pg-boss/src/manager.js:27:1)\n    at async Worker.onFetch (webpack://akashnetwork/node_modules/pg-boss/src/manager.js:216:1)",
  "message": "No context available"
}

Summary by CodeRabbit

  • Bug Fixes

    • Corrected anonymous free trial validation to use the proper user context during transaction execution.
    • Aligned lease-created events with intended gating to prevent incorrect event emissions.
    • Reduced signing errors by using a wallet-aware execution path for decoded transactions.
  • Refactor

    • Switched deployment close flow to use wallet-based transaction signing for consistency.
    • Introduced a wallet-aware transaction execution path to standardize behavior without changing end-user functionality.

@stalniy stalniy requested a review from a team as a code owner September 17, 2025 03:04
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 17, 2025

Walkthrough

Adds a wallet-aware signer method executeDecodedTxByUserWallet, makes executeDecodedTxByUserId delegate to it, updates deployment close to call the wallet-based method, and applies wallet-owner-aware user resolution and gating for anonymous trial and lease-created events.

Changes

Cohort / File(s) Summary
Managed signer: wallet-based execution & trial gating
apps/api/src/billing/services/managed-signer/managed-signer.service.ts
Adds executeDecodedTxByUserWallet(userWallet, messages, walletOwner?) returning decoded tx results; executeDecodedTxByUserId delegates to it. Imports UserOutput, resolves user context when ANONYMOUS_FREE_TRIAL is enabled, applies user-aware trial/lease validations, and gates lease-created events on hasCreateTrialLeaseMessage.
Deployment writer: switch to wallet-based signer call
apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts
Replaces executeDecodedTxByUserId(wallet.userId, [message]) with executeDecodedTxByUserWallet(wallet, [message]) in close(). No other control-flow changes.
Tests: signer mock updated
apps/api/test/functional/deployments.spec.ts
Test for DELETE /v1/deployments/{dseq} updated to mock executeDecodedTxByUserWallet instead of executeDecodedTxByUserId; deposit test still uses the older API.

Sequence Diagram(s)

sequenceDiagram
  participant DeploymentWriterService as DeploymentWriterService
  participant ManagedSignerService as ManagedSignerService
  participant UserRepo as UserRepository
  participant Chain as Chain RPC

  DeploymentWriterService->>ManagedSignerService: executeDecodedTxByUserWallet(wallet, [message])
  alt ANONYMOUS_FREE_TRIAL enabled
    ManagedSignerService->>ManagedSignerService: determine user context (walletOwner or fetch by wallet.userId)
    ManagedSignerService->>UserRepo: fetch user (if needed)
    UserRepo-->>ManagedSignerService: user
    ManagedSignerService->>ManagedSignerService: run trial/provider/lease validations with user
  end
  ManagedSignerService->>Chain: broadcast tx (messages)
  Chain-->>ManagedSignerService: { code, hash, transactionHash, rawLog }
  opt hasCreateTrialLeaseMessage
    ManagedSignerService->>ManagedSignerService: emit lease-created domain event
  end
  ManagedSignerService-->>DeploymentWriterService: tx result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • ygrishajev

Poem

A rabbit signs with wallet in paw,
Swapping IDs for pouches without a flaw.
Trials checked, leases sing,
Events hop out at the right spring.
Hashes sparkle—hop, hop, hooray! 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly describes the primary change: preventing the managed-wallet deployment close flow from depending on authService so it can run in background jobs; this matches the code changes that add a wallet-based signer method and switch callers from userId-based to wallet-based invocation. It is concise, specific to the main issue, and readable.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/close-deployment-trial

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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

Copy link
Contributor

@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: 0

Caution

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

⚠️ Outside diff range comments (1)
apps/api/src/billing/services/managed-signer/managed-signer.service.ts (1)

143-145: Disallow any in catch per repo TS guidelines

Use unknown and pass it through. This complies with “never use any” and keeps error typing safe.

Apply this diff:

-    } catch (error: any) {
+    } catch (error: unknown) {
       throw await this.chainErrorService.toAppError(error, messages);
     }
🧹 Nitpick comments (3)
apps/api/src/billing/services/managed-signer/managed-signer.service.ts (3)

100-103: Status code consistency for missing wallet owner

Consider returning 404 instead of 500 when the user tied to the wallet is not found, to align with other “not found” asserts in this service.

Apply this diff:

-      assert(user, 500, "User for wallet not found");
+      assert(user, 404, "User for wallet not found");

133-142: Make tx result assembly type-safe and resilient (hash vs transactionHash)

Current casting can produce an object typed to have transactionHash when it may be absent. Prefer explicit normalization and a hard assert if neither hash is present.

Apply this diff:

-      const result = pick(tx, ["code", "hash", "transactionHash", "rawLog"]) as Pick<IndexedTx, "code" | "hash" | "rawLog">;
-
-      if (result.hash) {
-        return {
-          ...result,
-          transactionHash: result.hash
-        };
-      }
-
-      return result as Pick<IndexedTx, "code" | "hash" | "rawLog"> & { transactionHash: string };
+      const picked = pick(tx, ["code", "hash", "transactionHash", "rawLog"]) as {
+        code: number;
+        hash?: string;
+        transactionHash?: string;
+        rawLog: string;
+      };
+      const normalizedHash = picked.hash ?? picked.transactionHash;
+      assert(normalizedHash, 500, "Tx hash missing");
+      return {
+        code: picked.code,
+        hash: normalizedHash!,
+        rawLog: picked.rawLog,
+        transactionHash: picked.transactionHash ?? normalizedHash!
+      };

92-99: Optional: early guard to ensure messages are owned by the wallet

Right now we rely on chain-level signature checks. A fast-fail assert that message owner fields (where applicable) match userWallet.address would improve defense-in-depth and error clarity.

Would you like a small helper that inspects known Akash message types (*.MsgCloseDeployment, *.MsgUpdateDeployment, *.MsgDepositDeployment, *.MsgCreateDeployment, *.MsgCreateLease) and asserts owner matches wallet?

Also applies to: 113-121

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 016ef7d and 82199be.

📒 Files selected for processing (2)
  • apps/api/src/billing/services/managed-signer/managed-signer.service.ts (4 hunks)
  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts
  • apps/api/src/billing/services/managed-signer/managed-signer.service.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts
  • apps/api/src/billing/services/managed-signer/managed-signer.service.ts
🧬 Code graph analysis (1)
apps/api/src/billing/services/managed-signer/managed-signer.service.ts (2)
apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts (1)
  • UserWalletOutput (16-20)
apps/api/src/user/repositories/user/user.repository.ts (1)
  • UserOutput (11-13)
⏰ 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). (2)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
🔇 Additional comments (4)
apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts (1)

71-71: Decouples close path from auth context — LGTM

Switching to executeDecodedTxByUserWallet removes the authService dependency, unblocking CloseDeploymentJob usage in workers.

Please confirm that all job/worker call sites now route through this wallet-based path.

apps/api/src/billing/services/managed-signer/managed-signer.service.ts (3)

79-80: Good delegation to wallet-based signer

executeDecodedTxByUserId now delegates to the wallet variant with an explicit walletOwner. This preserves existing API while enabling worker-safe execution.


82-91: New wallet-based signer API fits the background-job use case

Public executeDecodedTxByUserWallet removes reliance on authService. Solid direction for worker contexts.


82-91: No unsafe call sites found — wallets are retrieved from trusted repos/internal services.

executeDecodedTxByUserWallet is only invoked from internal services/controllers that pass UserWallet objects obtained via UserWalletRepository (accessibleBy/findById) — e.g. deployment-writer.service.ts (close), lease.service.ts (createLeasesAndSendManifest), provider-cleanup.service.ts, stale-managed-deployments-cleaner.service.ts — tests use seeded wallets. No direct use of request-provided wallet data detected.

@codecov
Copy link

codecov bot commented Sep 17, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 44.31%. Comparing base (959ed00) to head (b10f1fc).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1932      +/-   ##
==========================================
- Coverage   44.68%   44.31%   -0.38%     
==========================================
  Files         979      969      -10     
  Lines       27586    27241     -345     
  Branches     7134     7094      -40     
==========================================
- Hits        12327    12071     -256     
+ Misses      14160    13991     -169     
- Partials     1099     1179      +80     
Flag Coverage Δ *Carryforward flag
api 81.17% <100.00%> (+0.04%) ⬆️
deploy-web 22.67% <ø> (ø) Carriedforward from 959ed00
log-collector ?
notifications 87.94% <ø> (ø) Carriedforward from 959ed00
provider-console 81.48% <ø> (ø) Carriedforward from 959ed00
provider-proxy 84.47% <ø> (ø) Carriedforward from 959ed00

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
.../services/managed-signer/managed-signer.service.ts 98.71% <100.00%> (+0.06%) ⬆️
...ces/deployment-writer/deployment-writer.service.ts 98.43% <100.00%> (ø)

... and 41 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

ygrishajev
ygrishajev previously approved these changes Sep 17, 2025
@stalniy stalniy force-pushed the fix/close-deployment-trial branch from e8d991b to b10f1fc Compare September 17, 2025 10:16
Copy link
Contributor

@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: 0

Caution

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

⚠️ Outside diff range comments (1)
apps/api/test/functional/deployments.spec.ts (1)

587-588: Update tests: spy on executeDecodedTxByUserWallet for close flow

Close() now calls signerService.executeDecodedTxByUserWallet (apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts:71). Change the jest.spyOn in apps/api/test/functional/deployments.spec.ts (lines 587–588 and 683–684) from executeDecodedTxByUserId to executeDecodedTxByUserWallet. Keep executeDecodedTxByUserId mocks for flows that still use it (deployment-writer.service.ts:60/89/124; apps/api/src/deployment/services/lease/lease.service.ts:30; apps/api/src/certificate/services/certificate.service.ts:34).

🧹 Nitpick comments (5)
apps/api/test/functional/deployments.spec.ts (5)

520-521: Switch to wallet-based signer for close: good; assert the call to prevent regressions

This aligns the DELETE flow with the wallet-based API. Add an assertion that the wallet path is invoked, so future refactors don’t silently drift back to userId.

Apply this diff:

-      jest.spyOn(signerService, "executeDecodedTxByUserWallet").mockResolvedValueOnce(mockTxResult);
+      const execWalletSpy = jest.spyOn(signerService, "executeDecodedTxByUserWallet").mockResolvedValueOnce(mockTxResult);
@@
       expect(response.status).toBe(200);
       const result = await response.json();
       expect(result).toEqual({
         data: {
           success: true
         }
       });
+      expect(execWalletSpy).toHaveBeenCalledTimes(1);
+      expect(execWalletSpy).toHaveBeenCalledWith(
+        expect.objectContaining({ address: wallets[0].address }),
+        expect.any(Array),
+        expect.anything()
+      );

507-535: Add a failure-path test for signer non-zero code

We should cover the case where executeDecodedTxByUserWallet returns a non‑zero code (or throws), and assert the endpoint responds with the expected error status/body.

I can draft the additional test if you confirm the expected HTTP status for a failed sign (e.g., 4xx vs 5xx) in the close flow.


172-174: Remove duplicate nock stub for active leases list

The same persistent handler is registered twice; drop the duplicate to avoid confusion.

-    nock(apiNodeUrl).persist().get(`/akash/market/${betaTypeVersionMarket}/leases/list?filters.owner=${address}&filters.state=active`).reply(200, { leases });
-    nock(apiNodeUrl).persist().get(`/akash/market/${betaTypeVersionMarket}/leases/list?filters.owner=${address}&filters.state=active`).reply(200, { leases });
+    nock(apiNodeUrl)
+      .persist()
+      .get(`/akash/market/${betaTypeVersionMarket}/leases/list?filters.owner=${address}&filters.state=active`)
+      .reply(200, { leases });

836-844: Assert status code in this GET-by-address test

Tiny hardening: verify 200 before parsing.

       const response = await app.request(`/v1/addresses/${wallets[0].address}/deployments/0/1`, {
         method: "GET",
         headers: new Headers({ "Content-Type": "application/json", "x-api-key": userApiKeySecret })
       });
 
+      expect(response.status).toBe(200);
       const result = await response.json();

45-89: Optional: prefer per-test setup() over beforeEach for spec files

Guideline suggests a setup function at the bottom of the root describe. This suite is functional and fine, but consider migrating incrementally to reduce shared state.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82199be and b10f1fc.

📒 Files selected for processing (3)
  • apps/api/src/billing/services/managed-signer/managed-signer.service.ts (4 hunks)
  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts (1 hunks)
  • apps/api/test/functional/deployments.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/src/deployment/services/deployment-writer/deployment-writer.service.ts
  • apps/api/src/billing/services/managed-signer/managed-signer.service.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/no-jest-mock.mdc)

Don't use jest.mock() to mock dependencies in test files. Instead, use jest-mock-extended to create mocks and pass mocks as dependencies to the service under test.

**/*.spec.{ts,tsx}: Use setup function instead of beforeEach in test files
setup function must be at the bottom of the root describe block in test files
setup function creates an object under test and returns it
setup function should accept a single parameter with inline type definition
Don't use shared state in setup function
Don't specify return type of setup function

Files:

  • apps/api/test/functional/deployments.spec.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/api/test/functional/deployments.spec.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/api/test/functional/deployments.spec.ts
⏰ 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). (2)
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build

@stalniy stalniy merged commit 95b3430 into main Sep 17, 2025
62 checks passed
@stalniy stalniy deleted the fix/close-deployment-trial branch September 17, 2025 11:46
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.

2 participants

Comments