Skip to content

feat: token registry deploy#130

Merged
rongquan1 merged 7 commits intomainfrom
feat/token-registry-deploy
Feb 25, 2026
Merged

feat: token registry deploy#130
rongquan1 merged 7 commits intomainfrom
feat/token-registry-deploy

Conversation

@RishabhS7
Copy link
Copy Markdown
Contributor

@RishabhS7 RishabhS7 commented Feb 20, 2026

Deployment Feature Implementation

🚀 New Features

1. Token Registry Deployment (src/deploy/token-registry.ts)

A complete deployment module for TradeTrust Token Registry contracts with two deployment modes:

Quick-start Mode (Default)

  • Uses pre-deployed deployer contracts and implementation contracts
  • Faster deployment with lower gas costs
  • Leverages minimal proxy pattern for efficient deployment
  • Requires network to have deployer and implementation contracts

Standalone Mode

  • Deploys a fresh Token Registry contract from scratch
  • Works on any network with a supported Title Escrow Factory
  • Higher gas costs but more flexible
  • Useful for custom networks or when quick-start is unavailable

Key Features:

  • ✅ Automatic chain ID detection
  • ✅ Automatic mode selection based on network support
  • ✅ Custom contract address support
  • ✅ EIP-1559 gas configuration (maxFeePerGas, maxPriorityFeePerGas)
  • ✅ Full ethers v5 and v6 compatibility
  • ✅ Comprehensive error handling
  • ✅ Type-safe interfaces

Function Signature:

deployTokenRegistry(
  registryName: string,
  registrySymbol: string,
  signer: SignerV5 | SignerV6,
  options: DeployOptions
): Promise<TransactionReceipt>

2. Document Store Deployment (src/deploy/document-store.ts)

A deployment module for TrustVC Document Store contracts with two store types:

Standard Document Store (Default)

  • For issuing and revoking verifiable documents
  • Immutable ownership (documents cannot be transferred)
  • Suitable for most credential use cases

Transferable Document Store

  • Supports ownership transfers between addresses
  • Useful for transferable credentials or certificates
  • Maintains full document integrity during transfers

Key Features:

  • ✅ Automatic store type selection
  • ✅ Full ethers v5 and v6 compatibility
  • ✅ EIP-1559 gas configuration
  • ✅ Comprehensive error handling with detailed messages
  • ✅ Type-safe interfaces

🧪 Testing

Comprehensive Test Suite (src/__tests__/token-registry-functions/deploy.test.ts)

Token Registry Tests

  • ✅ Quick-start mode deployment
  • ✅ Standalone mode deployment
  • ✅ Custom contract addresses
  • ✅ Gas configuration
  • ✅ Error handling (invalid addresses, unsupported networks)
  • ✅ Ethers v5 and v6 compatibility

Document Store Tests

  • ✅ Standard document store deployment
  • ✅ Transferable document store deployment
  • ✅ Owner configuration
  • ✅ Gas configuration
  • ✅ Error handling
  • ✅ Ethers v5 and v6 compatibility

🔧 Utility Enhancements

Enhanced src/token-registry-functions/utils.ts

Added critical utility functions:

  1. getChainIdSafe() - Safe chain ID extraction from signers
  2. isValidAddress() - Ethereum address validation
  3. isSupportedTitleEscrowFactory() - Factory interface verification
  4. getDefaultContractAddress() - Network-specific contract addresses

Enhanced Type Definitions (src/token-registry-functions/types.ts)

Added comprehensive deployment-related types:

  • DeployOptions interface
  • TransactionReceipt union type
  • Gas value types (GasValue)
  • Contract address configuration types

Summary by CodeRabbit

  • New Features

    • Token Registry deployment tool (quick-start & standalone) with ethers v5/v6 support, auto chainId resolution, gas and address options.
    • Document Store deployment API supporting standard and transferable stores across ethers versions.
    • Utilities for default contract addresses, address validation, and factory support checks.
  • Refactor

    • Unified signer/provider handling and expanded public types for deployment, gas, and signer/network options.
    • Reorganized deployment re-exports.
  • Tests

    • Expanded deployment tests and updated transfer tests to include gas option parameters.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Moves DocumentStore deploy implementation from src/document-store/deploy.ts to src/deploy/document-store.ts; adds src/deploy/token-registry.ts (quick-start/standalone, ethers v5/v6); expands token-registry utils/types/fixtures/tests; updates ethers helper APIs and adjusts related tests and imports.

Changes

Cohort / File(s) Summary
DocumentStore (relocate + new impl)
src/document-store/deploy.ts, src/document-store/index.ts, src/deploy/document-store.ts
Removed legacy src/document-store/deploy.ts; added centralized src/deploy/document-store.ts (v5/v6-aware deployDocumentStore); updated index.ts to re-export from new path.
Token Registry deployment
src/deploy/token-registry.ts, src/token-registry-functions/index.ts
Added deployTokenRegistry supporting quick-start and standalone flows, auto chainId resolution, address validation, gas options, and v5/v6-specific deployment paths; re-exported from token-registry-functions.
Utils & types for token-registry
src/token-registry-functions/utils.ts, src/token-registry-functions/types.ts
Changed getTxOptions signature to include chainId; added getDefaultContractAddress, isValidAddress, isSupportedTitleEscrowFactory; introduced deploy-related types and gas options.
Ethers helper APIs
src/utils/ethers/index.ts
Added provider/contract factory helpers and explicit V5/V6 contract & factory type aliases; getEthersContractFromProvider and new getEthersContractFactoryFromProvider select implementation by provider version.
Token-registry runtime changes & v5 re-export
src/token-registry-functions/ownerOf.ts, src/token-registry-v5/index.ts
ownerOf now uses getEthersContractFromProvider to instantiate generic contract (v5/v4) and accepts `SignerV5
Tests & fixtures (added/updated)
src/__tests__/document-store/deploy.test.ts, src/__tests__/token-registry-functions/*, src/__tests__/token-registry-functions/fixtures.ts
Fixed document-store test import path; added extensive token-registry deploy tests (v5/v6, quick-start/standalone, defaults and validation); expanded mocks for v4/v5/v6 and ethers; updated transfers/ownerOf test expectations.
Transfers & ownerOf tests adjustments
src/__tests__/token-registry-functions/transfers.test.ts, src/__tests__/token-registry-functions/ownerOf.test.ts
Updated expected call args to include gas-station options and optional remark args; replaced some factory-connect assertions with interface-support checks; removed certain auto-detection tests.
DocumentStore type import tweaks
src/document-store/grant-role.ts, src/document-store/transferOwnership.ts
Changed ethers v6 import alias so ContractTransactionV6 refers to ContractTransactionResponse (type import only).

Sequence Diagram

sequenceDiagram
    participant User
    participant SDK
    participant Validator
    participant Detector
    participant Utils
    participant Deployer
    participant Factory
    participant Signer
    participant Chain

    rect rgba(200,200,255,0.5)
    User->>SDK: deployTokenRegistry(name,symbol,signer,opts)
    end

    SDK->>Validator: validate inputs (name,symbol,addresses)
    SDK->>Detector: detect signer version & chainId
    Detector-->>SDK: signerV5|signerV6, chainId
    SDK->>Utils: resolve txOptions & default addresses
    alt quick-start
        SDK->>Deployer: connect & encode init params
        Deployer->>Signer: send deploy tx (deploy(params, txOptions))
        Signer->>Chain: broadcast tx
        Chain-->>Signer: return receipt
    else standalone
        SDK->>Factory: create ContractFactory (v5/v6)
        Factory->>Signer: deploy implementation (constructor args, txOptions)
        Signer->>Chain: broadcast tx
        Chain-->>Signer: return txHash/receipt
        SDK->>Signer: waitFor deployment receipt (v5/v6 specific)
    end
    SDK-->>User: return contract address & receipt
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

released on @v1``

Suggested reviewers

  • rongquan1
  • nghaninn

Poem

🐰 I hopped through code and found new deploys bright,
Stores and registries now dance in the light.
V5 and V6 snug in a compatibility tune,
Tests and mocks warmed like sun in June.
Hooray — I nibble a carrot for CI tonight!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'feat: token registry deploy' directly and clearly summarizes the main change: adding token registry deployment functionality. It is concise, specific, and accurately reflects the primary feature introduced.
Description check ✅ Passed The pull request description provides excellent coverage with a detailed Summary section explaining the background, comprehensive Changes section detailing all modifications (Token Registry, Document Store, Testing, Utilities), and links to related Issues. However, it deviates from the template structure by using creative formatting with emojis and subsections instead of following the exact template format.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/token-registry-deploy

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.

Copy link
Copy Markdown
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: 9

🧹 Nitpick comments (4)
src/token-registry-functions/types.ts (1)

116-117: Partial<PrivateKeyOption> neutralizes the mutual exclusivity constraint.

PrivateKeyOption uses a discriminated union with never to enforce that only one of key or keyFile is provided. Wrapping it in Partial<> makes both fields optional (including the never-typed ones becoming undefined), which allows both key and keyFile to be set simultaneously.

If the intent is to preserve mutual exclusivity, use PrivateKeyOption directly instead of Partial<PrivateKeyOption>.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/token-registry-functions/types.ts` around lines 116 - 117, The type
NetworkAndWalletSignerOption currently uses Partial<PrivateKeyOption>, which
defeats the discriminated-union mutual-exclusivity enforced by PrivateKeyOption;
replace Partial<PrivateKeyOption> with PrivateKeyOption (keeping NetworkOption &
(Partial<WalletOption> | PrivateKeyOption)) so the discriminant and never fields
in PrivateKeyOption remain effective and prevent both key and keyFile being set
simultaneously; update any related imports or references if needed to reflect
the stronger type.
src/deploy/token-registry.ts (1)

188-241: Standalone mode lacks error handling around deployment — inconsistent with deployDocumentStore.

The deployDocumentStore function wraps deployment in a try/catch with a descriptive error message (lines 128–153 in document-store.ts). The standalone path here has no similar protection. A failed deployment will propagate a raw ethers error.

Consider wrapping for consistency, or at minimum document the different error-handling strategy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/deploy/token-registry.ts` around lines 188 - 241, Wrap the standalone
deployment block in a try/catch similar to deployDocumentStore: surround the
creation and deploy calls for tokenFactory/ContractFactoryV5/ContractFactoryV6
(references: getEthersContractFactoryFromProvider, tokenFactory,
TradeTrustToken__factory, isV6EthersProvider, getTxOptions, deploy,
deploymentTransaction, deployTransaction) with a try, catch the thrown error,
and rethrow a new Error that includes a clear contextual message like "Failed to
deploy Token Registry" plus the original error message/details to preserve
diagnostics; ensure both v5 and v6 branches are inside the try so any failure is
caught and logged-consistently.
src/token-registry-functions/utils.ts (1)

8-26: getTxOptions parameter types don't reflect actual usage — chainId and gas values can be undefined.

Callers (e.g., document-store.ts line 108, token-registry.ts line 220) pass potentially-undefined chainId and gas values. The function handles this at runtime (lines 15-16 fallback for chainId, line 25 check for gas values), but the signature declares them as required. This creates a type-safety gap that will silently pass undefined through unless strict null checks are enforced.

Consider making these parameters explicitly optional to match actual usage.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/token-registry-functions/utils.ts` around lines 8 - 26, The function
getTxOptions currently accepts required parameters but treats chainId,
maxFeePerGas and maxPriorityFeePerGas as possibly undefined; change its
signature to make chainId?: CHAIN_ID, maxFeePerGas?: GasValue and
maxPriorityFeePerGas?: GasValue (keeping signer: SignerV6 | Signer required),
update any internal casts/usages (e.g., the getChainIdSafe fallback and
SUPPORTED_CHAINS lookup) to rely on the now-optional types, and adjust any
callers or their passed arguments if they assume non-null inputs so the type
system reflects actual runtime behavior.
src/__tests__/token-registry-functions/deploy.test.ts (1)

101-103: Remove commented-out code.

These leftover commented-out import statements (also at lines 175-177) appear to be development artifacts. They add noise without value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/token-registry-functions/deploy.test.ts` around lines 101 -
103, Remove the leftover commented-out import statements referencing
getEthersContractFactoryFromProvider and isV6EthersProvider (the lines importing
from '../../utils/ethers/index.js') in the test file; simply delete those
commented lines at both occurrences (near the shown diff and the other instance
around lines 175-177) to eliminate noise and keep the test file clean.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/__tests__/token-registry-functions/deploy.test.ts`:
- Around line 371-408: The test fails because getChainIdSafe is not mocked and
the test still passes chainId so auto-detection in deployTokenRegistry is never
exercised; update the test fixtures to export a mocked getChainIdSafe (e.g., add
getChainIdSafe: vi.fn() to the vi.mock('../../token-registry-functions/utils',
...) setup in fixtures.ts) and change the test in deploy.test.ts to call
deployTokenRegistry without passing the chainId option (remove { chainId:
mockChainId } so deployTokenRegistry triggers the !chainId branch and the mocked
getChainIdSafe can be used).

In `@src/__tests__/token-registry-functions/fixtures.ts`:
- Around line 20-34: The failing CI is due to the mock for the utils module
missing getChainIdSafe, so tests call the real function and then try to use
.mockResolvedValue; add a mocked getChainIdSafe to the vi.mock return object
(alongside getDefaultContractAddress, isSupportedTitleEscrowFactory,
isValidAddress) as vi.fn().mockResolvedValue(<number>)—use the expected test
chain id (e.g., 31337 or 1) so deploy.test.ts can call
getChainIdSafe.mockResolvedValue without error.

In `@src/deploy/document-store.ts`:
- Line 137: Call to contract.deploymentTransaction().wait() lacks a null guard;
change the call to mirror the token-registry pattern by using optional chaining
or an explicit null check before awaiting the transaction (e.g., use
contract.deploymentTransaction()?.wait() or check the result of
contract.deploymentTransaction() and handle null), updating the deployment logic
that uses contract.deploymentTransaction() so it safely handles the null |
ContractTransactionResponse signature.

In `@src/deploy/token-registry.ts`:
- Around line 228-230: The call to contract.deploymentTransaction()?.wait() can
yield undefined if deploymentTransaction() returns null/undefined, violating the
declared Promise<TransactionReceipt> return type; update the function (around
contract.deploymentTransaction and its usage of wait) to explicitly check the
result of contract.deploymentTransaction(), throw or return a rejected Promise
with a clear error if it's null/undefined, and only call .wait() on a non-null
transaction so the function always resolves/rejects with a TransactionReceipt or
an error.
- Around line 154-169: In deployTokenRegistry add an explicit guard that
validates signer.provider before it's used: check if signer.provider is falsy
(similar to deployDocumentStore's check) and throw a clear error (e.g., "Signer
has no provider, cannot deploy TokenRegistry") before calling
getEthersContractFromProvider; this ensures
getEthersContractFromProvider(signer.provider) and subsequent usage in
getEthersContractFromProvider, deployerContract creation and
TDocDeployer__factory interactions are safe.
- Around line 186-187: The quick-start branch is returning the raw
ContractTransaction/ContractTransactionResponse from
deployerContract.deploy(...) instead of a TransactionReceipt; change it to
capture the deploy call result and await its receipt: call
deployerContract.deploy(tokenRegistryImplAddress, initParam, txOptions) into a
variable, then if that result has a wait method (handle both ethers v5/v6
shapes) call await result.wait() and return that receipt; if the result is
already a receipt, return it as-is. Ensure you reference
deployerContract.deploy, tokenRegistryImplAddress, initParam, and txOptions when
implementing this change.

In `@src/token-registry-functions/ownerOf.ts`:
- Around line 47-68: The code instantiates contracts with new Contract(...) and
declares tradeTrustTokenContract with if (isV5TT) / else if (isV4TT) leaving a
possible uninitialized variable; change the second branch to else to ensure
tradeTrustTokenContract is always assigned, and stop using new Contract(...) —
instead use the factories' .connect(...) (e.g.
v5Contracts.TradeTrustToken__factory.connect(tokenRegistryAddress, signer) and
v4Contracts.TradeTrustToken__factory.connect(...)) or update the
getEthersContractFromProvider test fixture to return a real Contract
constructor; ensure the resulting tradeTrustTokenContract has an ownerOf method
before calling ownerOf(tokenId).

In `@src/token-registry-functions/types.ts`:
- Around line 119-128: Remove the unused type declaration
DeployTokenRegistryCommand from the types file: locate the exported type named
DeployTokenRegistryCommand and delete its entire definition; then run a
project-wide search for DeployTokenRegistryCommand to confirm no references
remain and update any barrel exports or export lists if it was re-exported
elsewhere (remove it from exports if present) so the codebase and build stay
clean.

In `@src/token-registry-functions/utils.ts`:
- Around line 64-85: The function isSupportedTitleEscrowFactory currently
accepts an optional provider but immediately passes it to
getEthersContractFromProvider (via provider) which will throw if undefined;
either make provider required in the function signature or add an explicit early
guard at the start of isSupportedTitleEscrowFactory that checks provider !==
undefined and throws a clear error (e.g. "provider is required for
isSupportedTitleEscrowFactory") before calling getEthersContractFromProvider, so
titleEscrowFactoryContract and subsequent calls (implementation,
supportsInterface) never receive an undefined provider.

---

Nitpick comments:
In `@src/__tests__/token-registry-functions/deploy.test.ts`:
- Around line 101-103: Remove the leftover commented-out import statements
referencing getEthersContractFactoryFromProvider and isV6EthersProvider (the
lines importing from '../../utils/ethers/index.js') in the test file; simply
delete those commented lines at both occurrences (near the shown diff and the
other instance around lines 175-177) to eliminate noise and keep the test file
clean.

In `@src/deploy/token-registry.ts`:
- Around line 188-241: Wrap the standalone deployment block in a try/catch
similar to deployDocumentStore: surround the creation and deploy calls for
tokenFactory/ContractFactoryV5/ContractFactoryV6 (references:
getEthersContractFactoryFromProvider, tokenFactory, TradeTrustToken__factory,
isV6EthersProvider, getTxOptions, deploy, deploymentTransaction,
deployTransaction) with a try, catch the thrown error, and rethrow a new Error
that includes a clear contextual message like "Failed to deploy Token Registry"
plus the original error message/details to preserve diagnostics; ensure both v5
and v6 branches are inside the try so any failure is caught and
logged-consistently.

In `@src/token-registry-functions/types.ts`:
- Around line 116-117: The type NetworkAndWalletSignerOption currently uses
Partial<PrivateKeyOption>, which defeats the discriminated-union
mutual-exclusivity enforced by PrivateKeyOption; replace
Partial<PrivateKeyOption> with PrivateKeyOption (keeping NetworkOption &
(Partial<WalletOption> | PrivateKeyOption)) so the discriminant and never fields
in PrivateKeyOption remain effective and prevent both key and keyFile being set
simultaneously; update any related imports or references if needed to reflect
the stronger type.

In `@src/token-registry-functions/utils.ts`:
- Around line 8-26: The function getTxOptions currently accepts required
parameters but treats chainId, maxFeePerGas and maxPriorityFeePerGas as possibly
undefined; change its signature to make chainId?: CHAIN_ID, maxFeePerGas?:
GasValue and maxPriorityFeePerGas?: GasValue (keeping signer: SignerV6 | Signer
required), update any internal casts/usages (e.g., the getChainIdSafe fallback
and SUPPORTED_CHAINS lookup) to rely on the now-optional types, and adjust any
callers or their passed arguments if they assume non-null inputs so the type
system reflects actual runtime behavior.

Comment thread src/__tests__/token-registry-functions/deploy.test.ts
Comment thread src/__tests__/token-registry-functions/fixtures.ts
Comment thread src/deploy/document-store.ts
Comment thread src/deploy/token-registry.ts
Comment thread src/deploy/token-registry.ts
Comment thread src/deploy/token-registry.ts Outdated
Comment thread src/token-registry-functions/ownerOf.ts
Comment thread src/token-registry-functions/types.ts Outdated
Comment thread src/token-registry-functions/utils.ts
Copy link
Copy Markdown
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: 2

🧹 Nitpick comments (2)
src/__tests__/token-registry-functions/ownerOf.test.ts (1)

47-47: Remove dead commented-out code.

Line 47 and lines 70–72 are leftover code that serves no documentation purpose. If the disabled afterEach was intentionally suppressed to avoid destroying module-level spies, add an inline comment explaining why rather than leaving the commented-out block.

Also applies to: 70-72

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/token-registry-functions/ownerOf.test.ts` at line 47, Remove
the dead commented-out code: delete the commented line initializing mockContract
and the commented-out afterEach block in ownerOf.test.ts (the disabled "let
mockContract = isV5TT ? mockV5TradeTrustTokenContract :
mockV4TradeTrustTokenContract;" and the commented afterEach that was left to
preserve module-level spies). If the afterEach was intentionally suppressed,
replace the commented block with a concise inline comment explaining why
module-level spies must not be destroyed (reference the afterEach and
mockContract symbols so reviewers understand the rationale).
src/__tests__/token-registry-functions/fixtures.ts (1)

24-35: Prefer a static default for getChainIdSafe instead of delegating to the real function.

Using vi.fn().mockImplementation(original.getChainIdSafe) makes the mock's default behavior call the real function, which internally queries the provider's network. This works only because the providerV5.getNetwork spy is active (lines 370–373), but creates a hidden coupling: any test using a signer not connected to providerV5, or any scenario where that spy is cleared/restored, will cause getChainIdSafe to attempt a real network call.

A static default is simpler and unconditionally safe:

♻️ Proposed refactor
-    getChainIdSafe: vi.fn().mockImplementation(original.getChainIdSafe),
+    getChainIdSafe: vi.fn().mockResolvedValue(1),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/token-registry-functions/fixtures.ts` around lines 24 - 35,
Replace the mock that delegates to the real function so getChainIdSafe no longer
calls into the provider; specifically, replace
vi.fn().mockImplementation(original.getChainIdSafe) with a static mock that
returns a fixed chain id (e.g., vi.fn().mockResolvedValue(1) or
vi.fn().mockReturnValue(1) depending on whether getChainIdSafe is async) so
tests are not coupled to providerV5.getNetwork or a real network call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/__tests__/token-registry-functions/fixtures.ts`:
- Around line 368-373: The module-level spy on providerV5.getNetwork is fragile
and persists across tests; move the vi.spyOn(providerV5,
'getNetwork').mockResolvedValue(...) out of module scope and install it inside a
test lifecycle hook (e.g., beforeEach or beforeAll) in the consuming test files
so the mock implementation is re-applied for each test and survives mock
restoration/clear calls; reference the providerV5 constant and its getNetwork
method when locating where to remove the module-level spy and add the
lifecycle-scoped spy.
- Around line 40-45: MockContractConstructor currently routes by abi ===
'TradeTrustToken' which is identical for both factories
(TradeTrustToken__factory.abi), causing all addresses to resolve to
mockV5TradeTrustTokenContract; change the routing to use the address only (e.g.,
check address === MOCK_V5_ADDRESS to pick mockV5TradeTrustTokenContract and
otherwise return mockV4TradeTrustTokenContract) or alternatively give the v4/v5
factory abis unique strings and route by those; update MockContractConstructor
accordingly so MOCK_V4_ADDRESS maps to mockV4TradeTrustTokenContract and
MOCK_V5_ADDRESS maps to mockV5TradeTrustTokenContract.

---

Nitpick comments:
In `@src/__tests__/token-registry-functions/fixtures.ts`:
- Around line 24-35: Replace the mock that delegates to the real function so
getChainIdSafe no longer calls into the provider; specifically, replace
vi.fn().mockImplementation(original.getChainIdSafe) with a static mock that
returns a fixed chain id (e.g., vi.fn().mockResolvedValue(1) or
vi.fn().mockReturnValue(1) depending on whether getChainIdSafe is async) so
tests are not coupled to providerV5.getNetwork or a real network call.

In `@src/__tests__/token-registry-functions/ownerOf.test.ts`:
- Line 47: Remove the dead commented-out code: delete the commented line
initializing mockContract and the commented-out afterEach block in
ownerOf.test.ts (the disabled "let mockContract = isV5TT ?
mockV5TradeTrustTokenContract : mockV4TradeTrustTokenContract;" and the
commented afterEach that was left to preserve module-level spies). If the
afterEach was intentionally suppressed, replace the commented block with a
concise inline comment explaining why module-level spies must not be destroyed
(reference the afterEach and mockContract symbols so reviewers understand the
rationale).

Comment thread src/__tests__/token-registry-functions/fixtures.ts
Comment thread src/__tests__/token-registry-functions/fixtures.ts
Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
src/token-registry-functions/types.ts (2)

116-117: Partial<PrivateKeyOption> is redundant and misleading.

Both branches of PrivateKeyOption already have only optional fields, so Partial<> applied to the union is a no-op. Only Partial<WalletOption> serves a real purpose (making encryptedWalletPath optional).

♻️ Proposed cleanup
 export type NetworkAndWalletSignerOption = NetworkOption &
-  (Partial<WalletOption> | Partial<PrivateKeyOption>);
+  (Partial<WalletOption> | PrivateKeyOption);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/token-registry-functions/types.ts` around lines 116 - 117,
NetworkAndWalletSignerOption currently uses Partial<PrivateKeyOption> which is
redundant because PrivateKeyOption's fields are already optional; remove
Partial<> and simplify the union so NetworkAndWalletSignerOption = NetworkOption
& (Partial<WalletOption> | PrivateKeyOption) (keeping Partial<WalletOption> to
allow encryptedWalletPath to be optional) and update any references to
NetworkAndWalletSignerOption to match the simplified type; ensure imports/types
for NetworkOption, WalletOption, and PrivateKeyOption remain unchanged.

5-9: GasPriceScale is asymmetric and GasOption.dryRun should be optional.

Two related concerns:

  1. GasPriceScale exposes maxPriorityFeePerGasScale but has no corresponding maxFeePerGasScale. Since EIP-1559 involves both maxFeePerGas and maxPriorityFeePerGas, please confirm this asymmetry is intentional (e.g. only the tip is user-scaled, base fee is network-driven).

  2. dryRun: boolean on GasOption is required, which forces every caller to explicitly pass dryRun: false. As an opt-in flag it should be optional.

♻️ Proposed fix for `dryRun` optionality
 export interface GasOption extends GasPriceScale {
-  dryRun: boolean;
+  dryRun?: boolean;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/token-registry-functions/types.ts` around lines 5 - 9, The GasPriceScale
interface currently only exposes maxPriorityFeePerGasScale—decide whether this
asymmetry is intentional; if not, add a corresponding maxFeePerGasScale property
to GasPriceScale (or document/confirm why only the tip is user-scaled). Also
make GasOption.dryRun optional by changing its declaration on the GasOption
interface (so callers aren’t forced to pass dryRun: false); update any related
types/usages of GasOption to handle the optional boolean.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/token-registry-functions/types.ts`:
- Around line 116-117: NetworkAndWalletSignerOption currently uses
Partial<PrivateKeyOption> which is redundant because PrivateKeyOption's fields
are already optional; remove Partial<> and simplify the union so
NetworkAndWalletSignerOption = NetworkOption & (Partial<WalletOption> |
PrivateKeyOption) (keeping Partial<WalletOption> to allow encryptedWalletPath to
be optional) and update any references to NetworkAndWalletSignerOption to match
the simplified type; ensure imports/types for NetworkOption, WalletOption, and
PrivateKeyOption remain unchanged.
- Around line 5-9: The GasPriceScale interface currently only exposes
maxPriorityFeePerGasScale—decide whether this asymmetry is intentional; if not,
add a corresponding maxFeePerGasScale property to GasPriceScale (or
document/confirm why only the tip is user-scaled). Also make GasOption.dryRun
optional by changing its declaration on the GasOption interface (so callers
aren’t forced to pass dryRun: false); update any related types/usages of
GasOption to handle the optional boolean.

Copy link
Copy Markdown
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.

🧹 Nitpick comments (2)
src/document-store/transferOwnership.ts (1)

53-58: Null guards on transaction results are dead code.

documentStoreGrantRole and documentStoreRevokeRole either return a truthy transaction object or throw — they never return a falsy value. The if (!grantTransactionResult) and if (!revokeTransactionResult) branches can therefore never be entered.

♻️ Proposed cleanup
-  const grantTransactionResult = await grantTransaction;
-  if (!grantTransactionResult) {
-    throw new Error('Grant transaction failed, not proceeding with revoke transaction');
-  }
+  await grantTransaction;

   const revokeTransaction = documentStoreRevokeRole(
     documentStoreAddress,
     roleString,
     ownerAddress,
     signer,
     options,
   );
-  const revokeTransactionResult = await revokeTransaction;
-  if (!revokeTransactionResult) {
-    throw new Error('Revoke transaction failed');
-  }
+  await revokeTransaction;

Also applies to: 68-72

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document-store/transferOwnership.ts` around lines 53 - 58, The null-check
branches around the awaited transaction results are dead code because
documentStoreGrantRole and documentStoreRevokeRole either return a transaction
object or throw; remove the unreachable guards and their error throws in
transferOwnership.ts: delete the if (!grantTransactionResult) { ... } block
(referring to grantTransactionResult and the accompanying error throw) and
likewise remove the if (!revokeTransactionResult) { ... } block that checks
revokeTransactionResult; keep the awaits (const grantTransactionResult = await
grantTransaction; const revokeTransactionResult = await revokeTransaction;) and
proceed using those results or let thrown errors propagate.
src/document-store/grant-role.ts (1)

108-111: Original error is swallowed — consider chaining it as cause.

The caught error e is logged but discarded when the generic Error is thrown, which makes root-cause debugging harder in production (only the log survives if the caller catches and re-throws).

♻️ Proposed fix: chain the original error
-    console.error('callStatic failed:', e);
-    throw new Error('Pre-check (callStatic) for grant-role failed');
+    throw new Error('Pre-check (callStatic) for grant-role failed', { cause: e });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document-store/grant-role.ts` around lines 108 - 111, The catch block in
grant-role.ts currently logs the caught error `e` and throws a new generic
Error('Pre-check (callStatic) for grant-role failed') which discards the
original error; update the catch to rethrow the new Error while chaining the
original error as its cause (use the Error constructor's options or equivalent)
so callers can inspect the root cause (preserve or keep the console.error line
if desired), referencing the existing thrown message and the caught variable
`e`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/document-store/grant-role.ts`:
- Around line 108-111: The catch block in grant-role.ts currently logs the
caught error `e` and throws a new generic Error('Pre-check (callStatic) for
grant-role failed') which discards the original error; update the catch to
rethrow the new Error while chaining the original error as its cause (use the
Error constructor's options or equivalent) so callers can inspect the root cause
(preserve or keep the console.error line if desired), referencing the existing
thrown message and the caught variable `e`.

In `@src/document-store/transferOwnership.ts`:
- Around line 53-58: The null-check branches around the awaited transaction
results are dead code because documentStoreGrantRole and documentStoreRevokeRole
either return a transaction object or throw; remove the unreachable guards and
their error throws in transferOwnership.ts: delete the if
(!grantTransactionResult) { ... } block (referring to grantTransactionResult and
the accompanying error throw) and likewise remove the if
(!revokeTransactionResult) { ... } block that checks revokeTransactionResult;
keep the awaits (const grantTransactionResult = await grantTransaction; const
revokeTransactionResult = await revokeTransaction;) and proceed using those
results or let thrown errors propagate.
ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15339ad and a77083f.

📒 Files selected for processing (2)
  • src/document-store/grant-role.ts
  • src/document-store/transferOwnership.ts

@RishabhS7
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@RishabhS7
Copy link
Copy Markdown
Contributor Author

@CodeRabbit full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 24, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
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: 1

♻️ Duplicate comments (7)
src/__tests__/token-registry-functions/deploy.test.ts (1)

371-408: ⚠️ Potential issue | 🟡 Minor

Chain-ID auto-detect test still passes a chainId.

Line 405 supplies chainId, so the auto-detect branch (and getChainIdSafe) never runs. Consider omitting chainId and asserting getChainIdSafe is called; also ensure the utils mock exports a mocked getChainIdSafe.

✅ Suggested adjustment
-      await deployTokenRegistry(mockRegistryName, mockRegistrySymbol, wallet, {
-        chainId: mockChainId,
-      });
+      await deployTokenRegistry(mockRegistryName, mockRegistrySymbol, wallet, {});
+      expect(getChainIdSafe).toHaveBeenCalled();
#!/bin/bash
# Verify getChainIdSafe is mocked in fixtures and inspect the chain-id test.
rg -n "getChainIdSafe" src/__tests__/token-registry-functions/fixtures.ts -C2
rg -n "auto-detect chain ID" src/__tests__/token-registry-functions/deploy.test.ts -C3
rg -n "chainId:\s*mockChainId" src/__tests__/token-registry-functions/deploy.test.ts -C2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/token-registry-functions/deploy.test.ts` around lines 371 -
408, The test currently passes a chainId into deployTokenRegistry so the
auto-detect branch never runs; remove the chainId from the deployTokenRegistry
call in the "should auto-detect chain ID when not provided" test and instead
assert that getChainIdSafe was invoked (mock and spy the utils export
getChainIdSafe) and that deployTokenRegistry still resolves; ensure
getChainIdSafe is properly exported and mocked in your test fixtures (search for
getChainIdSafe in fixtures and add a vi.fn() mock if missing) so the test
verifies auto-detection behavior for deployTokenRegistry.
src/__tests__/token-registry-functions/fixtures.ts (2)

368-373: Avoid module-scope getNetwork spy.

Same as prior review: move the spy into a test lifecycle hook to avoid fragile cross-test state.

♻️ Suggested change
 export const providerV5 = new ethersV5.providers.JsonRpcProvider();
-// Mock the getNetwork method for v5 provider
-vi.spyOn(providerV5, 'getNetwork').mockResolvedValue({
-  name: 'mainnet',
-  chainId: 1,
-});

Then add a beforeEach/beforeAll spy in the consuming tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/token-registry-functions/fixtures.ts` around lines 368 - 373,
The module-level spy on providerV5.getNetwork is creating cross-test state;
remove the vi.spyOn call from the top-level in fixtures.ts and instead set up
and restore the spy inside the test lifecycle of consuming tests (e.g., add a
beforeEach or beforeAll that does vi.spyOn(providerV5,
'getNetwork').mockResolvedValue({ name: 'mainnet', chainId: 1 }) and an
afterEach/afterAll that restores it via vi.restoreAllMocks() or
spy.mockRestore(); keep the providerV5 instance export unchanged but move any
mocking into the tests that import it.

40-45: ⚠️ Potential issue | 🟡 Minor

Route MockContractConstructor by address only.

Same issue as prior review: both ABIs are identical, so routing by ABI collapses v4 into v5.

🔧 Proposed fix
-    const isV5 = address === MOCK_V5_ADDRESS || abi === 'TradeTrustToken';
+    const isV5 = address === MOCK_V5_ADDRESS;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/token-registry-functions/fixtures.ts` around lines 40 - 45, The
mock factory MockContractConstructor currently selects v5 vs v4 by checking
address OR abi, which collapses v4 into v5 because ABIs are identical; change
the selection logic in MockContractConstructor to route solely by address (e.g.,
compare address to MOCK_V5_ADDRESS and return mockV5TradeTrustTokenContract,
otherwise return mockV4TradeTrustTokenContract) and remove the abi ===
'TradeTrustToken' check so mockV4TradeTrustTokenContract is reachable.
src/deploy/document-store.ts (1)

131-137: ⚠️ Potential issue | 🟡 Minor

Guard deploymentTransaction() before .wait().

Same as prior review: add a null guard to keep the return type safe.

🔒 Proposed fix
-      return await contract.deploymentTransaction().wait();
+      const deployTx = contract.deploymentTransaction();
+      if (!deployTx) throw new Error('Deployment transaction receipt not available');
+      return await deployTx.wait();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/deploy/document-store.ts` around lines 131 - 137, The code calls
contract.deploymentTransaction().wait() without guarding for a null/undefined
deployment transaction; in the deploy path using ContractFactoryV6 (symbols:
contractFactory, ContractFactoryV6, deploy, contract.deploymentTransaction,
.wait) add a null check: retrieve the result of
contract.deploymentTransaction(), ensure it is not null/undefined before calling
.wait(), and handle the null case by returning a safe fallback (e.g., null or a
rejected Promise) or throwing a descriptive error so the function's return type
remains correct and type-safe.
src/deploy/token-registry.ts (3)

220-228: ⚠️ Potential issue | 🟡 Minor

Guard deploymentTransaction() before .wait().

Same as prior review: add a null guard to keep return type consistent.

🔒 Proposed fix
-      return await contract.deploymentTransaction().wait();
+      const deployTx = contract.deploymentTransaction();
+      if (!deployTx) throw new Error('Deployment transaction receipt not available');
+      return await deployTx.wait();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/deploy/token-registry.ts` around lines 220 - 228, The code calls
deploymentTransaction().wait() on the contract returned by (tokenFactory as
ContractFactoryV6).deploy(...); guard against a null/undefined
deploymentTransaction to keep the function's return type consistent: after
calling deploy in the isV6 branch (symbols: isV6, tokenFactory,
ContractFactoryV6, deploy, contract, deploymentTransaction, wait), check if
contract.deploymentTransaction() is truthy and only call .wait() when present,
otherwise return the appropriate empty/undefined/default value consistent with
the non-v6 branch.

184-185: ⚠️ Potential issue | 🔴 Critical

Quick-start should return a mined receipt.

Same as prior review: deployerContract.deploy(...) returns a tx response, not a receipt. Await .wait() to fulfill the documented return type.

🧾 Proposed fix
-    return await deployerContract.deploy(tokenRegistryImplAddress, initParam, txOptions);
+    const tx = await deployerContract.deploy(tokenRegistryImplAddress, initParam, txOptions);
+    return await tx.wait();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/deploy/token-registry.ts` around lines 184 - 185, The current return from
deployerContract.deploy(tokenRegistryImplAddress, initParam, txOptions) is a
transaction response, not a mined receipt; change the implementation to await
the deploy call into a variable (e.g., tx = await deployerContract.deploy(...))
and then await tx.wait() and return that receipt so the function returns a mined
receipt as documented.

121-165: ⚠️ Potential issue | 🟠 Major

Add a signer.provider guard before use.

Same issue as prior review: signer.provider can be undefined, causing failures in getEthersContractFromProvider and isSupportedTitleEscrowFactory.

🔒 Proposed fix
 export const deployTokenRegistry = async (
   registryName: string,
   registrySymbol: string,
   signer: SignerV5 | SignerV6,
   options: DeployOptions = {},
 ): Promise<TransactionReceipt> => {
+  if (!signer.provider) throw new Error('Provider is required');
   // Extract gas options
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/deploy/token-registry.ts` around lines 121 - 165, Add a guard to ensure
signer.provider is defined before any use: check signer.provider at the top of
the function (before calling getEthersContractFromProvider and
isSupportedTitleEscrowFactory) and either throw a clear error (e.g., "Signer has
no provider; cannot deploy in quick-start mode") or obtain/require a provider
parameter; then use signer.provider only after this check when calling
getEthersContractFromProvider and isSupportedTitleEscrowFactory to avoid
undefined access.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/deploy/token-registry.ts`:
- Around line 115-120: The function deployTokenRegistry destructures properties
from the options parameter but doesn't provide a default, causing a crash if
callers omit options; update the deployTokenRegistry signature to default
options to an empty object (options = {}) so destructuring inside the function
is safe and existing callers that pass nothing continue to work; ensure you
update any JSDoc/type annotations if present for DeployOptions to reflect
optionality.

---

Duplicate comments:
In `@src/__tests__/token-registry-functions/deploy.test.ts`:
- Around line 371-408: The test currently passes a chainId into
deployTokenRegistry so the auto-detect branch never runs; remove the chainId
from the deployTokenRegistry call in the "should auto-detect chain ID when not
provided" test and instead assert that getChainIdSafe was invoked (mock and spy
the utils export getChainIdSafe) and that deployTokenRegistry still resolves;
ensure getChainIdSafe is properly exported and mocked in your test fixtures
(search for getChainIdSafe in fixtures and add a vi.fn() mock if missing) so the
test verifies auto-detection behavior for deployTokenRegistry.

In `@src/__tests__/token-registry-functions/fixtures.ts`:
- Around line 368-373: The module-level spy on providerV5.getNetwork is creating
cross-test state; remove the vi.spyOn call from the top-level in fixtures.ts and
instead set up and restore the spy inside the test lifecycle of consuming tests
(e.g., add a beforeEach or beforeAll that does vi.spyOn(providerV5,
'getNetwork').mockResolvedValue({ name: 'mainnet', chainId: 1 }) and an
afterEach/afterAll that restores it via vi.restoreAllMocks() or
spy.mockRestore(); keep the providerV5 instance export unchanged but move any
mocking into the tests that import it.
- Around line 40-45: The mock factory MockContractConstructor currently selects
v5 vs v4 by checking address OR abi, which collapses v4 into v5 because ABIs are
identical; change the selection logic in MockContractConstructor to route solely
by address (e.g., compare address to MOCK_V5_ADDRESS and return
mockV5TradeTrustTokenContract, otherwise return mockV4TradeTrustTokenContract)
and remove the abi === 'TradeTrustToken' check so mockV4TradeTrustTokenContract
is reachable.

In `@src/deploy/document-store.ts`:
- Around line 131-137: The code calls contract.deploymentTransaction().wait()
without guarding for a null/undefined deployment transaction; in the deploy path
using ContractFactoryV6 (symbols: contractFactory, ContractFactoryV6, deploy,
contract.deploymentTransaction, .wait) add a null check: retrieve the result of
contract.deploymentTransaction(), ensure it is not null/undefined before calling
.wait(), and handle the null case by returning a safe fallback (e.g., null or a
rejected Promise) or throwing a descriptive error so the function's return type
remains correct and type-safe.

In `@src/deploy/token-registry.ts`:
- Around line 220-228: The code calls deploymentTransaction().wait() on the
contract returned by (tokenFactory as ContractFactoryV6).deploy(...); guard
against a null/undefined deploymentTransaction to keep the function's return
type consistent: after calling deploy in the isV6 branch (symbols: isV6,
tokenFactory, ContractFactoryV6, deploy, contract, deploymentTransaction, wait),
check if contract.deploymentTransaction() is truthy and only call .wait() when
present, otherwise return the appropriate empty/undefined/default value
consistent with the non-v6 branch.
- Around line 184-185: The current return from
deployerContract.deploy(tokenRegistryImplAddress, initParam, txOptions) is a
transaction response, not a mined receipt; change the implementation to await
the deploy call into a variable (e.g., tx = await deployerContract.deploy(...))
and then await tx.wait() and return that receipt so the function returns a mined
receipt as documented.
- Around line 121-165: Add a guard to ensure signer.provider is defined before
any use: check signer.provider at the top of the function (before calling
getEthersContractFromProvider and isSupportedTitleEscrowFactory) and either
throw a clear error (e.g., "Signer has no provider; cannot deploy in quick-start
mode") or obtain/require a provider parameter; then use signer.provider only
after this check when calling getEthersContractFromProvider and
isSupportedTitleEscrowFactory to avoid undefined access.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f00437 and a77083f.

📒 Files selected for processing (17)
  • src/__tests__/document-store/deploy.test.ts
  • src/__tests__/token-registry-functions/deploy.test.ts
  • src/__tests__/token-registry-functions/fixtures.ts
  • src/__tests__/token-registry-functions/ownerOf.test.ts
  • src/__tests__/token-registry-functions/transfers.test.ts
  • src/deploy/document-store.ts
  • src/deploy/token-registry.ts
  • src/document-store/deploy.ts
  • src/document-store/grant-role.ts
  • src/document-store/index.ts
  • src/document-store/transferOwnership.ts
  • src/token-registry-functions/index.ts
  • src/token-registry-functions/ownerOf.ts
  • src/token-registry-functions/types.ts
  • src/token-registry-functions/utils.ts
  • src/token-registry-v5/index.ts
  • src/utils/ethers/index.ts
💤 Files with no reviewable changes (1)
  • src/document-store/deploy.ts

Comment thread src/deploy/token-registry.ts
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
4.3% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link
Copy Markdown
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.

♻️ Duplicate comments (2)
src/deploy/token-registry.ts (2)

184-185: ⚠️ Potential issue | 🔴 Critical

Return a mined receipt in quick-start mode.
deployerContract.deploy(...) returns a transaction response (v5/v6), not a receipt, but the function promises a TransactionReceipt. Await .wait() before returning.

✅ Proposed fix
-    return await deployerContract.deploy(tokenRegistryImplAddress, initParam, txOptions);
+    const tx = await deployerContract.deploy(tokenRegistryImplAddress, initParam, txOptions);
+    return await tx.wait();
In ethers v5/v6, what does a Contract method transaction return, and is `.wait()` required to obtain a TransactionReceipt?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/deploy/token-registry.ts` around lines 184 - 185, The current return uses
deployerContract.deploy(...) which yields a TransactionResponse in ethers v5/v6,
but the function signature promises a TransactionReceipt; update the code to
await the transaction response's .wait() and return that receipt (i.e., call
deployTx = await deployerContract.deploy(tokenRegistryImplAddress, initParam,
txOptions); const receipt = await deployTx.wait(); return receipt) so
deployerContract.deploy, tokenRegistryImplAddress, initParam and txOptions are
used as before but the function returns the mined TransactionReceipt.

115-165: ⚠️ Potential issue | 🟠 Major

Guard against missing signer.provider before provider-based helpers.
Signer.provider can be undefined (especially in ethers v5), which will break provider-dependent helpers and isV6EthersProvider. Add an explicit guard near the top of the function.

🔧 Proposed fix
 export const deployTokenRegistry = async (
   registryName: string,
   registrySymbol: string,
   signer: SignerV5 | SignerV6,
   options: DeployOptions = {},
 ): Promise<TransactionReceipt> => {
+  if (!signer.provider) {
+    throw new Error('Signer has no provider; cannot deploy Token Registry');
+  }
   // Extract gas options
   const { maxFeePerGas, maxPriorityFeePerGas } = options;
In ethers v5 and v6, is `Signer.provider` optional/undefined for certain signer types, and is a guard recommended before accessing it?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/deploy/token-registry.ts` around lines 115 - 165, Add an explicit guard
for a missing signer.provider at the start of deployTokenRegistry: check if
signer.provider is defined before calling any provider-dependent helpers (e.g.,
getEthersContractFromProvider, isV6EthersProvider, getChainIdSafe) and either
throw a clear error (e.g., "signer.provider is required for deployTokenRegistry
in non-standalone mode") or handle the fallback path (force standalone mode)
depending on desired behavior; update references in deployTokenRegistry to use
this guard so provider-based calls only occur when signer.provider is present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/deploy/token-registry.ts`:
- Around line 184-185: The current return uses deployerContract.deploy(...)
which yields a TransactionResponse in ethers v5/v6, but the function signature
promises a TransactionReceipt; update the code to await the transaction
response's .wait() and return that receipt (i.e., call deployTx = await
deployerContract.deploy(tokenRegistryImplAddress, initParam, txOptions); const
receipt = await deployTx.wait(); return receipt) so deployerContract.deploy,
tokenRegistryImplAddress, initParam and txOptions are used as before but the
function returns the mined TransactionReceipt.
- Around line 115-165: Add an explicit guard for a missing signer.provider at
the start of deployTokenRegistry: check if signer.provider is defined before
calling any provider-dependent helpers (e.g., getEthersContractFromProvider,
isV6EthersProvider, getChainIdSafe) and either throw a clear error (e.g.,
"signer.provider is required for deployTokenRegistry in non-standalone mode") or
handle the fallback path (force standalone mode) depending on desired behavior;
update references in deployTokenRegistry to use this guard so provider-based
calls only occur when signer.provider is present.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a77083f and adec3e8.

📒 Files selected for processing (1)
  • src/deploy/token-registry.ts

@rongquan1 rongquan1 merged commit 9faf75f into main Feb 25, 2026
20 of 21 checks passed
@rongquan1 rongquan1 deleted the feat/token-registry-deploy branch February 25, 2026 05:55
nghaninn pushed a commit that referenced this pull request Feb 25, 2026
## [2.9.0](v2.8.0...v2.9.0) (2026-02-25)

### Features

* token registry deploy ([#130](#130)) ([9faf75f](9faf75f))
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 2.9.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants