Skip to content

feat: deploy issue revoke functions#128

Merged
rongquan1 merged 11 commits intomainfrom
feat/add-document-store-functions
Feb 12, 2026
Merged

feat: deploy issue revoke functions#128
rongquan1 merged 11 commits intomainfrom
feat/add-document-store-functions

Conversation

@RishabhS7
Copy link
Copy Markdown
Contributor

@RishabhS7 RishabhS7 commented Feb 11, 2026

PR Summary: Add Document Store Functions

Overview

This PR adds comprehensive document store functionality to the TrustVC library, enabling deployment, issuance, revocation, and role management of document stores on blockchain networks.

Key Features Added

📋 Document Store Deployment

  • deployDocumentStore() function supporting both ethers v5 and v6
  • Support for both standard DocumentStore and TransferableDocumentStore contracts
  • Configurable gas settings and chain ID support
  • Comprehensive error handling and validation

📄 Document Management

  • issueDocument() - Issue documents to the store
  • revokeDocument() - Revoke issued documents
  • Batch operations support for multiple documents

🔐 Role Management

  • grantRole() - Grant roles to addresses
  • revokeRole() - Revoke roles from addresses
  • Support for document store specific roles

🧪 Comprehensive Testing

  • Full test coverage for all functions
  • Ethers v5 and v6 compatibility testing
  • Mock factories and fixtures for reliable testing
  • Error case validation

Technical Implementation

Files Added:

  • src/document-store/deploy.ts - Deployment functionality
  • src/document-store/issue.ts - Document issuance
  • src/document-store/revoke.ts - Document revocation
  • src/document-store/document-store-roles.ts - Role definitions
  • src/document-store/contract-interfaces.ts - Contract interfaces
  • src/document-store/index.ts - Module exports
  • Test files with fixtures for all functionality

Key Technical Details:

  • Dual ethers version support (v5/v6) with automatic detection
  • TypeScript interfaces for all function parameters
  • Transaction receipt handling for deployment confirmation
  • Gas optimization through configurable transaction options
  • Comprehensive error messages for debugging

Breaking Changes

None - this is a new feature addition.

Testing

  • 15 new test files with 1972 lines of test code
  • Tests cover both ethers versions and all error scenarios
  • Mock implementations for reliable CI/CD execution

This implementation provides a complete document store management solution for blockchain applications requiring secure document issuance and management capabilities.

Summary by CodeRabbit

  • New Features

    • Deploy, issue, and revoke documents across multiple contract variants with automatic interface detection, legacy fallback, and gas option controls
    • Resolve document store roles (admin / issuer / revoker)
    • Ethers v5/v6 compatibility and public exports for document-store operations
  • Documentation

    • Added comprehensive "Document Store" usage guide with examples and API descriptions
  • Tests

    • Extensive unit tests covering deploy, issue, revoke flows, interface detection, fallbacks, and error paths
  • Chores

    • Added document-store dependency

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Adds a Document Store feature set: deploy, issue, revoke, role resolution, ERC‑165 interface detection with TT fallback, ethers v5/v6 compatibility, ABIs/interface IDs, public exports, tests/fixtures, README docs, and adds dependency @trustvc/document-store (^1.0.3).

Changes

Cohort / File(s) Summary
Dependency
package.json
Added dependency @trustvc/document-store ^1.0.3.
Interface & ABI defs
src/document-store/contract-interfaces.ts, src/document-store/supportInterfaceIds.ts, src/document-store/tt-document-store-abi.ts
Adds function sighash lists, computed ERC‑165 interface IDs, and a legacy TT Document Store ABI for detection/fallback.
Core operations
src/document-store/deploy.ts, src/document-store/issue.ts, src/document-store/revoke.ts, src/document-store/document-store-roles.ts
Implements deploy/issue/revoke and role-resolution with ethers v5/v6 support, auto-detection of contract type (ERC‑165), TT fallback, pre-callStatic/staticCall checks, gas/chainId options, and typed option interfaces. Exports new types and functions.
Module exports / barrel
src/document-store/index.ts, src/index.ts
Re-exports new document-store APIs and contract factories in public barrels; some role-related exports remain commented out.
Tests & fixtures
src/__tests__/document-store/fixtures.ts, src/__tests__/document-store/deploy.test.ts, src/__tests__/document-store/issue.test.ts, src/__tests__/document-store/revoke.test.ts
Adds comprehensive unit tests and fixtures covering deploy/issue/revoke across contract variants and ethers v5/v6 with mocked providers, wallets, interface checks, and many success/error paths.
Docs
README.md
Adds "9. Document Store" documentation with usage examples, API descriptions, examples for deploy/issue/revoke, and migration/behavior notes.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/App
    participant DS as DocumentStore\nModule
    participant Provider as Ethers\nProvider
    participant Contract as Contract\nInstance
    participant Chain as Blockchain

    Client->>DS: documentStoreIssue(address, hash, signer)
    activate DS
    DS->>Provider: determine signer/version (v5 or v6)
    Provider-->>DS: version info

    alt auto-detect contract type
        DS->>Contract: checkSupportsInterface(IDocumentStore)
        Contract-->>DS: supports? / not?
        alt not supported
            DS->>Contract: checkSupportsInterface(ITransferableDocumentStore)
            Contract-->>DS: supports? / not?
            alt none supported
                DS->>DS: select TT Document Store ABI
            else
                DS->>DS: select TransferableDocumentStore ABI
            end
        else
            DS->>DS: select DocumentStore ABI
        end
    else
        DS->>DS: use explicit contract type
    end

    DS->>Contract: pre-check (callStatic/staticCall).issue(hash)
    Contract-->>DS: pre-check result
    alt pre-check passed
        DS->>DS: build tx options (gas/chainId)
        DS->>Contract: send transaction: issue(hash)
        Contract->>Chain: broadcast tx
        Chain-->>Contract: tx receipt
        Contract-->>DS: receipt
        DS-->>Client: return transaction/receipt
    else
        DS-->>Client: throw pre-check error
    end
    deactivate DS
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

released on @v1``

Suggested reviewers

  • rongquan1
  • nghaninn
  • kmk142789

Poem

🐇
I hop with keys and ABI bright,
I sign and send throughout the night,
Issue, revoke, resolve each role,
v5 or v6 — I play the goal,
A TT fallback keeps it light.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: deploy issue revoke functions' accurately summarizes the main changes: adding deployment, issue, and revoke functions for document stores.
Description check ✅ Passed The description provides comprehensive coverage with a clear Overview, Key Features section, Technical Implementation details, Breaking Changes statement, and Testing information, exceeding the template requirements.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add-document-store-functions

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: 13

🤖 Fix all issues with AI agents
In `@package.json`:
- Line 124: The dependency "@trustvc/document-store" in package.json is invalid;
update package.json to remove or replace this entry: either delete the
"@trustvc/document-store": "^1.0.2" line if unused, or change it to the correct
package name (e.g. "@tradetrust-tt/document-store" or the canonical TradeTrust
package) and run npm install to verify; ensure any import/require references in
the codebase (search for "@trustvc/document-store") are updated to the new
package identifier as well.

In `@src/__tests__/document-store/deploy.test.ts`:
- Line 32: Remove the leftover debug console.log by deleting the statement that
logs "result" (the console.log('result', result) call) from the test in
deploy.test.ts; if needed, replace it with or strengthen an assertion that
checks the expected properties of the variable result so the test verifies
behavior rather than printing debug output.

In `@src/__tests__/document-store/fixtures.ts`:
- Around line 6-8: The mock address constants (MOCK_DOCUMENT_STORE_ADDRESS,
MOCK_TRANSFERABLE_DOCUMENT_STORE_ADDRESS, MOCK_TT_DOCUMENT_STORE_ADDRESS) use
non-hex strings and should be replaced with valid 0x-prefixed 40-hex-char
Ethereum addresses (preferably EIP-55 checksummed) so code paths that validate
addresses (e.g., ethers.utils.getAddress) won’t fail; update the three constants
to valid checksum addresses (or derive them via ethers.utils.getAddress in
tests) while keeping the same constant names.

In `@src/__tests__/document-store/issue.test.ts`:
- Around line 56-58: The describe.each callback is declared async which is
incorrect because describe/describe.each must use a synchronous factory
function; remove the async keyword from the describe.each callback (the function
that currently takes { Provider, ethersVersion, contractType }) and, if any
awaited setup exists, move that asynchronous work into beforeAll/beforeEach or
individual it blocks (refer to the describe.each usage and any
beforeAll/beforeEach/it inside the same block to relocate awaits).
- Around line 82-90: The test file is missing beforeAll in the vitest import
list; update the top import that currently pulls in "describe, it, expect,
beforeEach, vi" to also include "beforeAll" so the file consistently uses
explicit imports (e.g., change the import to "describe, it, expect, beforeEach,
beforeAll, vi"); no other code changes are needed—just add beforeAll to the
import statement so the beforeAll hook is explicitly imported.
- Around line 229-252: The failing tests call documentStoreIssue which
internally calls getEthersContractFromProvider, but vi.clearAllMocks() in
beforeEach clears that mock for the tests at "should handle invalid document
hash format gracefully" and "should handle already issued document";
re-establish the mock in both tests by creating a mockContractConstructor that
returns mockContract and then calling
vi.mocked(getEthersContractFromProvider).mockReturnValue(mockContractConstructor(mockContract))
so documentStoreIssue receives the expected mockContract when it calls
getEthersContractFromProvider(signer.provider).

In `@src/__tests__/document-store/revoke.test.ts`:
- Around line 56-58: The describe.each callback is declared async which is
incorrect because describe must register synchronously; remove the async
modifier from the describe.each callback and move any asynchronous setup/awaits
inside that block into proper lifecycle hooks (e.g., beforeAll, beforeEach, or
inside individual it/test callbacks). Locate the describe.each usage that
iterates over providers and the callback function (the one referencing Provider,
ethersVersion, contractType) and ensure it is synchronous, relocating any awaits
to hooks like beforeAll/afterAll or to the tests themselves.
- Around line 82-102: The beforeAll sets a mock for
getEthersContractFromProvider which is then wiped by beforeEach's
vi.clearAllMocks; either move the getEthersContractFromProvider mock setup into
beforeEach after the vi.clearAllMocks call (so the mock is re-established each
test) or remove the vi.clearAllMocks from beforeEach and replace it with
targeted mock resets (e.g., mockContract.mockReset or specific .mockReset on
getEthersContractFromProvider) to preserve the getEthersContractFromProvider
mock; update references in the test setup where mockContract.callStatic.revoke
and mockContract.revoke.staticCall are set so they run after clearing/resetting
mocks.

In `@src/document-store/deploy.ts`:
- Line 58: Remove the debug console.log calls that were left in production:
delete the console.log('isv6', isV6) and the other console.log statements around
that area (any remaining console.log uses in this deploy logic) and either
replace them with the project's structured logger (e.g., processLogger.debug or
a configured logger) or remove them entirely; search for occurrences of
console.log in this file and update the code paths that reference isV6 and
related deployment variables to use the proper logging API (or no logging) so no
debug console statements remain.
- Line 75: deploymentTransaction() can return null so calling .wait() unguarded
may throw; update the code around the call in deploy.ts to check the result of
contract.deploymentTransaction() before calling .wait() — store the transaction
in a local (e.g., const tx = contract.deploymentTransaction()), if tx is null
handle it (throw a descriptive error or log and return) and only call await
tx.wait() when tx is non-null, assigning the result to receipt; ensure any
downstream use of receipt handles the error path accordingly.

In `@src/document-store/document-store-roles.ts`:
- Around line 18-23: The code can dereference SUPPORTED_CHAINS[chainId].name
when both options.provider and options.chainId are missing; in getRoleString,
check for a valid provider first and only access SUPPORTED_CHAINS when chainId
is defined and points to a valid entry. If chainId is missing or invalid, either
use a safe default (e.g., DEFAULT_CHAIN_ID) or throw a clear error; then call
utils.getProvider({ network: SUPPORTED_CHAINS[chainId].name }) (or with the
default) so you never read .name on undefined. Ensure the guards involve the
symbols options, chainId, provider, SUPPORTED_CHAINS, and utils.getProvider to
locate and fix the logic.

In `@src/document-store/index.ts`:
- Around line 3-4: The exports revokeDocumentStoreRole and
grantDocumentStoreRole in src/document-store/index.ts reference missing modules
('./revoke-role' and './grant-role') and break the build; remove these two
export lines from index.ts (or alternatively add the corresponding module files
implementing revokeDocumentStoreRole and grantDocumentStoreRole) so the package
no longer imports non-existent modules—ensure any code that depended on those
symbols is updated or guarded until the modules are added.

In `@src/document-store/revoke.ts`:
- Around line 47-52: The public API for documentStoreRevoke is inconsistent with
documentStoreIssue: change the documentStoreRevoke function signature to
(documentStoreAddress: string, documentHash: string, signer: SignerV5 |
SignerV6, options: RevokeOptions = {}) to match documentStoreIssue; update all
internal uses within this module (and any exports) to pass documentHash before
signer and adjust any parameter destructuring or calls to functions like
getDocumentStoreContract or revoke on the contract accordingly so runtime calls
remain correct.
🧹 Nitpick comments (11)
src/__tests__/document-store/fixtures.ts (1)

126-259: Consider extracting a factory function for mock contracts to reduce duplication.

The three mock contract objects (mockDocumentStoreContract, mockTransferableDocumentStoreContract, mockTTDocumentStoreContract) are nearly identical, differing only in the string prefix of the resolved tx hash and the name return value. A helper function like createMockContract(prefix: string, name: string) would eliminate ~130 lines of repetition.

src/document-store/deploy.ts (1)

42-50: Input validation could be more robust for owner address.

The function validates that owner is truthy but does not check if it's a valid Ethereum address. An invalid address would only fail later during deployment with a less descriptive error. Consider validating with ethers.utils.isAddress(owner).

src/document-store/tt-document-store-abi.ts (2)

7-9: Stale/misleading TODO comments should be cleaned up.

Line 8's TODO says "Replace with the actual full ABI" and line 9 says "Minimal ABI for issue function" — but the ABI below already contains ~20 functions, events, and a constructor. Similarly, line 232's comment says "Add other functions from the ABI here (name, isIssued, isRevoked, etc.)" but those functions are already present (lines 143, 160, 186). These stale comments are misleading and suggest the code is incomplete when it may not be.


1-5: Doc comment contradicts ABI contents re: supportsInterface.

The file header (line 3) states this is "a legacy document store contract that doesn't support ERC-165 supportsInterface," yet the ABI includes a supportsInterface(bytes4) entry (lines 218-224). If the contract truly doesn't support ERC-165, having it in the ABI is confusing. If it does have the function but just returns false for known interface IDs, the doc comment should clarify that distinction.

Also applies to: 218-224

src/document-store/document-store-roles.ts (1)

13-42: Consider narrowing role parameter type to the known role literals.

The role parameter is string, but the only valid values are 'admin' | 'issuer' | 'revoker'. Using a union type (or at least typeof rolesList[number]) would give callers compile-time feedback instead of a runtime 'Invalid role' error.

src/document-store/revoke.ts (2)

85-89: Unreachable dead code — this condition can never be true.

When isTransferable is provided (not undefined), one of isDocumentStore or isTransferableDocumentStore is always true (lines 59-60). When isTransferable is undefined, the detection block (lines 64-83) sets isTTDocumentStore = true if both are false. Therefore, the condition !isDocumentStore && !isTransferableDocumentStore && !isTTDocumentStore on line 85 is unreachable.

This isn't harmful, but it's worth noting for clarity. Same applies to the identical block in issue.ts (line 85).


47-131: Significant code duplication with issue.ts.

The entire body of documentStoreRevoke is nearly identical to documentStoreIssue — the contract type detection, ABI selection, contract instantiation, callStatic pre-check, and tx execution logic are all the same with only issuerevoke substituted. Consider extracting shared logic (e.g., contract detection + instantiation, pre-check execution) into a common helper to reduce duplication.

src/__tests__/document-store/issue.test.ts (1)

106-118: Inconsistent argument order in documentStoreIssue calls — blank lines between arguments.

Throughout the test, calls to documentStoreIssue have blank lines inserted between arguments (e.g., line 109 between mockDocumentStoreAddress and mockDocumentHash). This is just a formatting/readability nit but is unusual and inconsistent. Consider removing the blank lines for cleaner call sites.

src/document-store/issue.ts (2)

121-124: console.error in library code may be undesirable for consumers.

Logging the raw error to console.error in a library function creates noise that consumers can't control. Consider either removing it, or re-throwing the original error as the cause so consumers can inspect it if needed.

Proposed fix
   } catch (e) {
-    console.error('callStatic failed:', e);
-    throw new Error('Pre-check (callStatic) for issue failed');
+    throw new Error('Pre-check (callStatic) for issue failed', { cause: e });
   }

Same applies to revoke.ts line 122.


95-103: When both interfaces are supported, TransferableDocumentStore silently wins.

If a contract supports both IDocumentStore and ITransferableDocumentStore (which is the case for TransferableDocumentStore contracts that inherit DocumentStore), line 99 checks isTransferableDocumentStore first. This is likely the correct behavior, but adding a brief comment here would make the priority explicit for future maintainers.

src/document-store/index.ts (1)

1-10: Missing exports for role utilities (getRoleString, rolesList) from document-store-roles.ts.

The barrel file exports role mutation functions (grantDocumentStoreRole, revokeDocumentStoreRole) but doesn't export the role resolution utilities (getRoleString, rolesList) from ./document-store-roles. If these are intended to be part of the public API, add the re-export.

Comment thread package.json Outdated
"@tradetrust-tt/token-registry-v5": "npm:@tradetrust-tt/token-registry@^5.5.0",
"@tradetrust-tt/tradetrust": "^6.10.2",
"@tradetrust-tt/tt-verify": "^9.6.0",
"@trustvc/document-store": "^1.0.2",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

@trustvc/document-store npm package

💡 Result:

There doesn’t appear to be an npm package published as @trustvc/document-store (no npm listing turns up for that exact name). Instead, the “Document Store” smart-contract bindings/library used in the TrustVC/TradeTrust ecosystem is published as:

  • @tradetrust-tt/document-store (current TradeTrust package) — install with:
    npm i `@tradetrust-tt/document-store`
    and deploy via deployAndWait(...). [1]

Related/legacy alternatives you may see referenced:

  • @govtechsg/document-store (older OpenAttestation-era package). [2]
  • @trustvc/trustvc (TrustVC umbrella library; TradeTrust docs commonly instruct installing this for document workflows, but it’s not a standalone @trustvc/document-store package). [3][4]

Sources:
[1] (npmjs.com)
[2] (npmjs.com)
[3] (npmjs.com)
[4] (github.com)

Citations:


🏁 Script executed:

# Check package.json for ethers setup and current dependencies
cat -n package.json | head -150

Repository: TrustVC/trustvc

Length of output: 6595


Remove or replace @trustvc/document-store — package does not exist on npm.

The package @trustvc/document-store@^1.0.2 does not exist in the npm registry. npm install will fail on this dependency. Either remove it if unused, or replace with the correct package name (likely @tradetrust-tt/document-store based on TradeTrust documentation).

🤖 Prompt for AI Agents
In `@package.json` at line 124, The dependency "@trustvc/document-store" in
package.json is invalid; update package.json to remove or replace this entry:
either delete the "@trustvc/document-store": "^1.0.2" line if unused, or change
it to the correct package name (e.g. "@tradetrust-tt/document-store" or the
canonical TradeTrust package) and run npm install to verify; ensure any
import/require references in the codebase (search for "@trustvc/document-store")
are updated to the new package identifier as well.

Comment thread src/__tests__/document-store/deploy.test.ts Outdated
Comment on lines +6 to +8
export const MOCK_DOCUMENT_STORE_ADDRESS = '0xDocumentStoreContract';
export const MOCK_TRANSFERABLE_DOCUMENT_STORE_ADDRESS = '0xTransferableDocumentStoreContract';
export const MOCK_TT_DOCUMENT_STORE_ADDRESS = '0xTTDocumentStoreContract';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Mock addresses are not valid Ethereum addresses.

'0xDocumentStoreContract' and similar are not valid hex addresses (wrong length, non-hex characters). While these work for simple mock assertions, they may cause issues if any code path validates address format (e.g., ethers.utils.getAddress). Consider using properly checksummed 40-hex-char addresses for more robust mocking.

🤖 Prompt for AI Agents
In `@src/__tests__/document-store/fixtures.ts` around lines 6 - 8, The mock
address constants (MOCK_DOCUMENT_STORE_ADDRESS,
MOCK_TRANSFERABLE_DOCUMENT_STORE_ADDRESS, MOCK_TT_DOCUMENT_STORE_ADDRESS) use
non-hex strings and should be replaced with valid 0x-prefixed 40-hex-char
Ethereum addresses (preferably EIP-55 checksummed) so code paths that validate
addresses (e.g., ethers.utils.getAddress) won’t fail; update the three constants
to valid checksum addresses (or derive them via ethers.utils.getAddress in
tests) while keeping the same constant names.

Comment thread src/__tests__/document-store/issue.test.ts Outdated
Comment thread src/__tests__/document-store/issue.test.ts
Comment thread src/document-store/deploy.ts Outdated
Comment thread src/document-store/deploy.ts Outdated
console.log('before ContractFactory');
const contract = await ContractFactory.deploy(storeName, owner, txOptions);
console.log('after ContractFactory');
const receipt = await contract.deploymentTransaction().wait();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Potential null-pointer dereference: deploymentTransaction() can return null.

In ethers v6, deploymentTransaction() returns ContractTransactionResponse | null. If it returns null, calling .wait() on it will throw a runtime error. Add a null guard.

Proposed fix
-      const receipt = await contract.deploymentTransaction().wait();
+      const deployTx = contract.deploymentTransaction();
+      if (!deployTx) throw new Error('Deployment transaction not found');
+      const receipt = await deployTx.wait();
📝 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
const receipt = await contract.deploymentTransaction().wait();
const deployTx = contract.deploymentTransaction();
if (!deployTx) throw new Error('Deployment transaction not found');
const receipt = await deployTx.wait();
🤖 Prompt for AI Agents
In `@src/document-store/deploy.ts` at line 75, deploymentTransaction() can return
null so calling .wait() unguarded may throw; update the code around the call in
deploy.ts to check the result of contract.deploymentTransaction() before calling
.wait() — store the transaction in a local (e.g., const tx =
contract.deploymentTransaction()), if tx is null handle it (throw a descriptive
error or log and return) and only call await tx.wait() when tx is non-null,
assigning the result to receipt; ensure any downstream use of receipt handles
the error path accordingly.

Comment on lines +18 to +23
const { chainId } = options || {};
let provider = options?.provider;

if (!provider) {
provider = utils.getProvider({ network: SUPPORTED_CHAINS[chainId].name });
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Null-pointer dereference when both provider and chainId are missing.

If getRoleString is called without options, or with options that omit both provider and chainId, then chainId is undefined. SUPPORTED_CHAINS[undefined] evaluates to undefined, and accessing .name on it will throw a TypeError at runtime with no helpful message.

Add a guard before accessing the chain config:

🐛 Proposed fix
   if (!provider) {
+    if (!chainId) throw new Error('Either provider or chainId must be provided');
     provider = utils.getProvider({ network: SUPPORTED_CHAINS[chainId].name });
   }
🤖 Prompt for AI Agents
In `@src/document-store/document-store-roles.ts` around lines 18 - 23, The code
can dereference SUPPORTED_CHAINS[chainId].name when both options.provider and
options.chainId are missing; in getRoleString, check for a valid provider first
and only access SUPPORTED_CHAINS when chainId is defined and points to a valid
entry. If chainId is missing or invalid, either use a safe default (e.g.,
DEFAULT_CHAIN_ID) or throw a clear error; then call utils.getProvider({ network:
SUPPORTED_CHAINS[chainId].name }) (or with the default) so you never read .name
on undefined. Ensure the guards involve the symbols options, chainId, provider,
SUPPORTED_CHAINS, and utils.getProvider to locate and fix the logic.

Comment thread src/document-store/index.ts Outdated
Comment thread src/document-store/revoke.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: 1

🤖 Fix all issues with AI agents
In `@src/__tests__/document-store/fixtures.ts`:
- Around line 71-72: The test fixtures list has grantRole and revokeRole with
reversed parameter order; update the fixture entries for the functions named
grantRole and revokeRole (in the fixtures array) so their format strings match
the actual ABI/OpenZeppelin signature: use "grantRole(bytes32 role, address
account)" and "revokeRole(bytes32 role, address account)" (i.e., bytes32 first,
address second) to mirror the definitions in tt-document-store-abi.ts and avoid
signature mismatches in tests.
🧹 Nitpick comments (3)
src/__tests__/document-store/fixtures.ts (3)

76-121: Constructors should not return values — use plain objects or vi.fn() instead.

Biome correctly flags that returning from a constructor overrides this, which is a confusing anti-pattern. Since these are mocks and don't need class semantics, replace the classes with plain object factories.

Proposed fix (example for DocumentStore__factory)
-  DocumentStore__factory: class {
-    static abi = ['constructor(string storeName, address owner)'];
-    static bytecode = '0x60006000';
-    constructor() {
-      return {
-        deploy: vi.fn(() => ({
-          waitForDeployment: vi.fn().mockResolvedValue(undefined),
-          target: '0xDeployedDocumentStoreAddress',
-          deploymentTransaction: vi.fn(() => ({ hash: 'deploy_tx_hash' })),
-          deployTransaction: {
-            wait: vi.fn().mockResolvedValue({}),
-            hash: 'deploy_tx_hash',
-          },
-          address: '0xDeployedDocumentStoreAddress',
-        })),
-      };
-    }
-  },
+  DocumentStore__factory: Object.assign(
+    vi.fn(() => ({
+      deploy: vi.fn(() => ({
+        waitForDeployment: vi.fn().mockResolvedValue(undefined),
+        target: '0xDeployedDocumentStoreAddress',
+        deploymentTransaction: vi.fn(() => ({ hash: 'deploy_tx_hash' })),
+        deployTransaction: {
+          wait: vi.fn().mockResolvedValue({}),
+          hash: 'deploy_tx_hash',
+        },
+        address: '0xDeployedDocumentStoreAddress',
+      })),
+    })),
+    {
+      abi: ['constructor(string storeName, address owner)'],
+      bytecode: '0x60006000',
+    },
+  ),

Apply the same pattern to TransferableDocumentStore__factory.


125-130: Shared callStaticFxn reference across all mock contracts risks test pollution.

All three mock contracts (mockDocumentStoreContract, mockTransferableDocumentStoreContract, mockTTDocumentStoreContract) share the same callStaticFxn object. If a test resets, reconfigures, or asserts call counts on one contract's callStatic, it silently affects the others.

Create separate instances per contract:

Proposed fix
-const callStaticFxn = {
-  issue: vi.fn(),
-  revoke: vi.fn(),
-  grantRole: vi.fn(),
-  revokeRole: vi.fn(),
-};
+const createCallStaticFxn = () => ({
+  issue: vi.fn(),
+  revoke: vi.fn(),
+  grantRole: vi.fn(),
+  revokeRole: vi.fn(),
+});

Then use callStatic: createCallStaticFxn() in each mock contract.


123-124: Same shared-reference concern applies to promiseResolveTrue / promiseResolveFalse.

isIssued, isRevoked, and isActive across all three contracts point to the same vi.fn() instances. Call counts and mock state will leak between contracts. Consider creating per-contract instances (e.g., via a factory function) for the same reason as callStaticFxn.

Also applies to: 165-167, 205-207, 245-247

Comment thread src/__tests__/document-store/fixtures.ts Outdated
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

🤖 Fix all issues with AI agents
In `@src/document-store/deploy.ts`:
- Around line 83-86: The call to contract.deployTransaction.wait() can return
undefined in ethers v5; add a null guard after awaiting wait() in the deploy
function (the section using ContractFactory.deploy and
contract.deployTransaction.wait) so that if receipt is undefined you handle it
(e.g., throw a descriptive error or retry) instead of returning undefined—ensure
you reference the local variable receipt and update callers if necessary to
expect an error rather than a possibly undefined receipt.
🧹 Nitpick comments (1)
src/document-store/deploy.ts (1)

42-50: Consider validating owner as a proper address.

Currently the check is only !owner, which would pass any non-empty string. A malformed address would still reach the contract deployment and fail with an obscure on-chain error. Consider using ethers.utils.isAddress(owner) (v5) or ethers.isAddress(owner) (v6) for an early, descriptive validation.

Comment on lines +83 to +86
const contract = await ContractFactory.deploy(storeName, owner, txOptions);
const receipt = await contract.deployTransaction.wait();

return receipt;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a null guard on the v5 wait() result.

In ethers v5, wait() can also return undefined if the transaction is dropped/replaced. For consistency and safety, guard the receipt here too.

Proposed fix
       const contract = await ContractFactory.deploy(storeName, owner, txOptions);
       const receipt = await contract.deployTransaction.wait();
-
+      if (!receipt) throw new Error('Deployment transaction receipt not found');
       return receipt;
📝 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
const contract = await ContractFactory.deploy(storeName, owner, txOptions);
const receipt = await contract.deployTransaction.wait();
return receipt;
const contract = await ContractFactory.deploy(storeName, owner, txOptions);
const receipt = await contract.deployTransaction.wait();
if (!receipt) throw new Error('Deployment transaction receipt not found');
return receipt;
🤖 Prompt for AI Agents
In `@src/document-store/deploy.ts` around lines 83 - 86, The call to
contract.deployTransaction.wait() can return undefined in ethers v5; add a null
guard after awaiting wait() in the deploy function (the section using
ContractFactory.deploy and contract.deployTransaction.wait) so that if receipt
is undefined you handle it (e.g., throw a descriptive error or retry) instead of
returning undefined—ensure you reference the local variable receipt and update
callers if necessary to expect an error rather than a possibly undefined
receipt.

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

🤖 Fix all issues with AI agents
In `@src/__tests__/document-store/issue.test.ts`:
- Around line 87-99: The beforeEach lacks re-mocking
getEthersContractFromProvider so new Contract(...) returns an empty mock and the
mocked methods on mockContract (e.g., mockContract.callStatic.issue and
mockContract.issue.staticCall) are never present; in the beforeEach (near the
existing vi.spyOn and mockContract mocks) restore/mock
getEthersContractFromProvider to return a constructor that yields mockContract
(so new Contract(...) returns mockContract) — use the same approach as the
"Contract Type Detection" setup to ensure documentStoreIssue calls the mocked
methods on mockContract.

In `@src/__tests__/document-store/revoke.test.ts`:
- Around line 196-213: The test for fallback to TT Document Store is mocking
getEthersContractFromProvider to return the standard mockContract instead of
mockTTDocumentStoreContract, so update the mock in the test for
documentStoreRevoke when coreModule.checkSupportsInterface resolves false to
return mockTTDocumentStoreContract (or ensure the mock returns a constructor
that creates mockTTDocumentStoreContract) and add an assertion that verifies
TT-specific behavior—e.g., that getEthersContractFromProvider was called with
TT_DOCUMENT_STORE_ABI or that the result matches the
mockTTDocumentStoreContract's expected value; reference checkSupportsInterface,
documentStoreRevoke, getEthersContractFromProvider, mockTTDocumentStoreContract,
and TT_DOCUMENT_STORE_ABI to locate and change the mocks and assertions.
🧹 Nitpick comments (12)
src/document-store/deploy.ts (2)

42-47: No validation on owner address format.

owner is only checked for truthiness. An invalid address (e.g., "hello") will pass validation but cause a revert on-chain during deployment. Consider validating it as a proper Ethereum address using ethers.utils.isAddress (v5) or ethers.isAddress (v6) to fail fast with a clear message.


88-93: Error wrapping discards the original stack trace.

throw new Error(...) with the message from e loses the original error's stack. Consider using cause to preserve it for debugging.

Proposed fix
     throw new Error(
       `Failed to deploy DocumentStore: ${e instanceof Error ? e.message : String(e)}`,
+      { cause: e },
     );
src/__tests__/document-store/fixtures.ts (1)

76-111: Constructors returning values are a code smell flagged by Biome.

The DocumentStore__factory and TransferableDocumentStore__factory mock classes have constructors that return an object, which overrides this. While this works for mocking, it's fragile and flagged by static analysis (lint/correctness/noConstructorReturn). Consider using plain factory functions or objects with a mock constructor instead.

Proposed refactor using factory functions
-  DocumentStore__factory: class {
-    static abi = ['constructor(string storeName, address owner)'];
-    static bytecode = '0x60006000';
-    constructor() {
-      return {
-        deploy: vi.fn(() => ({
-          ...
-        })),
-      };
-    }
-  },
+  DocumentStore__factory: {
+    abi: ['constructor(string storeName, address owner)'],
+    bytecode: '0x60006000',
+  },

Note: The actual ContractFactory is mocked at the ethers level (lines 10-48), so the @trustvc/document-store mock only needs to provide abi and bytecode as static properties—the constructor-based deploy mock is already handled by the ethers ContractFactory mock.

src/__tests__/document-store/deploy.test.ts (3)

19-37: Test relies on shape-based provider detection via any-typed objects.

The v6 deployment tests construct inline mock objects (const providerV6: any = {...}) that mimic the structural checks in isV6EthersProvider (having provider property, no _isProvider). This is fine for unit tests but note that the local providerV6 variable on line 20 shadows the imported providerV6 from fixtures (line 6), which could cause confusion during maintenance.

Consider renaming the local variable to something like mockProviderV6 to distinguish it from the fixture import.


39-61: Redundant vi.spyOn — the mock object already has getNetwork as a vi.fn().

Line 48 spies on providerV6.getNetwork, but providerV6 (the local mock on line 40) already has getNetwork: vi.fn().mockResolvedValue(...). The subsequent vi.spyOn replaces it with the same value, adding no coverage.


9-116: Tests only verify the happy-path receipt shape; no assertion on contract factory selection.

All four deployment tests assert the same receipt shape. There's no verification that DocumentStore__factory vs TransferableDocumentStore__factory was actually used based on the isTransferable flag. Consider adding assertions (e.g., spy on factory constructor calls) to confirm the correct factory is selected for each variant.

src/__tests__/document-store/issue.test.ts (1)

67-76: Wallet and spy setup in describe.each body runs during test registration — fragile pattern.

The wallet creation and vi.spyOn calls (lines 68–76) execute synchronously when describe.each registers the test suite, not inside a beforeEach. While vi.clearAllMocks() preserves spy implementations, this means:

  1. The same wallet instance is shared across all tests in the variant.
  2. Any test that mutates the wallet or its provider could affect subsequent tests.

Moving wallet creation into beforeEach would improve test isolation.

src/document-store/revoke.ts (3)

121-124: Error wrapping in callStatic catch discards the original error context.

The original error e is logged via console.error but not attached to the re-thrown error. Consumers won't have the root cause in the error chain. Preserve it with cause.

Proposed fix
   } catch (e) {
-    console.error('callStatic failed:', e);
-    throw new Error('Pre-check (callStatic) for revoke failed');
+    throw new Error('Pre-check (callStatic) for revoke failed', { cause: e });
   }

59-61: Implicit boolean coercion of isTransferable (which can be undefined) may be confusing.

let isDocumentStore = !isTransferable; evaluates to true when isTransferable is undefined, which happens to be correct here since the variables are overwritten in the detection block. But the intent isn't immediately clear — a reader might wonder if undefined was meant to default to "DocumentStore". A brief comment would help.


53-55: No format validation on documentStoreAddress or documentHash.

Like deploy.ts, both documentStoreAddress and documentHash are only checked for truthiness. Invalid formats (e.g., non-hex strings, wrong length) will pass validation and only fail on-chain. For documentHash, a basic regex check for 0x + 64 hex chars, and for documentStoreAddress, isAddress(), would give early, clear errors.

src/__tests__/document-store/revoke.test.ts (2)

82-88: Remove commented-out beforeAll block.

This dead code is left over from the previous review fix. It adds noise and should be cleaned up.

Proposed fix
-      // beforeAll(() => {
-      //   vi.clearAllMocks();
-      //   const mockContractConstructor = (mockContract: any) => vi.fn(() => mockContract);
-      //   vi.mocked(getEthersContractFromProvider).mockReturnValue(
-      //     mockContractConstructor(mockContract),
-      //   );
-      // });
-

67-76: Wallet and spy setup in describe body — same isolation concern as issue.test.ts.

Same pattern as the issue tests: wallet instances and spies are created during test registration. Consider moving to beforeEach for better test isolation across test cases in the same variant.

Comment thread src/__tests__/document-store/issue.test.ts
Comment thread src/__tests__/document-store/revoke.test.ts
@RishabhS7
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 11, 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.

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

🤖 Fix all issues with AI agents
In `@src/__tests__/document-store/issue.test.ts`:
- Around line 204-227: The test's fallback path is mocking the wrong contract
and using a weak assertion: update the mocked getEthersContractFromProvider to
return the TT ABI mock (use mockTTDocumentStoreContract instead of mockContract)
so documentStoreIssue exercises the TT Document Store fallback, then replace
expect(result).toBeDefined() with a strict equality check matching the expected
issued response (same shape/value used in the dedicated TT fallback tests), and
keep/verify coreModule.checkSupportsInterface call counts as before; ensure you
reference getEthersContractFromProvider, mockTTDocumentStoreContract,
documentStoreIssue, and coreModule.checkSupportsInterface when making the
changes.
🧹 Nitpick comments (2)
src/__tests__/document-store/issue.test.ts (2)

82-104: Fragile mock lifecycle: beforeAll setup + beforeEach clearAllMocks relies on subtle Vitest behavior.

vi.clearAllMocks() in beforeEach (line 93) clears call history but preserves the mockReturnValue set in beforeAll (line 87). This works, but the coupling is implicit and fragile — switching to vi.resetAllMocks() or enabling mockReset: true in Vitest config would silently break all tests in this block. Consider re-establishing the getEthersContractFromProvider mock inside beforeEach instead, matching the pattern used in the Contract Type Detection and TT Document Store (Fallback) blocks (lines 274-277, 374-377).

Suggested consolidation into beforeEach
-     beforeAll(() => {
-       // Clear any existing mocks first
-       vi.clearAllMocks();
-       const mockContractConstructor = (mockContract: any) => vi.fn(() => mockContract);
-       // Only set up the mock if it hasn't been set up yet
-       vi.mocked(getEthersContractFromProvider).mockReturnValue(
-         mockContractConstructor(mockContract),
-       );
-     });
-
      beforeEach(() => {
        vi.clearAllMocks();
+       const mockContractConstructor = (mc: any) => vi.fn(() => mc);
+       vi.mocked(getEthersContractFromProvider).mockReturnValue(
+         mockContractConstructor(mockContract),
+       );
        vi.spyOn(coreModule, 'checkSupportsInterface').mockImplementation(

67-76: Wallet and provider spy setup in describe body risks test pollution.

vi.spyOn(wallet, 'getChainId') (line 70) and vi.spyOn(Provider, 'getNetwork') (line 73) are executed during describe-body evaluation rather than inside a lifecycle hook. This means:

  1. The spy on providerV6 (a shared fixture) is never properly restored between parameterized runs, since clearAllMocks() preserves spy wiring.
  2. If a test mutates the wallet instance, state leaks to subsequent tests.

Moving wallet creation and spy setup into beforeEach would improve isolation and align with the pattern in the non-parameterized describe blocks.

Comment on lines +204 to +227
it('should fallback to TT Document Store when ERC-165 interfaces not supported', async () => {
// When checkSupportsInterface returns false, it should fallback to TT Document Store
vi.spyOn(coreModule, 'checkSupportsInterface').mockResolvedValue(false);

// Mock the contract to use TT Document Store
const mockContractConstructor = (mockContract: any) => vi.fn(() => mockContract);
vi.mocked(getEthersContractFromProvider).mockReturnValue(
mockContractConstructor(mockContract),
);

const result = await documentStoreIssue(
mockDocumentStoreAddress,

mockDocumentHash,
wallet,
{
chainId: mockChainId,
},
);

// Should successfully issue using TT Document Store as fallback
expect(result).toBeDefined();
expect(coreModule.checkSupportsInterface).toHaveBeenCalledTimes(2);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Weak assertion and wrong mock contract in fallback test.

This test verifies TT Document Store fallback but:

  1. Line 211: Re-mocks getEthersContractFromProvider to return mockContract (DocumentStore or TransferableDocumentStore), not mockTTDocumentStoreContract. The fallback path should use the TT ABI, but the test can't verify this because the mock contract doesn't change.
  2. Line 225: expect(result).toBeDefined() is a weak assertion compared to toEqual(...) used elsewhere. It passes for any truthy value.

The dedicated TT fallback block (lines 366–495) properly uses mockTTDocumentStoreContract and asserts exact values. Consider aligning this test or removing it in favor of the dedicated block.

🤖 Prompt for AI Agents
In `@src/__tests__/document-store/issue.test.ts` around lines 204 - 227, The
test's fallback path is mocking the wrong contract and using a weak assertion:
update the mocked getEthersContractFromProvider to return the TT ABI mock (use
mockTTDocumentStoreContract instead of mockContract) so documentStoreIssue
exercises the TT Document Store fallback, then replace
expect(result).toBeDefined() with a strict equality check matching the expected
issued response (same shape/value used in the dedicated TT fallback tests), and
keep/verify coreModule.checkSupportsInterface call counts as before; ensure you
reference getEthersContractFromProvider, mockTTDocumentStoreContract,
documentStoreIssue, and coreModule.checkSupportsInterface when making the
changes.

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

🤖 Fix all issues with AI agents
In `@src/document-store/revoke.ts`:
- Around line 112-124: The pre-check callStatic invocation for ethers v5 is
using bracket signature lookup which doesn't match the test mock; update the v5
branch to call (documentStoreContract as
ContractV5).callStatic.revoke(documentHash) instead of using
callStatic['revoke(bytes32)'], and in the catch block include the original error
when re-throwing (e.g., attach or wrap the caught error) so the thrown Error
preserves the underlying error details; refer to isV6EthersProvider,
documentStoreContract, ContractV5, and callStatic.revoke to locate the change.
🧹 Nitpick comments (1)
src/document-store/revoke.ts (1)

85-89: Unreachable dead code — the error at Line 86 can never be thrown.

When isTransferable is defined (true/false), one of isDocumentStore/isTransferableDocumentStore is always true (Lines 59-60). When isTransferable is undefined, Lines 80-82 set isTTDocumentStore = true as a fallback if both interface checks fail. Thus the condition on Line 85 is never satisfied.

Consider removing it or converting it to a debug assertion if you want to keep it as a safety net.

Comment on lines +112 to +124
// Check callStatic (dry run) to ensure transaction will succeed
try {
const isV6 = isV6EthersProvider(signer.provider);

if (isV6) {
await (documentStoreContract as ContractV6)['revoke(bytes32)'].staticCall(documentHash);
} else {
await (documentStoreContract as ContractV5).callStatic['revoke(bytes32)'](documentHash);
}
} catch (e) {
console.error('callStatic failed:', e);
throw new Error('Pre-check (callStatic) for revoke failed');
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find test files for revoke.ts and locate fixtures
fd "revoke" --type f -e "ts" -e "test" | head -20

Repository: TrustVC/trustvc

Length of output: 131


🏁 Script executed:

# Find issue.ts and related document-store test files
fd "issue\." --type f | grep -E "document-store|test"

Repository: TrustVC/trustvc

Length of output: 129


🏁 Script executed:

# Look for test fixtures and mock setups
find . -name "*fixture*" -o -name "*mock*" | grep -E "document-store|test" | head -20

Repository: TrustVC/trustvc

Length of output: 248


🏁 Script executed:

# Search for callStatic usage patterns in the codebase
rg "callStatic\[" --type ts -B 3 -A 3

Repository: TrustVC/trustvc

Length of output: 1120


🏁 Script executed:

# Read the revoke test file to see test structure and failures
wc -l src/__tests__/document-store/revoke.test.ts

Repository: TrustVC/trustvc

Length of output: 106


🏁 Script executed:

# Read the fixtures file to understand mock setup
wc -l src/__tests__/document-store/fixtures.ts

Repository: TrustVC/trustvc

Length of output: 103


🏁 Script executed:

# Read issue.ts to compare callStatic pattern
wc -l src/document-store/issue.ts

Repository: TrustVC/trustvc

Length of output: 90


🏁 Script executed:

# Read the revoke test file
cat -n src/__tests__/document-store/revoke.test.ts

Repository: TrustVC/trustvc

Length of output: 15885


🏁 Script executed:

# Read the fixtures file
cat -n src/__tests__/document-store/fixtures.ts

Repository: TrustVC/trustvc

Length of output: 10214


🏁 Script executed:

# Read issue.ts for comparison
cat -n src/document-store/issue.ts

Repository: TrustVC/trustvc

Length of output: 6016


🏁 Script executed:

# Verify exact v5 pattern by checking if issue and revoke have different signatures
rg "callStatic\.(revoke|issue)" src/document-store/ -B 2 -A 2

Repository: TrustVC/trustvc

Length of output: 1107


🏁 Script executed:

# Check if there are any overloads of revoke() function that would require signature notation
rg "revoke\(" src/document-store/revoke.ts -B 3 -A 3

Repository: TrustVC/trustvc

Length of output: 689


🏁 Script executed:

# Compare the actual transaction calls (non-callStatic) to understand the pattern
rg "\['revoke\(bytes32\)\'\]|\['issue\(bytes32\)\'\]" src/document-store/ -B 1 -A 1

Repository: TrustVC/trustvc

Length of output: 193


🏁 Script executed:

# Compare the actual transaction calls (non-callStatic) to understand the pattern
rg "revoke\(bytes32\)" src/document-store/ -B 1 -A 1

Repository: TrustVC/trustvc

Length of output: 636


🏁 Script executed:

# Check the DocumentStore ABI/interface to see if revoke has multiple overloads
rg "revoke" src/__tests__/document-store/fixtures.ts -B 2 -A 2

Repository: TrustVC/trustvc

Length of output: 1764


🏁 Script executed:

# Check if there's a pattern difference - does issue have overloads?
cat src/__tests__/document-store/fixtures.ts | grep -A 10 "documentStoreFxnFragments"

Repository: TrustVC/trustvc

Length of output: 1011


🏁 Script executed:

# Check if issue() function also uses signature notation anywhere
cat src/document-store/issue.ts | grep -E "\['issue|callStatic"

Repository: TrustVC/trustvc

Length of output: 382


🏁 Script executed:

# Verify the actual test execution - check if tests currently pass or fail
# Look for any CI/test status information
git log --oneline -20 -- "src/__tests__/document-store/revoke.test.ts"

Repository: TrustVC/trustvc

Length of output: 101


Fix ethers v5 callStatic invocation — revoke.ts uses inconsistent bracket notation.

The code at line 119 uses callStatic['revoke(bytes32)'] but should use callStatic.revoke() to match the actual mock setup and be consistent with issue.ts (line 119). There is only one revoke overload, so signature notation is not needed. The bracket notation with full signature 'revoke(bytes32)' creates a property lookup mismatch: the test fixtures mock .callStatic.revoke (simple name), not .callStatic['revoke(bytes32)'] (signature key).

Change line 119 from:

await (documentStoreContract as ContractV5).callStatic['revoke(bytes32)'](documentHash);

to:

await (documentStoreContract as ContractV5).callStatic.revoke(documentHash);

Additionally, preserve the original error when re-throwing at line 123:

  } catch (e) {
-   console.error('callStatic failed:', e);
-   throw new Error('Pre-check (callStatic) for revoke failed');
+   throw new Error(`Pre-check (callStatic) for revoke failed: ${e instanceof Error ? e.message : e}`);
  }
🧰 Tools
🪛 GitHub Actions: CI

[error] 123-123: Pre-check (callStatic) for revoke failed. documentStoreContract.callStatic.revoke(bytes32) is not a function.

🪛 GitHub Check: Tests / Run Tests (20.x)

[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document without chainId option
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:137:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document without chainId option
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:137:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document with explicit contract type
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:123:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document with explicit contract type
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:123:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document with explicit contract type
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:123:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document with explicit contract type
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:123:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document hash successfully
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:109:24


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document hash successfully
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:109:24


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document hash successfully
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:109:24


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document hash successfully
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:109:24

🪛 GitHub Check: Tests / Run Tests (22.x)

[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document without chainId option
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:137:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document without chainId option
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:137:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document with explicit contract type
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:123:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document with explicit contract type
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:123:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document with explicit contract type
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:123:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document with explicit contract type
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:123:30


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document hash successfully
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:109:24


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document hash successfully
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:109:24


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document hash successfully
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:109:24


[failure] 123-123: src/tests/document-store/revoke.test.ts > Revoke Document > Revoke Document with 'DocumentStore' and ethers version 'v5' > should revoke document hash successfully
Error: Pre-check (callStatic) for revoke failed
❯ Module.documentStoreRevoke src/document-store/revoke.ts:123:11
❯ src/tests/document-store/revoke.test.ts:109:24

🤖 Prompt for AI Agents
In `@src/document-store/revoke.ts` around lines 112 - 124, The pre-check
callStatic invocation for ethers v5 is using bracket signature lookup which
doesn't match the test mock; update the v5 branch to call (documentStoreContract
as ContractV5).callStatic.revoke(documentHash) instead of using
callStatic['revoke(bytes32)'], and in the catch block include the original error
when re-throwing (e.g., attach or wrap the caught error) so the thrown Error
preserves the underlying error details; refer to isV6EthersProvider,
documentStoreContract, ContractV5, and callStatic.revoke to locate the change.

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

🤖 Fix all issues with AI agents
In `@src/__tests__/document-store/issue.test.ts`:
- Around line 175-183: The test 'should throw when provider is missing' always
constructs a v5 wallet (new WalletV5(...)) so it doesn't cover the v6 path;
update the test to branch on ethersVersion (or the test parameter) and create
the providerless signer appropriately (use WalletV5 for ethersVersion === 'v5'
and the v6 Wallet constructor/class for ethersVersion === 'v6'), then call
documentStoreIssue(mockDocumentStoreAddress, mockDocumentHash,
signerWithoutProvider, { chainId: mockChainId }) and assert it rejects with
'Provider is required'.
🧹 Nitpick comments (2)
src/__tests__/document-store/issue.test.ts (2)

67-76: Wallet creation and spy setup outside lifecycle hooks is fragile.

Placing wallet initialization and vi.spyOn calls in the describe body (rather than in beforeAll or beforeEach) works because vi.clearAllMocks() preserves implementations, but this pattern is atypical and can silently break if someone switches to vi.resetAllMocks() or vi.restoreAllMocks() later. Consider moving this setup into beforeAll alongside the existing mock setup at lines 82–90.


246-256: Test relies on execution order for mock state — consider re-establishing the mock explicitly.

Unlike the sibling test at line 229, this test does not re-set getEthersContractFromProvider. It works only because test 229 (which runs first) restores the mock after the fallback test at line 204 overrides it. Adding the mock setup here would make this test self-contained and resilient to reordering.

Proposed fix
       it('should handle already issued document', async () => {
+        const mockContractConstructor = (mockContract: any) => vi.fn(() => mockContract);
+        vi.mocked(getEthersContractFromProvider).mockReturnValue(
+          mockContractConstructor(mockContract),
+        );
         mockContract.callStatic.issue.mockRejectedValue(new Error('Document already issued'));

Comment on lines +175 to +183
it('should throw when provider is missing', async () => {
const signerWithoutProvider = new WalletV5('0x'.padEnd(66, '1'));

await expect(
documentStoreIssue(mockDocumentStoreAddress, mockDocumentHash, signerWithoutProvider, {
chainId: mockChainId,
}),
).rejects.toThrow('Provider is required');
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

"Missing provider" test always uses a v5 wallet, even in v6-parameterized runs.

new WalletV5('0x'.padEnd(66, '1')) is hard-coded regardless of ethersVersion. For v6 iterations this doesn't exercise the v6 signer-without-provider path. Consider branching on ethersVersion to create the appropriate providerless wallet.

Proposed fix
       it('should throw when provider is missing', async () => {
-        const signerWithoutProvider = new WalletV5('0x'.padEnd(66, '1'));
+        const signerWithoutProvider = ethersVersion === 'v5'
+          ? new WalletV5('0x'.padEnd(66, '1'))
+          : new WalletV6('0x'.padEnd(66, '1'));

         await expect(
           documentStoreIssue(mockDocumentStoreAddress, mockDocumentHash, signerWithoutProvider, {
🤖 Prompt for AI Agents
In `@src/__tests__/document-store/issue.test.ts` around lines 175 - 183, The test
'should throw when provider is missing' always constructs a v5 wallet (new
WalletV5(...)) so it doesn't cover the v6 path; update the test to branch on
ethersVersion (or the test parameter) and create the providerless signer
appropriately (use WalletV5 for ethersVersion === 'v5' and the v6 Wallet
constructor/class for ethersVersion === 'v6'), then call
documentStoreIssue(mockDocumentStoreAddress, mockDocumentHash,
signerWithoutProvider, { chainId: mockChainId }) and assert it rejects with
'Provider is required'.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

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

See analysis details on SonarQube Cloud

@rongquan1 rongquan1 merged commit ba89e9c into main Feb 12, 2026
11 of 12 checks passed
@rongquan1 rongquan1 deleted the feat/add-document-store-functions branch February 12, 2026 09:03
nghaninn pushed a commit that referenced this pull request Feb 12, 2026
## [2.7.0](v2.6.1...v2.7.0) (2026-02-12)

### Features

* deploy issue revoke functions ([#128](#128)) ([ba89e9c](ba89e9c))
@tradetrustimda
Copy link
Copy Markdown

🎉 This PR is included in version 2.7.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