Skip to content

feat: create nano contract tx without signing#1023

Merged
raul-oliveira merged 14 commits intomasterfrom
feat/build-tx-without-sign-and-lock
Mar 5, 2026
Merged

feat: create nano contract tx without signing#1023
raul-oliveira merged 14 commits intomasterfrom
feat/build-tx-without-sign-and-lock

Conversation

@raul-oliveira
Copy link
Copy Markdown
Contributor

@raul-oliveira raul-oliveira commented Feb 27, 2026

Motivation

Rpc-lib handler needs to provide the fee header to the wallet-mobile, this will show the total fee amount to the user before accepting the tx. To avoid building the tx twice, for rpc-prompt and rpc-request, we'll do the following:

  • Build the tx in the prompt
  • Grab the fee header
  • Request for user confirmation
  • sign tx
  • push tx

Acceptance Criteria

  • should be able to create a nano contract tx without signing
  • integration test to cover the proposed scenario

Security Checklist

  • Make sure you do not include new dependencies in the project unless strictly necessary and do not include dev-dependencies as production ones. More dependencies increase the possibility of one of them being hijacked and affecting us.

Summary by CodeRabbit

  • New Features

    • Per-call signing control for nano contract transactions (option to create unsigned transactions, edit caller, then sign).
    • PIN required only when signing is requested; non-signing flows skip PIN.
  • Behavior

    • Sending flow continues to sign by default when using the send-and-mine path.
  • Tests

    • Integration test validating unsigned build → caller edit → signing → send workflow.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 27, 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

Adds an optional signTx?: boolean to CreateNanoTxOptions and wiring to allow building unsigned nano-contract transactions; signing and PIN validation are now conditional on that flag. Changes touch types, wallet flow (build/prepare/sign), and add an integration test covering unsigned→edit→sign→send.

Changes

Cohort / File(s) Summary
Type Definitions
src/nano_contracts/types.ts, src/wallet/types.ts
Add signTx?: boolean to CreateNanoTxOptions; update IHathorWallet.createNanoContractTransaction to accept the typed options.
Wallet Implementation
src/wallet/wallet.ts, src/new/wallet.ts
Introduce per-call signTx handling (default true); PIN required only when signing; propagate signTx into prepareNanoSendTransactionWalletService; tighten builder typing.
Integration Tests
__tests__/integration/hathorwallet_prepare_without_sign.test.ts
New integration test that builds an unsigned nano tx (signTx:false), edits caller, then signs and sends — validates headers, inputs/outputs, fee, and inclusion.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Wallet
    participant Builder as NanoContractTransactionBuilder
    participant Signer
    participant Blockchain

    rect rgba(100, 150, 200, 0.5)
    Note over Client,Wallet: Build unsigned (signTx: false)
    Client->>Wallet: createNanoContractTransaction(signTx: false)
    Wallet->>Builder: build()
    Builder-->>Wallet: Transaction (unsigned)
    Wallet-->>Client: SendTransaction (unsigned, no PIN)
    end

    rect rgba(150, 100, 200, 0.5)
    Note over Client,Signer: Edit caller and sign
    Client->>Client: Edit nano header (caller, seqnum)
    Client->>Wallet: request signing (provide PIN)
    Wallet->>Signer: prepareToSend/sign()
    Signer-->>Wallet: Signed transaction
    Wallet-->>Client: Prepared SendTransaction (signed)
    end

    rect rgba(100, 200, 150, 0.5)
    Note over Client,Blockchain: Submit
    Client->>Blockchain: send signed transaction
    Blockchain-->>Client: confirmation / inclusion
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • r4mmer
  • tuliomir
  • pedroferreira1

Poem

🐇 I built a tx both meek and bold,
Left signing off to watch it fold,
Then hopped to edit caller fine,
Poked a PIN — it signed in time,
Off it raced to chain and gold. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main feature: enabling creation of nano contract transactions without signing, which is the primary objective of this changeset.
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 unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/build-tx-without-sign-and-lock

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

Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
src/wallet/wallet.ts (1)

2926-2927: Align pinCode typing with deferred-sign runtime behavior.

Line 2926 requires pinCode: string, but Line 2849 passes an optional value (pin!) that is intentionally absent when signTx is false. Making this parameter nullable/optional removes the non-null assertion and keeps the contract accurate.

Proposed type-safe adjustment
-  async prepareNanoSendTransactionWalletService(
+  async prepareNanoSendTransactionWalletService(
     tx: Transaction,
     address: string,
-    pinCode: string,
+    pinCode?: string | null,
     options: { signTx?: boolean } = { signTx: true }
   ): Promise<SendTransactionWalletService> {
@@
-    const sendTransaction = new SendTransactionWalletService(this, {
-      transaction: tx,
-      pin: pinCode,
-    });
+    const sendTransaction = new SendTransactionWalletService(this, {
+      transaction: tx,
+      pin: pinCode ?? undefined,
+    });

Also applies to: 2849-2851

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

In `@src/wallet/wallet.ts` around lines 2926 - 2927, The function currently
declares pinCode: string but callers sometimes pass pin! only when
options.signTx is true; change the parameter to be optional or nullable
(pinCode?: string or pinCode: string | undefined) and update the function body
(the code paths in the function that reference pinCode) to only require a
non-null pin when options.signTx is true (throw or assert if signTx true and
pinCode is missing); also update any internal usages to guard against undefined
and remove the non-null assertion at the caller site (the call around lines
2849–2851) so the runtime matches the deferred-sign behavior.
🤖 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/wallet/wallet.ts`:
- Around line 2849-2851: The code is calling tx.prepareToSend() and passing a
non-null asserted pin (pin!) even for deferred-sign flows (signTx === false),
which finalizes the tx too early and violates the helper's non-null pin
contract; update the logic around prepareNanoSendTransactionWalletService and
tx.prepareToSend(): only call tx.prepareToSend() after signatures are injected
(i.e., when signTx is true or after external signing completes) and remove the
unconditional prepareToSend() from the deferred path (also apply the same change
in src/new/wallet.ts where tx.prepareToSend() is called at line ~3101); fix the
pin contract by changing the call to prepareNanoSendTransactionWalletService to
pass a nullable pin (pass pin or undefined/null) or update
prepareNanoSendTransactionWalletService's signature to accept pin?: string |
null so you don't use pin! when signTx is false. Ensure all branches that
perform external signing re-finalize the transaction after signature injection.

---

Nitpick comments:
In `@src/wallet/wallet.ts`:
- Around line 2926-2927: The function currently declares pinCode: string but
callers sometimes pass pin! only when options.signTx is true; change the
parameter to be optional or nullable (pinCode?: string or pinCode: string |
undefined) and update the function body (the code paths in the function that
reference pinCode) to only require a non-null pin when options.signTx is true
(throw or assert if signTx true and pinCode is missing); also update any
internal usages to guard against undefined and remove the non-null assertion at
the caller site (the call around lines 2849–2851) so the runtime matches the
deferred-sign behavior.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65a73b4 and 8d5f434.

📒 Files selected for processing (5)
  • __tests__/integration/template/transaction/fee.test.ts
  • src/nano_contracts/types.ts
  • src/new/wallet.ts
  • src/wallet/types.ts
  • src/wallet/wallet.ts

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 27, 2026

Codecov Report

❌ Patch coverage is 90.74074% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.82%. Comparing base (8e6bf10) to head (2cd5f05).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/wallet/wallet.ts 83.33% 4 Missing ⚠️
src/new/wallet.ts 95.65% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           master    #1023       +/-   ##
===========================================
+ Coverage   66.68%   87.82%   +21.13%     
===========================================
  Files         114      114               
  Lines        8791     8818       +27     
  Branches     1994     2004       +10     
===========================================
+ Hits         5862     7744     +1882     
+ Misses       2901     1046     -1855     
  Partials       28       28               

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@r4mmer

This comment was marked as spam.

r4mmer
r4mmer previously approved these changes Feb 27, 2026
@raul-oliveira raul-oliveira moved this from Todo to In Review (WIP) in Hathor Network Mar 2, 2026
@raul-oliveira raul-oliveira force-pushed the feat/build-tx-without-sign-and-lock branch from a9b8260 to e5dc76f Compare March 3, 2026 16:42
Copy link
Copy Markdown

@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 (1)
src/wallet/wallet.ts (1)

2849-2851: ⚠️ Potential issue | 🟠 Major

Type contract mismatch: pin! may be null when signTx is false.

When signTx is false, the PIN validation is skipped (lines 2808-2810), so pin can be null. However, prepareNanoSendTransactionWalletService declares pinCode: string (non-nullable at line 2926). The non-null assertion pin! masks a runtime type violation.

Either update prepareNanoSendTransactionWalletService to accept pinCode: string | null, or pass a safe fallback value when signing is deferred.

🔧 Suggested fix: Update helper signature to accept nullable pin

At line 2926:

   async prepareNanoSendTransactionWalletService(
     tx: Transaction,
     address: string,
-    pinCode: string,
+    pinCode: string | null,
     options: { signTx?: boolean } = { signTx: true }
   ): Promise<SendTransactionWalletService> {

Then at line 2942, guard before using:

     if (options.signTx) {
+      if (!pinCode) {
+        throw new PinRequiredError('Pin is required for signing.');
+      }
       await transaction.signTransaction(tx, storageProxy.createProxy(), pinCode);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wallet/wallet.ts` around lines 2849 - 2851, The call currently passes
pin! into prepareNanoSendTransactionWalletService but pin may be null when
newOptions.signTx is false; update prepareNanoSendTransactionWalletService's
signature to accept pinCode: string | null (or undefined) and then guard uses of
pinCode inside prepareNanoSendTransactionWalletService (e.g., where signing or
PIN validation occurs) to only access it when non-null, or alternatively pass a
safe fallback when calling from the caller that knows signTx is false; reference
prepareNanoSendTransactionWalletService, the pin variable at the caller, and
newOptions.signTx to implement the chosen fix.
🧹 Nitpick comments (1)
src/wallet/wallet.ts (1)

2807-2810: Consider using signTx !== false pattern for consistency.

The current check newOptions.signTx === true differs from the pattern used in src/new/wallet.ts. Using !== false guards against invalid inputs like null or 0 and maintains consistency across wallet implementations.

♻️ Suggested change
-    if (newOptions.signTx === true && !pin) {
+    if (newOptions.signTx !== false && !pin) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wallet/wallet.ts` around lines 2807 - 2810, Update the conditional that
gates requiring a PIN in wallet.ts to use the same pattern as other
implementations: replace the check `newOptions.signTx === true` with
`newOptions.signTx !== false` so the PIN is required for any truthy or
unspecified values and only skipped when explicitly false; update the `if` that
throws `PinRequiredError('Pin is required.')` accordingly (target the block
referencing newOptions.signTx and PinRequiredError).
🤖 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/wallet/wallet.ts`:
- Around line 2864-2873: The IHathorWallet interface's
createAndSendNanoContractTransaction signature doesn't match the implementation
in createAndSendNanoContractTransaction (method: string, address: string, data:
CreateNanoTxData, options: Omit<CreateNanoTxOptions, 'signTx'>):
Promise<Transaction>; — update the IHathorWallet declaration to use the same
optional options type (options?: Omit<CreateNanoTxOptions, 'signTx'>) and return
type Promise<Transaction>, and ensure relevant types (CreateNanoTxOptions,
CreateNanoTxData, Transaction) are imported/available in the interface file so
the signature matches the implementation.

---

Duplicate comments:
In `@src/wallet/wallet.ts`:
- Around line 2849-2851: The call currently passes pin! into
prepareNanoSendTransactionWalletService but pin may be null when
newOptions.signTx is false; update prepareNanoSendTransactionWalletService's
signature to accept pinCode: string | null (or undefined) and then guard uses of
pinCode inside prepareNanoSendTransactionWalletService (e.g., where signing or
PIN validation occurs) to only access it when non-null, or alternatively pass a
safe fallback when calling from the caller that knows signTx is false; reference
prepareNanoSendTransactionWalletService, the pin variable at the caller, and
newOptions.signTx to implement the chosen fix.

---

Nitpick comments:
In `@src/wallet/wallet.ts`:
- Around line 2807-2810: Update the conditional that gates requiring a PIN in
wallet.ts to use the same pattern as other implementations: replace the check
`newOptions.signTx === true` with `newOptions.signTx !== false` so the PIN is
required for any truthy or unspecified values and only skipped when explicitly
false; update the `if` that throws `PinRequiredError('Pin is required.')`
accordingly (target the block referencing newOptions.signTx and
PinRequiredError).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9b8260 and e5dc76f.

📒 Files selected for processing (2)
  • src/new/wallet.ts
  • src/wallet/wallet.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/new/wallet.ts

Comment on lines 2864 to +2873
async createAndSendNanoContractTransaction(
method: string,
address: string,
data: CreateNanoTxData,
options: CreateNanoTxOptions = {}
options: Omit<CreateNanoTxOptions, 'signTx'> = {}
): Promise<Transaction> {
const sendTransaction = await this.createNanoContractTransaction(
method,
address,
data,
options
);
const sendTransaction = await this.createNanoContractTransaction(method, address, data, {
...options,
signTx: true,
});
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 3, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n --type=ts -A5 'createAndSendNanoContractTransaction' src/wallet/types.ts

Repository: HathorNetwork/hathor-wallet-lib

Length of output: 269


Interface mismatch with IHathorWallet.createAndSendNanoContractTransaction.

The implementation declares options: Omit<CreateNanoTxOptions, 'signTx'> but the IHathorWallet interface in src/wallet/types.ts (line 432) declares options?: { pinCode?: string }. Update the interface to match:

createAndSendNanoContractTransaction(
  method: string,
  address: string,
  data: CreateNanoTxData,
  options?: Omit<CreateNanoTxOptions, 'signTx'>
): Promise<Transaction>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wallet/wallet.ts` around lines 2864 - 2873, The IHathorWallet interface's
createAndSendNanoContractTransaction signature doesn't match the implementation
in createAndSendNanoContractTransaction (method: string, address: string, data:
CreateNanoTxData, options: Omit<CreateNanoTxOptions, 'signTx'>):
Promise<Transaction>; — update the IHathorWallet declaration to use the same
optional options type (options?: Omit<CreateNanoTxOptions, 'signTx'>) and return
type Promise<Transaction>, and ensure relevant types (CreateNanoTxOptions,
CreateNanoTxData, Transaction) are imported/available in the interface file so
the signature matches the implementation.

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.

@raul-oliveira : this is important, and is being tracked by the breaking changes described in HathorNetwork/rfcs#100

I think it doesn't demand any action here , but it's good to keep in mind anyway.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown

@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 (1)
src/wallet/wallet.ts (1)

2844-2846: ⚠️ Potential issue | 🟠 Major

Remove the unsafe pin! contract in deferred-sign flows.

Line 2808 allows unsigned creation without a PIN, but Line 2844 still forces pin! into a method that requires string at Line 2921. This masks a nullable path and weakens the runtime/type contract.

🔧 Proposed fix
-    return this.prepareNanoSendTransactionWalletService(tx, address, pin!, {
+    return this.prepareNanoSendTransactionWalletService(tx, address, pin, {
       signTx: newOptions.signTx,
     });
   }

   async prepareNanoSendTransactionWalletService(
     tx: Transaction,
     address: string,
-    pinCode: string,
+    pinCode: string | null,
     options: { signTx?: boolean } = { signTx: true }
   ): Promise<SendTransactionWalletService> {
@@
-    if (options.signTx) {
+    if (options.signTx) {
+      if (!pinCode) {
+        throw new PinRequiredError('Pin is required.');
+      }
       await transaction.signTransaction(tx, storageProxy.createProxy(), pinCode);
       // Finalize the transaction
       tx.prepareToSend();
     }

     const sendTransaction = new SendTransactionWalletService(this, {
       transaction: tx,
-      pin: pinCode,
+      pin: pinCode ?? undefined,
     });
#!/bin/bash
set -euo pipefail

echo "== Deferred-sign PIN flow and non-null assertions =="
rg -n --type=ts -C3 'signTx|prepareNanoSendTransactionWalletService|pin!' src/wallet/wallet.ts src/new/wallet.ts

echo "== SendTransactionWalletService PIN typing/usage =="
rg -n --type=ts -C4 'class SendTransactionWalletService|constructor\\(|pin\\??:' src/wallet/sendTransactionWalletService.ts

Also applies to: 2919-2923

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

In `@src/wallet/wallet.ts` around lines 2844 - 2846, The call to
prepareNanoSendTransactionWalletService at
prepareNanoSendTransactionWalletService(tx, address, pin!, { signTx:
newOptions.signTx }) unsafely force-unwraps pin; remove the non-null assertion
and thread the nullable PIN correctly through the deferred-sign flow: change the
call to pass pin (nullable) and update prepareNanoSendTransactionWalletService /
SendTransactionWalletService constructors and the method that requires a string
PIN (referenced around the pin usage at lines ~2921 and the class
SendTransactionWalletService) to accept pin?: string and handle the
unsigned/signTx case (throw or skip signing) so no nullable path is masked by a
non-null assertion.
🧹 Nitpick comments (1)
__tests__/integration/hathorwallet_prepare_without_sign.test.ts (1)

75-79: Avoid stopping the same wallet twice in teardown.

generateWalletHelper tracks created wallets, and stopAllWallets() already stops them. Calling hWallet.stop() first can create noisy double-stop behavior.

♻️ Suggested cleanup
   afterAll(async () => {
-    await hWallet.stop();
     await stopAllWallets();
     await GenesisWalletHelper.clearListeners();
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/integration/hathorwallet_prepare_without_sign.test.ts` around lines
75 - 79, The teardown calls both hWallet.stop() and stopAllWallets(), causing
duplicate stops for wallets tracked by generateWalletHelper; remove the explicit
await hWallet.stop() from the afterAll block (leave await stopAllWallets() and
await GenesisWalletHelper.clearListeners()) so wallets are only stopped once, or
alternatively register hWallet with generateWalletHelper instead of calling
hWallet.stop() directly; update the afterAll in
__tests__/integration/hathorwallet_prepare_without_sign.test.ts accordingly to
reference hWallet.stop and stopAllWallets.
🤖 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/wallet/wallet.ts`:
- Around line 2844-2846: The call to prepareNanoSendTransactionWalletService at
prepareNanoSendTransactionWalletService(tx, address, pin!, { signTx:
newOptions.signTx }) unsafely force-unwraps pin; remove the non-null assertion
and thread the nullable PIN correctly through the deferred-sign flow: change the
call to pass pin (nullable) and update prepareNanoSendTransactionWalletService /
SendTransactionWalletService constructors and the method that requires a string
PIN (referenced around the pin usage at lines ~2921 and the class
SendTransactionWalletService) to accept pin?: string and handle the
unsigned/signTx case (throw or skip signing) so no nullable path is masked by a
non-null assertion.

---

Nitpick comments:
In `@__tests__/integration/hathorwallet_prepare_without_sign.test.ts`:
- Around line 75-79: The teardown calls both hWallet.stop() and
stopAllWallets(), causing duplicate stops for wallets tracked by
generateWalletHelper; remove the explicit await hWallet.stop() from the afterAll
block (leave await stopAllWallets() and await
GenesisWalletHelper.clearListeners()) so wallets are only stopped once, or
alternatively register hWallet with generateWalletHelper instead of calling
hWallet.stop() directly; update the afterAll in
__tests__/integration/hathorwallet_prepare_without_sign.test.ts accordingly to
reference hWallet.stop and stopAllWallets.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5dc76f and 8a90ab3.

📒 Files selected for processing (3)
  • __tests__/integration/hathorwallet_prepare_without_sign.test.ts
  • src/new/wallet.ts
  • src/wallet/wallet.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/new/wallet.ts

Copy link
Copy Markdown

@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

🤖 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/new/wallet.ts`:
- Around line 3107-3114: createAndSendNanoContractTransaction must not accept
signTx: false because createNanoContractTransaction can return an unsigned
SendTransaction while createAndSendNanoContractTransaction always calls
runFromMining(); update createAndSendNanoContractTransaction to validate
newOptions.signTx and either reject/throw when signTx === false or coerce it to
true before calling createNanoContractTransaction so the returned object is
always signed, and add a clear error message referencing
createAndSendNanoContractTransaction, createNanoContractTransaction,
runFromMining, and prepareNanoSendTransaction/SendTransaction to make the
contract explicit.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a90ab3 and 431f772.

📒 Files selected for processing (2)
  • src/new/wallet.ts
  • src/wallet/types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/wallet/types.ts

pedroferreira1
pedroferreira1 previously approved these changes Mar 3, 2026
)
).rejects.toThrow(/exceeds maximum fee/);
});
it('should create a fee token and deposit htr into the contract', async () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This test is not related with the template test file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll open another pr to address it. we have some tests that aren't using the template but are related to fees.

@raul-oliveira raul-oliveira force-pushed the feat/build-tx-without-sign-and-lock branch 5 times, most recently from ce94a20 to c0001db Compare March 5, 2026 13:00
@raul-oliveira raul-oliveira force-pushed the feat/build-tx-without-sign-and-lock branch from c0001db to 6044a14 Compare March 5, 2026 13:49
@raul-oliveira raul-oliveira requested a review from tuliomir March 5, 2026 13:52
tuliomir
tuliomir previously approved these changes Mar 5, 2026
return addressInfo.seqnum + 1;
}

async setNanoHeaderCaller(nanoHeader: NanoContractHeader, address: string): Promise<void> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Missing docstring

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done 2cd5f05

* @param tx - The transaction to be signed
* @param options - Options for signing
* @param options.pinCode - PIN to decrypt the private key. Optional but required if not set in this
* @param pinCode - PIN to decrypt the private key.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why? The pinCode is inside the options parameter

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done 2cd5f05

Comment on lines +1620 to +1630
async setNanoHeaderCaller(nanoHeader: NanoContractHeader, address: string): Promise<void> {
const newAddress = new Address(address, { network: this.network });

newAddress.validateAddress();

const newCallerSeqnum = await this.getNanoHeaderSeqnum(address);
// eslint-disable-next-line no-param-reassign
nanoHeader.address = newAddress;
// eslint-disable-next-line no-param-reassign
nanoHeader.seqnum = newCallerSeqnum;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Both methods are exactly the same. We should create a util method with this that just receives the nanoHeader, address, wallet. That way we can remove the duplicated code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done 2cd5f05

Comment on lines +3081 to +3087
const storageProxy = new WalletServiceStorageProxy(this, this.storage);
const signedTx = await transaction.signTransaction(
tx,
storageProxy.createProxy(),
options.pinCode
);
signedTx.prepareToSend();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This part was inspired by the prepareNanoSendTransactionWalletService method of this same file. I think we could go to this method and just call this.signTx, removing the duplicated code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done 2cd5f05

@tuliomir tuliomir moved this from In Review (WIP) to In Review (Done) in Hathor Network Mar 5, 2026
@raul-oliveira raul-oliveira merged commit a1fe6fc into master Mar 5, 2026
6 checks passed
@github-project-automation github-project-automation bot moved this from In Review (Done) to Waiting to be deployed in Hathor Network Mar 5, 2026
@tuliomir tuliomir linked an issue Mar 6, 2026 that may be closed by this pull request
@raul-oliveira raul-oliveira moved this from Waiting to be deployed to Done in Hathor Network Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

fix: guard createAndSendNanoContractTransaction against signTx: false

4 participants