Skip to content

Conversation

ByteZhang1024
Copy link
Contributor

@ByteZhang1024 ByteZhang1024 commented Sep 17, 2025

OneKeyHQ/cross-inpage-provider#406

Summary by CodeRabbit

  • New Features

    • TON wallet v4 (v4R2) transfer support and improved external-message construction.
    • Manifest-based TON dApp connection with origin/manifest validation.
    • Balance precheck before sending to prevent failed transactions.
  • Improvements

    • Stronger TON transaction validation and clearer error messages.
    • Updated TON core integration: serialization/API shape changes and added per-level cell representation support.
  • Chores

    • Added mobile dependency react-native-fast-pbkdf2 and Android PBKDF2 build property.
    • Bumped several internal package versions to 2.2.43.

Copy link
Contributor

coderabbitai bot commented Sep 17, 2025

Walkthrough

Migrates TON usage from TonWeb to @ton/core, adds wallet v4R2 transfer builders and Cell.repr support, updates keyrings and vault serialization to the new toBoc API/shape, adds manifest-based TON connect and precheck flows, introduces TON deps and @ton/core patch, and adds mobile pbkdf2 build setting and dependency.

Changes

Cohort / File(s) Summary
Mobile config & deps
apps/mobile/android/gradle.properties, apps/mobile/package.json
Adds Pbkdf2_compileSdkVersion=android-31 and dependency react-native-fast-pbkdf2@^0.3.1. (AsyncStorage_db_size_in_MB re-added, no functional change.)
Root package bumps
package.json
Bumps six @onekeyfe/* packages from 2.2.422.2.43.
Core TON deps
packages/core/package.json
Adds @ton/core@^0.61.0, @ton/crypto@^3.3.0, @ton/ton@^15.3.1.
Core chain update
packages/core/src/chains/ton/CoreChainSoftware.ts
Replace TonWeb decoding with Cell.fromHex; adjust imports and switch to synchronous hash/serialization usage.
New wallet v4 transfer builder
packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts
New module: IMaybe, IWalletV4BasicSendArgs, packSignatureToFront, and createWalletTransferV4 to assemble v4 signing messages and transfers (max 4 messages, signingMessage layout).
Provider TON: connect & tx checks
packages/kit-bg/src/providers/ProviderApiTon.ts
Adds manifest-based connect flow/types; fetches/validates manifest host; adds vault precheck (Confirm), stricter network/address validation, payload/stateInit validation, improved errors and logs.
Keyrings → @ton/core
packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts, .../KeyringHd.ts, .../KeyringImported.ts
Replace TonWeb cell ops with @ton/core Cell; change toBoc(false)toBoc({ idx: false }); remove awaits for toBoc and adapt repr/hash usage.
Vault TON serialization
packages/kit-bg/src/vaults/impls/ton/Vault.ts
Use signingMessage.toBoc({ idx: false }); switch to init_code/init_data sources; update expired-tx error text and toBoc option usage.
TON sdkTon utils (v4R2)
packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts
Add v4R2 support: createWalletTransferV4, toStateInit, getTonSendMode; expand IWallet API and several exported interfaces; update external message creation and return shapes.
@ton/core patch & typings
patches/@ton+core+0.61.0.patch, node_modules/@ton/core/...
Adds Cell.repr(level?: number) and threads reprs through wonderCalculator; updates source, dist builds, and typings to expose reprs and repr() API.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant DApp
  participant Provider as ProviderApiTon
  participant Manifest as Manifest Host
  participant Vault as VaultTon
  participant Account as Account Store

  rect rgb(230,245,255)
  Note over DApp,Provider: Connect flow with manifest validation
  DApp->>Provider: connect({ manifestUrl, items })
  Provider->>Manifest: GET manifest.json
  Manifest-->>Provider: { name, url }
  Provider->>Provider: Validate host matches origin
  Provider->>Account: Resolve current account
  Account-->>Provider: Account info
  Provider-->>DApp: Connected account response
  end
Loading
sequenceDiagram
  autonumber
  participant DApp
  participant Provider as ProviderApiTon
  participant Vault as VaultTon
  participant Keyring as Keyring(HW/HD/Imported)
  participant TonCore as @ton/core

  rect rgb(240,255,240)
  Note over DApp,Provider: Send transaction with prechecks and new TON flows
  DApp->>Provider: sendTransaction(encodedTx)
  Provider->>Provider: Validate network/mainnet
  Provider->>Provider: Validate payload/stateInit (Cell.fromHex/base64)
  Provider->>Vault: precheckUnsignedTx(timing=Confirm)
  Vault-->>Provider: ok / not enough funds
  alt ok
    Provider->>Vault: serializeUnsignedTransaction (may use v4R2)
    Vault->>TonCore: createWalletTransferV4 / toStateInit / packSignatureToFront
    Vault-->>Provider: signingMessage, init_code/init_data
    Provider->>Keyring: signTransaction(params)
    Keyring->>TonCore: Cell.toBoc({ idx:false }) / repr/hash
    Keyring-->>Provider: signed external message (base64)
    Provider-->>DApp: result
  else insufficient
    Provider-->>DApp: Error "Not enough funds"
  end
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The PR implements extensive TON SDK and wallet serialization changes that align with the SDK-replacement objective, but the diffs do not show the guest account credentials ("guest"/"Test123!"), nor do they show fixes or references for hardware issues #808 and #65 or an explicit note that software issue #270 remains unimplemented. There is also no visible cross-platform regression test plan or test results for TON cases. Therefore the PR only partially satisfies the linked issue. Add or point to commits/config that introduce the guest account and password, document hardware fixes or link follow-up PRs for #808 and #65, explicitly state the status of software issue #270, and include a cross-platform regression test plan and results for all TON cases before merging.
Out of Scope Changes Check ⚠️ Warning The PR contains changes that appear out of scope for the OK-42883 objective, notably mobile/android build setting changes (apps/mobile/android/gradle.properties), a new react-native-fast-pbkdf2 dependency (apps/mobile/package.json), and unrelated top-level dependency bumps in package.json that are not clearly tied to the TON fixes. These additions broaden the change set and increase review and regression risk for a PR focused on TON issues. Either split the Android/mobile dependency and compileSdk changes and general dependency bumps into a separate PR, or add a clear justification and targeted tests in this PR explaining why they are required for the TON work, then run CI and platform regression tests.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Fix/ton issue OK-42883" references the linked issue and signals a TON-related fix; it is concise and directly related to the changeset. It is somewhat generic and could be clearer about the main technical change (SDK swap and wallet v4R2 support), but it is not misleading.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/tonIssue

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

@revan-zhang
Copy link
Contributor

revan-zhang commented Sep 17, 2025

🎉 Snyk checks have passed. No issues have been found so far.

security/snyk check is complete. No issues have been found. (View Details)

license/snyk check is complete. No issues have been found. (View Details)

@ByteZhang1024 ByteZhang1024 force-pushed the fix/tonIssue branch 2 times, most recently from f099b0f to 062f7b9 Compare September 17, 2025 07:54
@ByteZhang1024 ByteZhang1024 marked this pull request as ready for review September 17, 2025 08:00
Copy link

socket-security bot commented Sep 17, 2025

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 18

Caution

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

⚠️ Outside diff range comments (4)
packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts (1)

185-196: Guard unknown wallet versions.

If versionMap[version] is undefined, the device call will be wrong.

Apply:

   const hwParams: CommonParams & TonSignMessageParams = {
     path: account.path,
     ...deviceCommonParams,
@@
-    walletVersion: versionMap[version as keyof typeof versionMap],
+    walletVersion: versionMap[version as keyof typeof versionMap],
   };
+  if (!hwParams.walletVersion) {
+    throw new OneKeyLocalError(`Unsupported TON wallet version: ${version}`);
+  }
packages/kit-bg/src/providers/ProviderApiTon.ts (3)

286-303: Separate “invalid” vs “expired” validUntil checks.

You currently throw “Incorrect validUntil” for expired TXs, then check again. Split concerns.

-    if (
-      validUntil !== undefined &&
-      (lodashIsNaN(validUntil) ||
-        validUntil === null ||
-        typeof validUntil !== 'number' ||
-        validUntil < Date.now() / 1000)
-    ) {
+    if (
+      validUntil !== undefined &&
+      (lodashIsNaN(validUntil) || validUntil === null || typeof validUntil !== 'number')
+    ) {
       throw new Web3RpcError(
         TonResponseError.BadRequest,
         'Incorrect validUntil',
       );
     }
     if (validUntil != null && validUntil < Date.now() / 1000) {

330-334: Enforce the 4-message limit (you advertise it).

You expose maxMessages: 4. Validate here.

-    // check messages
-    if (encodedTx.messages.length === 0) {
+    // check messages
+    if (encodedTx.messages.length === 0) {
       throw new Web3RpcError(TonResponseError.BadRequest, 'Empty messages');
     }
+    if (encodedTx.messages.length > 4) {
+      throw new Web3RpcError(TonResponseError.BadRequest, 'Too many messages');
+    }

337-381: Tighten amount checks and remove duplicate address check.

  • You check “Address is required” twice.
  • Negative string amounts slip through.
       if (typeof message.amount !== 'string') {
         throw new Web3RpcError(
           TonResponseError.BadRequest,
           'Wrong amount format',
         );
       }
+      if (new BigNumber(message.amount).isNaN() || new BigNumber(message.amount).isNegative()) {
+        throw new Web3RpcError(
+          TonResponseError.BadRequest,
+          'Wrong amount format',
+        );
+      }
@@
-      if (!message.address) {
-        throw new Web3RpcError(
-          TonResponseError.BadRequest,
-          'Address is required',
-        );
-      }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 183f9f5 and 062f7b9.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (13)
  • apps/mobile/android/gradle.properties (1 hunks)
  • apps/mobile/package.json (1 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/core/src/chains/ton/CoreChainSoftware.ts (3 hunks)
  • packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts (1 hunks)
  • packages/kit-bg/src/providers/ProviderApiTon.ts (8 hunks)
  • packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts (5 hunks)
  • packages/kit-bg/src/vaults/impls/ton/KeyringHd.ts (2 hunks)
  • packages/kit-bg/src/vaults/impls/ton/KeyringImported.ts (2 hunks)
  • packages/kit-bg/src/vaults/impls/ton/Vault.ts (1 hunks)
  • packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (5 hunks)
  • patches/@ton+core+0.61.0.patch (1 hunks)
🧰 Additional context used
🪛 Biome (2.1.2)
packages/kit-bg/src/providers/ProviderApiTon.ts

[error] 8-8: Do not shadow the global "isNaN" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unittest (20.x)
  • GitHub Check: lint (20.x)
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (11)
apps/mobile/android/gradle.properties (1)

68-69: AsyncStorage DB size re-added.

No functional change. LGTM.

packages/kit-bg/src/vaults/impls/ton/KeyringImported.ts (1)

83-85: BOC base64 for rawTx: good.

Using toBoc({ idx: false }) here is correct for network submission.

packages/core/src/chains/ton/CoreChainSoftware.ts (2)

3-3: Switch to @ton/core Cell: good move.

Removes TonWeb runtime dependency for signing path.


34-34: Type-only TonWeb import: confirm TS declarations exist.

You use import type TonWeb from 'tonweb' but cast with as keyof typeof TonWeb.Wallets.all — that pattern requires TonWeb to have TypeScript declaration files (bundled .d.ts or @types/tonweb). If no declarations exist, TonWeb becomes any and type safety is lost.

Verify a declaration file exists. If none, either switch to a runtime import (import TonWeb from 'tonweb') or add proper typings.

Location: packages/core/src/chains/ton/CoreChainSoftware.ts — lines 34 and 131–135

packages/kit-bg/src/vaults/impls/ton/KeyringHd.ts (1)

85-87: BOC base64 generation looks correct.

Consistent with other implementations.

package.json (1)

70-75: Bump cross-inpage-provider stack to 2.2.43 — run a quick integration smoke.

Couldn't verify in-repo references: ripgrep returned "No files were searched". I can't confirm impact. Sanity-check connect/Tx flows against cross-inpage-provider PR #406 and re-run these local checks; paste output if anything looks off.

Suggested local commands:

rg --debug -n --hidden --follow --no-ignore-vcs -S '@onekeyfe/cross-inpage-provider-(core|errors|injected|types)|onekey-cross-webview' || true
git ls-files | rg -n '@onekeyfe/cross-inpage-provider-(core|errors|injected|types)|onekey-cross-webview' || true
fd -H 'patches/*cross*' || true
packages/kit-bg/src/vaults/impls/ton/Vault.ts (1)

451-465: Verify fee estimators accept signingMessage BOC

You switched estimate params to use signingMessage.toBoc() as the encoded body — confirm all supported RPCs/fee-simulators (Toncenter, etc.) accept this BOC and return correct fee estimates, especially when init_code / init_data are present. Location: packages/kit-bg/src/vaults/impls/ton/Vault.ts (≈ lines 451–465).

apps/mobile/package.json (1)

99-99: Confirm react-native-fast-pbkdf2 is linked and actually used

  • I see the dependency in apps/mobile/package.json (line ~99) and yarn.lock, but no code imports "react-native-fast-pbkdf2".
  • pbkdf2 calls RN_AES.pbkdf2 (packages/shared/src/appCrypto/modules/pbkdf2.ts) — verify the RN_AES wrapper forwards to react-native-fast-pbkdf2.
  • Podfile.lock does not list a pod for it; Android only shows Pbkdf2_compileSdkVersion in apps/mobile/android/gradle.properties.
  • Action: either remove the unused dependency (package.json + yarn.lock) or ensure iOS Podfile.lock and Android autolinking actually include the native module and that the RN_AES wrapper calls it.
packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts (2)

228-231: Confirm extPayload encoding matches firmware expectations.

You push extMsg.payload as-is, while for the primary message you convert to hex or decode comment. Ensure the device expects hex/base64 consistently for ext payloads.

Would you like me to align ext payload handling (e.g., base64→hex like the main branch)?


303-305: LGTM: BOC without indices for rawTx.

Using toBoc({ idx: false }) here is correct and interoperable.

patches/@ton+core+0.61.0.patch (1)

1-20: Patch-package hygiene and forward-compat.

You added a non-standard Cell.repr() API — fragile across upgrades.

Verification:

  • package.json postinstall: node development/scripts/postinstall.js
  • patch-package present: 8.0.0
  • patches/@ton+core+0.61.0.patch: not found
  • no .repr( usages found under packages/

Action items:

  • Pin @ton/core to 0.61.0 and keep the patch under version control.
  • Ensure CI/release runs postinstall so patches are applied.
  • Add a feature-detect fallback where .repr() is called.

AsyncStorage_db_size_in_MB=1024
AsyncStorage_db_size_in_MB=1024

Pbkdf2_compileSdkVersion=android-31
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Set Pbkdf2 compileSdkVersion to match RN (avoid mismatch).

RN 0.79 typically compiles with a higher SDK (e.g., 35). Using android-31 here can cause build warnings or incompatibilities in submodules. Align it with the app’s compileSdk.

Apply:

-Pbkdf2_compileSdkVersion=android-31
+Pbkdf2_compileSdkVersion=android-35
📝 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
Pbkdf2_compileSdkVersion=android-31
Pbkdf2_compileSdkVersion=android-35
🤖 Prompt for AI Agents
In apps/mobile/android/gradle.properties around line 70,
Pbkdf2_compileSdkVersion is set to android-31 which can mismatch React Native's
compileSdk; update this property to match the app's compileSdk (for RN 0.79
typically android-35) or set it to the shared project compileSdk variable (e.g.,
android-35 or ${compileSdk}) so the submodule compiles against the same SDK as
the app.

Comment on lines +80 to 82
const signingMessage = Cell.fromHex(rawTxUnsigned);
const hash = signingMessage.hash();
const [signature] = await signer.sign(Buffer.from(hash));
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Harden parsing of rawTxUnsigned.

Add a guard so bad hex/BOC surfaces a clear error, not a cryptic stack.

-    const signingMessage = Cell.fromHex(rawTxUnsigned);
-    const hash = signingMessage.hash();
+    let signingMessage;
+    try {
+      signingMessage = Cell.fromHex(rawTxUnsigned);
+    } catch {
+      throw new OneKeyInternalError('rawTxUnsigned is not a valid TON BOC hex');
+    }
+    const hash = signingMessage.hash();
📝 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 signingMessage = Cell.fromHex(rawTxUnsigned);
const hash = signingMessage.hash();
const [signature] = await signer.sign(Buffer.from(hash));
let signingMessage;
try {
signingMessage = Cell.fromHex(rawTxUnsigned);
} catch {
throw new OneKeyInternalError('rawTxUnsigned is not a valid TON BOC hex');
}
const hash = signingMessage.hash();
const [signature] = await signer.sign(Buffer.from(hash));

/* eslint-disable no-plusplus */
import { beginCell, storeMessageRelaxed } from '@ton/core';

import type { Cell, MessageRelaxed, SendMode } from '@ton/ton';
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Unify types with @ton/core to avoid structural mismatches.

You import Cell, MessageRelaxed, SendMode types from @ton/ton but build cells with @ton/core. Prefer a single source.

Apply:

-import type { Cell, MessageRelaxed, SendMode } from '@ton/ton';
+import type { Cell, MessageRelaxed, SendMode } from '@ton/core';
📝 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
import type { Cell, MessageRelaxed, SendMode } from '@ton/ton';
import type { Cell, MessageRelaxed, SendMode } from '@ton/core';
🤖 Prompt for AI Agents
In packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts around line 4,
the file imports Cell, MessageRelaxed and SendMode from '@ton/ton' while
elsewhere it constructs Cells using '@ton/core', causing potential structural
type mismatches; change the import source to '@ton/core' (e.g. import types from
'@ton/core' instead of '@ton/ton') so all TON types come from the same package,
update any references to match the '@ton/core' type names if needed, and run
type-checks to ensure no remaining mismatches.

Comment on lines +28 to +30
export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>(
args: T & { sendMode: number; walletId: number },
) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Type sendMode as SendMode, not number.

This improves readability and prevents invalid flags.

-export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>(
-  args: T & { sendMode: number; walletId: number },
+export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>(
+  args: T & { sendMode: SendMode; walletId: number },
📝 Committable suggestion

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

Suggested change
export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>(
args: T & { sendMode: number; walletId: number },
) {
export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>(
args: T & { sendMode: SendMode; walletId: number },
) {
🤖 Prompt for AI Agents
In packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts around lines
28-30, the sendMode parameter is typed as number; change its type to the
SendMode enum/type (replace "number" with "SendMode") to restrict valid flag
values, add the appropriate import for SendMode from the module where it’s
defined, and update any local references or call sites if needed to use the
SendMode type; run type-checks to ensure no remaining violations.

});
params.unsignedTx.rawTxUnsigned = hexUtils.hexlify(
await serializeUnsignedTx.signingMessage.toBoc(),
serializeUnsignedTx.signingMessage.toBoc(),
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Unify BOC encoding: prefer idx:false for rawTxUnsigned for consistency.

Other paths use { idx: false }. Aligning avoids subtle mismatches across platforms/tools.

-    params.unsignedTx.rawTxUnsigned = hexUtils.hexlify(
-      serializeUnsignedTx.signingMessage.toBoc(),
+    params.unsignedTx.rawTxUnsigned = hexUtils.hexlify(
+      serializeUnsignedTx.signingMessage.toBoc({ idx: false }),
       {
         noPrefix: true,
       },
     );
📝 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
serializeUnsignedTx.signingMessage.toBoc(),
serializeUnsignedTx.signingMessage.toBoc({ idx: false }),
🤖 Prompt for AI Agents
In packages/kit-bg/src/vaults/impls/ton/KeyringImported.ts around line 66, the
call serializeUnsignedTx.signingMessage.toBoc() should be made consistent with
other code paths by passing { idx: false } to unify BOC encoding; update the
call to serializeUnsignedTx.signingMessage.toBoc({ idx: false }) so
rawTxUnsigned uses idx:false and avoid cross-platform/tool mismatches.

Comment on lines +212 to +215
return max === '1'
? SendMode.CARRY_ALL_REMAINING_BALANCE
: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Use bitwise OR for flags.

Send modes are bit flags. | is clearer than +.

-  return max === '1'
-    ? SendMode.CARRY_ALL_REMAINING_BALANCE
-    : SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS;
+  return max === '1'
+    ? SendMode.CARRY_ALL_REMAINING_BALANCE
+    : (SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS);
📝 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
return max === '1'
? SendMode.CARRY_ALL_REMAINING_BALANCE
: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS;
};
return max === '1'
? SendMode.CARRY_ALL_REMAINING_BALANCE
: (SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS);
};
🤖 Prompt for AI Agents
In packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts around lines 212 to 215,
the code mixes send-mode flags with arithmetic addition; replace the use of `+`
with bitwise OR (`|`) so the flags are combined as bitwise flags (e.g., use
`SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS`), preserving the ternary
return logic and keeping `SendMode.CARRY_ALL_REMAINING_BALANCE` for the `max ===
'1'` branch.

Comment on lines +228 to +233
const initSlice = Cell.fromBase64(stateInit).asSlice();
return {
code: initSlice.loadRef(),
data: initSlice.loadRef(),
};
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Safer StateInit decoding.

StateInit may omit code or data. Avoid unconditional loadRef().

-  const initSlice = Cell.fromBase64(stateInit).asSlice();
-  return {
-    code: initSlice.loadRef(),
-    data: initSlice.loadRef(),
-  };
+  const initSlice = Cell.fromBase64(stateInit).asSlice();
+  const code = initSlice.loadMaybeRef();
+  const data = initSlice.loadMaybeRef();
+  return { code: code ?? undefined, data: data ?? undefined };
📝 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 initSlice = Cell.fromBase64(stateInit).asSlice();
return {
code: initSlice.loadRef(),
data: initSlice.loadRef(),
};
};
const initSlice = Cell.fromBase64(stateInit).asSlice();
const code = initSlice.loadMaybeRef();
const data = initSlice.loadMaybeRef();
return {
code: code ?? undefined,
data: data ?? undefined,
};
};
🤖 Prompt for AI Agents
In packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts around lines 228–233,
the StateInit decoding unconditionally calls initSlice.loadRef() for code and
data even though StateInit may omit those refs; change to check for available
refs before loading (e.g., use initSlice.remainingRefs or the API's
safe/optional load method) and only call loadRef() when present, returning
undefined (or null) for missing code/data so the caller can handle absent refs
safely.

Comment on lines +242 to +266
const name = contract.getName();

if (name === 'v4R2') {
// @ts-expect-error
const walletId = contract?.options?.walletId;
const stateInit =
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/await-thenable
(await contract.createStateInit()) as unknown as StateInit;

const signingMessage = createWalletTransferV4({
seqno: encodedTx.sequenceNo || 0,
messages: encodedTx.messages.map((message) =>
internal({
to: message.address,
value: toNano(fromNano(message.amount)),
bounce: seeIfBounceable(message.address),
body: message.payload ? Cell.fromBase64(message.payload) : undefined,
init: toStateInit(message.stateInit),
}),
),
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
walletId,
});

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Runtime risk: contract may not implement getName()/getWalletId.

Casting doesn’t add methods. If absent, this will throw or produce undefined walletId.

-  const name = contract.getName();
+  const name = typeof (contract as any).getName === 'function'
+    ? (contract as any).getName()
+    : 'v4R2'; // fallback if TonWeb wallet lacks getName()
@@
-    // @ts-expect-error
-    const walletId = contract?.options?.walletId;
+    const walletId =
+      (typeof (contract as any).getWalletId === 'function'
+        ? await (contract as any).getWalletId()
+        : (contract as any)?.options?.walletId);
+    if (walletId == null) {
+      throw new OneKeyInternalError('walletId is missing for v4R2');
+    }

Also consider passing version into this function to avoid relying on instance methods.

📝 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 name = contract.getName();
if (name === 'v4R2') {
// @ts-expect-error
const walletId = contract?.options?.walletId;
const stateInit =
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/await-thenable
(await contract.createStateInit()) as unknown as StateInit;
const signingMessage = createWalletTransferV4({
seqno: encodedTx.sequenceNo || 0,
messages: encodedTx.messages.map((message) =>
internal({
to: message.address,
value: toNano(fromNano(message.amount)),
bounce: seeIfBounceable(message.address),
body: message.payload ? Cell.fromBase64(message.payload) : undefined,
init: toStateInit(message.stateInit),
}),
),
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
walletId,
});
const name = typeof (contract as any).getName === 'function'
? (contract as any).getName()
: 'v4R2'; // fallback if TonWeb wallet lacks getName()
if (name === 'v4R2') {
const walletId =
(typeof (contract as any).getWalletId === 'function'
? await (contract as any).getWalletId()
: (contract as any)?.options?.walletId);
if (walletId == null) {
throw new OneKeyInternalError('walletId is missing for v4R2');
}
const stateInit =
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/await-thenable
(await contract.createStateInit()) as unknown as StateInit;
const signingMessage = createWalletTransferV4({
seqno: encodedTx.sequenceNo || 0,
messages: encodedTx.messages.map((message) =>
internal({
to: message.address,
value: toNano(fromNano(message.amount)),
bounce: seeIfBounceable(message.address),
body: message.payload ? Cell.fromBase64(message.payload) : undefined,
init: toStateInit(message.stateInit),
}),
),
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
walletId,
});
🤖 Prompt for AI Agents
In packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts around lines 242–266,
the code assumes contract.getName() and contract.options.walletId exist which
can throw or yield undefined; add runtime guards and an explicit API to avoid
relying on instance methods: check typeof contract?.getName === 'function'
before calling it (fallback to a provided version parameter if absent), ensure
walletId is read defensively (const walletId = contract?.options?.walletId ??
providedWalletId) and validate it before use (throw or return a clear error if
missing), wrap await contract.createStateInit() in try/catch to handle
non-implementing contracts, and update function signature to accept an optional
version and/or walletId so callers can pass these instead of relying on contract
methods.

Comment on lines +253 to +265
seqno: encodedTx.sequenceNo || 0,
messages: encodedTx.messages.map((message) =>
internal({
to: message.address,
value: toNano(fromNano(message.amount)),
bounce: seeIfBounceable(message.address),
body: message.payload ? Cell.fromBase64(message.payload) : undefined,
init: toStateInit(message.stateInit),
}),
),
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
walletId,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Value conversion can be simpler and lossless.

If message.amount is nanotons, you can avoid fromNano→toNano.

-          value: toNano(fromNano(message.amount)),
+          value: BigInt(message.amount),

Confirm upstream amount unit is nanotons before changing.

📝 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
seqno: encodedTx.sequenceNo || 0,
messages: encodedTx.messages.map((message) =>
internal({
to: message.address,
value: toNano(fromNano(message.amount)),
bounce: seeIfBounceable(message.address),
body: message.payload ? Cell.fromBase64(message.payload) : undefined,
init: toStateInit(message.stateInit),
}),
),
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
walletId,
});
seqno: encodedTx.sequenceNo || 0,
messages: encodedTx.messages.map((message) =>
internal({
to: message.address,
value: BigInt(message.amount),
bounce: seeIfBounceable(message.address),
body: message.payload ? Cell.fromBase64(message.payload) : undefined,
init: toStateInit(message.stateInit),
}),
),
sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
walletId,
});

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (4)
packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts (1)

174-179: Use bitwise OR for send mode flags.

These are flags; | reads clearer than +.

Apply:

-        msg.sendMode =
-          ETonSendMode.PAY_GAS_SEPARATELY + ETonSendMode.IGNORE_ERRORS;
+        msg.sendMode =
+          ETonSendMode.PAY_GAS_SEPARATELY | ETonSendMode.IGNORE_ERRORS;
packages/kit-bg/src/providers/ProviderApiTon.ts (2)

285-303: Tighten validUntil checks and drop duplication.

One block is enough.

Apply:

-    if (
-      validUntil !== undefined &&
-      (isNaN(validUntil) ||
-        validUntil === null ||
-        typeof validUntil !== 'number' ||
-        validUntil < Date.now() / 1000)
-    ) {
+    if (
+      validUntil !== undefined &&
+      (lodashIsNaN(validUntil) || typeof validUntil !== 'number')
+    ) {
       throw new Web3RpcError(
         TonResponseError.BadRequest,
         'Incorrect validUntil',
       );
     }
-    if (validUntil != null && validUntil < Date.now() / 1000) {
+    if (validUntil != null && validUntil < Date.now() / 1000) {
       throw new Web3RpcError(
         TonResponseError.BadRequest,
         'Transaction has expired',
       );
     }

322-328: Avoid ts-expect-error: compare networks as strings.

Coerce and compare; remove the suppression.

Apply:

-    if (
-      encodedTx.network != null &&
-      // @ts-expect-error
-      encodedTx.network !== ETonNetwork.Mainnet
-    ) {
+    if (encodedTx.network != null) {
+      const net = String(encodedTx.network);
+      if (net !== ETonNetwork.Mainnet) {
         throw new Web3RpcError(TonResponseError.BadRequest, 'Wrong network');
-    }
+      }
+    }
patches/@ton+core+0.61.0.patch (1)

1-287: Add a safe fallback for Cell.repr() and confirm patch-package is applied

  • patch-package runs in postinstall and CI (development/scripts/postinstall.js); patches/@ton+core+0.61.0.patch is present.
  • Unguarded repr() usage found at packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts:241 — add a runtime guard/fallback (e.g., if repr is missing fall back to signingMessage.toBoc()/hash) to avoid runtime failures when the patch isn't applied.
♻️ Duplicate comments (17)
packages/kit-bg/src/vaults/impls/ton/KeyringHd.ts (1)

67-72: Unify BOC serialization: use { idx: false } for rawTxUnsigned.

Other paths already use idx:false. Keep it deterministic.

Apply:

-    params.unsignedTx.rawTxUnsigned = hexUtils.hexlify(
-      serializeUnsignedTx.signingMessage.toBoc(),
+    params.unsignedTx.rawTxUnsigned = hexUtils.hexlify(
+      serializeUnsignedTx.signingMessage.toBoc({ idx: false }),
       {
         noPrefix: true,
       },
     );
packages/kit-bg/src/vaults/impls/ton/KeyringImported.ts (1)

66-70: Unify BOC serialization: use { idx: false } for rawTxUnsigned.

Match the rest of the TON paths to avoid index sections.

Apply:

-    params.unsignedTx.rawTxUnsigned = hexUtils.hexlify(
-      serializeUnsignedTx.signingMessage.toBoc(),
+    params.unsignedTx.rawTxUnsigned = hexUtils.hexlify(
+      serializeUnsignedTx.signingMessage.toBoc({ idx: false }),
       {
         noPrefix: true,
       },
     );
apps/mobile/android/gradle.properties (1)

70-70: Set Pbkdf2 compileSdkVersion to match RN (avoid mismatch).

RN 0.79 typically compiles with android-35. Using android-31 can break NDK builds.

Apply:

-Pbkdf2_compileSdkVersion=android-31
+Pbkdf2_compileSdkVersion=android-35
packages/core/package.json (1)

36-38: Pin @ton/ to exact versions to keep the patch stable.*

You ship a patch for @ton/core 0.61.0. Carets can auto-upgrade and break Cell.repr.

Apply:

-    "@ton/core": "^0.61.0",
-    "@ton/crypto": "^3.3.0",
-    "@ton/ton": "^15.3.1",
+    "@ton/core": "0.61.0",
+    "@ton/crypto": "3.3.0",
+    "@ton/ton": "15.3.1",

Also add to root resolutions:

 "resolutions": {
+  "@ton/core": "0.61.0",
   // ...
 }

Check patch + hooks:

#!/bin/bash
fd -a 'patches/@ton+core+0.61.0.patch'
rg -n 'patch-package' -S
rg -n 'postinstall' package.json development -S
packages/core/src/chains/ton/CoreChainSoftware.ts (1)

80-82: Harden BOC parse with a clear error.

Bad hex now throws a cryptic error.

Apply:

-    const signingMessage = Cell.fromHex(rawTxUnsigned);
+    let signingMessage;
+    try {
+      signingMessage = Cell.fromHex(rawTxUnsigned);
+    } catch {
+      throw new OneKeyInternalError('rawTxUnsigned is not a valid TON BOC hex');
+    }
     const hash = signingMessage.hash();
packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts (2)

261-279: Normalize BOC for HW comparison (idx: false) and compare lowercase.

HW often returns BOC without indices. Avoid false mismatches.

Apply:

-    const signingMessageHex = Buffer.from(signingMessage.toBoc()).toString(
-      'hex',
-    );
-    const signingMessageHash = Buffer.from(signingMessage.hash()).toString(
-      'hex',
-    );
+    const signingMessageHex = Buffer.from(
+      signingMessage.toBoc({ idx: false }),
+    ).toString('hex').toLowerCase();
+    const signingMessageHash = Buffer.from(
+      signingMessage.hash(),
+    ).toString('hex').toLowerCase();
@@
-        signingMessageHexFromHw !== signingMessageHex
+        signingMessageHexFromHw.toLowerCase() !== signingMessageHex
@@
-        signingMessageHexFromHw !== signingMessageHash
+        signingMessageHexFromHw.toLowerCase() !== signingMessageHash

238-243: Fix hex encoding: don’t hex-encode then hex-encode again.

You pass a hex string into bytesToHex, producing wrong output. Write hex once and support no-repr fallback.

Apply:

-      hwParams.signingMessageRepr = bufferUtils.bytesToHex(
-        // await TonWeb.boc.Cell.oneFromBoc(Buffer.from(signingMessage.toBoc())).getRepr(),
-        // only for hardware, only serialize for stateInit
-        // eslint-disable-next-line @typescript-eslint/no-unsafe-call
-        Buffer.from(signingMessage.repr()).toString('hex'),
-      );
+      // only for hardware, only serialize for stateInit
+      // prefer repr() if available, fallback to canonical BOC
+      // @ts-expect-error
+      if (typeof (signingMessage as any).repr === 'function') {
+        // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+        hwParams.signingMessageRepr = Buffer.from(signingMessage.repr()).toString('hex');
+      } else {
+        hwParams.signingMessageRepr = Buffer.from(
+          signingMessage.toBoc({ idx: false }),
+        ).toString('hex');
+      }
packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts (3)

4-4: Unify TON types: import from @ton/core, not @ton/ton.

Avoid structural mismatches with Cells built via @ton/core.

Apply:

-import type { Cell, MessageRelaxed, SendMode } from '@ton/ton';
+import type { Cell, MessageRelaxed, SendMode } from '@ton/core';

28-30: Type sendMode as SendMode.

Prevents invalid flags.

Apply:

-export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>(
-  args: T & { sendMode: number; walletId: number },
+export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>(
+  args: T & { sendMode: SendMode; walletId: number },

36-45: Write 0xffffffff with storeUint, not 32 storeBit calls.

Cleaner and faster.

Apply:

-  if (args.seqno === 0) {
-    for (let i = 0; i < 32; i++) {
-      signingMessage.storeBit(1);
-    }
-  } else {
+  if (args.seqno === 0) {
+    signingMessage.storeUint(0xffffffff, 32);
+  } else {
packages/kit-bg/src/providers/ProviderApiTon.ts (4)

8-8: Avoid shadowing global isNaN.

Alias lodash import.

Apply:

-import { get, isEmpty, isNaN } from 'lodash';
+import { get, isEmpty, isNaN as lodashIsNaN } from 'lodash';

And below:

-      (isNaN(validUntil) ||
+      (lodashIsNaN(validUntil) ||

283-284: Remove noisy console.log in prod path.

Don’t log full TX payloads.

Apply:

-    console.log('sendTransaction ========>>>>> encodedTx: ', encodedTx);
+    // debug: encodedTx

426-441: Compare canonical friendly addresses, not raw form.

Raw vs friendly will always mismatch.

Apply:

-        const fromAddr = new TonWeb.Address(encodedTx.from);
-        if (
-          fromAddr.toString(false, false, false) !==
-          account.account.addressDetail.baseAddress
-        ) {
+        const fromAddr = new TonWeb.Address(encodedTx.from);
+        const canonicalFrom = fromAddr.toString(true, true, false);
+        const canonicalAccount = account.account.addressDetail.baseAddress;
+        if (canonicalFrom !== canonicalAccount) {
           throw new Web3RpcError(
             TonResponseError.BadRequest,
             'Wrong from address',
           );
         }

50-64: Drop redundant declare in .ts.

Interfaces in impl files don’t need declare.

Apply:

-export declare interface ITonAddressItem {
+export interface ITonAddressItem {
@@
-export declare interface ITonProofItem {
+export interface ITonProofItem {
@@
-export interface IConnectRequest {
+export interface IConnectRequest {
packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (3)

211-216: Combine send-mode flags with bitwise OR.

Use flags, not addition.

Apply:

 export const getTonSendMode = (max: string | undefined) => {
   return max === '1'
     ? SendMode.CARRY_ALL_REMAINING_BALANCE
-    : SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS;
+    : (SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS);
 };

223-233: Safer StateInit decoding: refs may be absent.

Avoid unconditional loadRef.

Apply:

-  const initSlice = Cell.fromBase64(stateInit).asSlice();
-  return {
-    code: initSlice.loadRef(),
-    data: initSlice.loadRef(),
-  };
+  const initSlice = Cell.fromBase64(stateInit).asSlice();
+  const code = initSlice.loadMaybeRef();
+  const data = initSlice.loadMaybeRef();
+  return { code: code ?? undefined, data: data ?? undefined };

242-266: Guard contract methods and validate walletId.

getName/getWalletId may not exist; options.walletId may be undefined.

Apply:

-  const name = contract.getName();
+  const name =
+    typeof (contract as any).getName === 'function'
+      ? (contract as any).getName()
+      : 'v4R2';
@@
-    // @ts-expect-error
-    const walletId = contract?.options?.walletId;
+    const walletId =
+      (typeof (contract as any).getWalletId === 'function'
+        ? await (contract as any).getWalletId()
+        : (contract as any)?.options?.walletId);
+    if (walletId == null) {
+      throw new OneKeyInternalError('walletId is missing for v4R2');
+    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 062f7b9 and 963977d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (13)
  • apps/mobile/android/gradle.properties (1 hunks)
  • apps/mobile/package.json (1 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/core/src/chains/ton/CoreChainSoftware.ts (3 hunks)
  • packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts (1 hunks)
  • packages/kit-bg/src/providers/ProviderApiTon.ts (8 hunks)
  • packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts (5 hunks)
  • packages/kit-bg/src/vaults/impls/ton/KeyringHd.ts (2 hunks)
  • packages/kit-bg/src/vaults/impls/ton/KeyringImported.ts (2 hunks)
  • packages/kit-bg/src/vaults/impls/ton/Vault.ts (2 hunks)
  • packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (5 hunks)
  • patches/@ton+core+0.61.0.patch (1 hunks)
🧰 Additional context used
🪛 Biome (2.1.2)
packages/kit-bg/src/providers/ProviderApiTon.ts

[error] 8-8: Do not shadow the global "isNaN" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: unittest (20.x)
  • GitHub Check: lint (20.x)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (11)
packages/kit-bg/src/vaults/impls/ton/KeyringHd.ts (1)

85-87: LGTM on final rawTx BOC options.

Using toBoc({ idx: false }) and base64 encoding is correct and consistent.

packages/kit-bg/src/vaults/impls/ton/KeyringImported.ts (1)

83-86: LGTM on signed external message serialization.

toBoc({ idx: false }) + base64 keeps rawTx stable across platforms.

packages/kit-bg/src/vaults/impls/ton/Vault.ts (2)

421-421: Clearer expiry error message.

Good copy. Short and precise.


451-465: BOC encoding now deterministic.

Switching body/init to toBoc({ idx: false }) is correct and aligns with signing paths.

package.json (1)

70-75: Version bump OK — dependents in lockstep.
package.json (lines 70–74) shows @onekeyfe/cross-inpage-provider-* and @onekeyfe/extension-bridge-hosted at 2.2.43; no 2.2.42 occurrences found.

apps/mobile/package.json (1)

99-99: Add rn-fast-pbkdf2: align Android + avoid double PBKDF2.

  • apps/mobile/package.json adds: "react-native-fast-pbkdf2": "^0.3.1".
  • Ensure Pbkdf2_compileSdkVersion equals RN compileSdk (check android/gradle.properties and android/build.gradle).
  • Do not bundle JS "pbkdf2" into mobile — gate the JS fallback to web/desktop or remove it from mobile deps. No "pbkdf2" entry was found in root package.json in the checked files; repo-wide search was inconclusive — verify all package.json and lockfiles and remove/gate any JS pbkdf2.
  • Suggested quick check: rg -n -S -C2 "pbkdf2|fast-pbkdf2|react-native-fast-pbkdf2" -g '!/node_modules/' || true
packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts (2)

302-305: LGTM: canonical rawTx serialization.

Using toBoc({ idx: false }) avoids index bytes and matches device output.


260-268: ```shell
#!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo "rg version:"
rg --version || echo "rg not found"

echo
echo "Check target file (from review):"
if [ -f "packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts" ]; then
echo "File exists — printing lines 240-300:"
sed -n '240,300p' packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts || true
else
echo "packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts not found"
fi

echo
echo "Searching repository for 'signning_message' and 'signing_message' (no ignores):"
rg -nC2 -uu --hidden --no-ignore-vcs -P '\bsignning_message\b|\bsigning_message\b' || true

echo
echo "Searching for 'signning' or 'signing' tokens (broader):"
rg -nC2 -uu --hidden --no-ignore-vcs -P '\bsignning\b|\bsigning\b' || true

echo
echo "Fallback: git grep (if in git repo):"
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
git grep -n --untracked -e 'signning_message' -e 'signing_message' || true
else
echo "Not a git repo, skipping git grep."
fi

echo
echo "Done."


</blockquote></details>
<details>
<summary>packages/kit-bg/src/providers/ProviderApiTon.ts (1)</summary><blockquote>

`122-142`: **LGTM: account/vault helpers.**

These helpers read clean and isolate concerns.

</blockquote></details>
<details>
<summary>packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (2)</summary><blockquote>

`297-346`: **LGTM: external message assembly with packSignatureToFront.**

Flow matches v4R2 expectations and keeps init optional.

---

`255-261`: **Amount unit: avoid redundant fromNano→toNano — verify upstream unit**

Vault.ts assigns encodedTx.messages[0].amount = amount (packages/kit-bg/src/vaults/impls/ton/Vault.ts:343). Confirm whether that upstream amount is already in nanotons. If it is, replace the conversion in sdkTon/utils.ts with a direct BigInt to avoid needless parse/format churn.

Apply (if nanotons):
```diff
-          value: toNano(fromNano(message.amount)),
+          value: BigInt(message.amount),

Files: packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (lines ~255–261), packages/kit-bg/src/vaults/impls/ton/Vault.ts (line ~343).

Comment on lines +49 to +52
for (const m of args.messages) {
signingMessage.storeUint(args.sendMode, 8);
signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m)));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Bug: storeRef expects a Cell; you pass a Builder.

Call .endCell() on the inner builder.

Apply:

-    signingMessage.storeUint(args.sendMode, 8);
-    signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m)));
+    signingMessage.storeUint(args.sendMode, 8);
+    signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m)).endCell());
📝 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
for (const m of args.messages) {
signingMessage.storeUint(args.sendMode, 8);
signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m)));
}
for (const m of args.messages) {
signingMessage.storeUint(args.sendMode, 8);
signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m)).endCell());
}
🤖 Prompt for AI Agents
In packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts around lines 49
to 52, the loop calls
signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m))) passing a
Builder to storeRef; change it to end the inner builder to pass a Cell by
calling .endCell() on the inner beginCell() chain (i.e., build the inner cell
with .endCell() before passing it to storeRef) so storeRef receives a Cell
rather than a Builder.

Comment on lines +381 to +406
// payload
if (message.payload) {
try {
void TonWeb.boc.Cell.oneFromBoc(
Buffer.from(message.payload, 'base64').toString('hex'),
);
} catch {
throw new Web3RpcError(
TonResponseError.BadRequest,
'Payload is invalid',
);
}
}

// init
if (message.stateInit) {
try {
void TonWeb.boc.Cell.oneFromBoc(
Buffer.from(message.stateInit, 'base64').toString('hex'),
);
} catch {
throw new Web3RpcError(
TonResponseError.BadRequest,
'stateInit is invalid',
);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Use @ton/core for payload/stateInit validation.

Stay consistent with the rest of the codebase and avoid TonWeb parsing here.

Apply:

-      if (message.payload) {
+      if (message.payload) {
         try {
-          void TonWeb.boc.Cell.oneFromBoc(
-            Buffer.from(message.payload, 'base64').toString('hex'),
-          );
+          void Cell.fromBase64(message.payload);
         } catch {
           throw new Web3RpcError(
             TonResponseError.BadRequest,
             'Payload is invalid',
           );
         }
       }
@@
-      if (message.stateInit) {
+      if (message.stateInit) {
         try {
-          void TonWeb.boc.Cell.oneFromBoc(
-            Buffer.from(message.stateInit, 'base64').toString('hex'),
-          );
+          void Cell.fromBase64(message.stateInit);
         } catch {
           throw new Web3RpcError(
             TonResponseError.BadRequest,
             'stateInit is invalid',
           );
         }
       }
📝 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
// payload
if (message.payload) {
try {
void TonWeb.boc.Cell.oneFromBoc(
Buffer.from(message.payload, 'base64').toString('hex'),
);
} catch {
throw new Web3RpcError(
TonResponseError.BadRequest,
'Payload is invalid',
);
}
}
// init
if (message.stateInit) {
try {
void TonWeb.boc.Cell.oneFromBoc(
Buffer.from(message.stateInit, 'base64').toString('hex'),
);
} catch {
throw new Web3RpcError(
TonResponseError.BadRequest,
'stateInit is invalid',
);
}
}
}
// payload
if (message.payload) {
try {
void Cell.fromBase64(message.payload);
} catch {
throw new Web3RpcError(
TonResponseError.BadRequest,
'Payload is invalid',
);
}
}
// init
if (message.stateInit) {
try {
void Cell.fromBase64(message.stateInit);
} catch {
throw new Web3RpcError(
TonResponseError.BadRequest,
'stateInit is invalid',
);
}
}
}

Comment on lines 1 to 12
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import {
Cell,
beginCell,
external,
fromNano,
internal,
storeMessage,
toNano,
} from '@ton/core';
import { Address, SendMode } from '@ton/ton';
import BigNumber from 'bignumber.js';
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Unify TON APIs: pull Address/SendMode from @ton/core.

Mixing @ton/ton and @ton/core types risks mismatches.

Apply:

-import {
-  Cell,
-  beginCell,
-  external,
-  fromNano,
-  internal,
-  storeMessage,
-  toNano,
-} from '@ton/core';
-import { Address, SendMode } from '@ton/ton';
+import {
+  Cell,
+  Address,
+  SendMode,
+  beginCell,
+  external,
+  fromNano,
+  internal,
+  storeMessage,
+  toNano,
+} from '@ton/core';
📝 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
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import {
Cell,
beginCell,
external,
fromNano,
internal,
storeMessage,
toNano,
} from '@ton/core';
import { Address, SendMode } from '@ton/ton';
import BigNumber from 'bignumber.js';
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import {
Cell,
Address,
SendMode,
beginCell,
external,
fromNano,
internal,
storeMessage,
toNano,
} from '@ton/core';
import BigNumber from 'bignumber.js';
🤖 Prompt for AI Agents
In packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts around lines 1–12, the
file imports Address and SendMode from '@ton/ton' causing mixed TON API usage;
change the import so Address and SendMode are pulled from '@ton/core' instead
(remove them from the '@ton/ton' import), update any uses accordingly, and
ensure there are no remaining imports or references to Address/SendMode from
'@ton/ton' elsewhere in this file.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (3)
packages/kit-bg/src/providers/ProviderApiTon.ts (1)

269-272: Update toBoc API: use options object and drop await.

ton-core’s toBoc is sync and takes { idx }. Current call will break.

-      walletStateInit: Buffer.from(
-        await deploy.stateInit.toBoc(false, false),
-      ).toString('base64'),
+      walletStateInit: Buffer.from(
+        deploy.stateInit.toBoc({ idx: false }),
+      ).toString('base64'),
packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (2)

30-31: Avoid deep type imports from tonweb/dist.

Deep paths are brittle. Inline a minimal local type instead.

-import type { TransferBodyParams } from 'tonweb/dist/types/contract/token/ft/jetton-wallet';
+// Local minimal shape to avoid deep import
+type TransferBodyParams = {
+  queryId?: number;
+  jettonAmount: TonWeb.utils.BN;
+  toAddress: TonWeb.Address;
+  responseAddress: TonWeb.Address;
+  forwardAmount?: TonWeb.utils.BN;
+  forwardPayload?: Uint8Array;
+};

140-165: Good: IWallet exposes getWalletId/getName. Use them and type createStateInit.

Leverage these instead of reaching into .options, and add createStateInit(): Promise<StateInit> to drop TS ignores.

 export interface IWallet extends IV4R2 {
   getWalletId(): Promise<number>;
   getName(): string;
+  createStateInit(): Promise<StateInit>;
   createTransferMessages(
♻️ Duplicate comments (20)
apps/mobile/android/gradle.properties (1)

70-70: Align Pbkdf2 compileSdk with app (RN 0.79 → likely 35).

android-31 can mismatch the app’s compileSdk and break builds. Bump to 35 (or match the project’s compileSdk).

Apply:

-Pbkdf2_compileSdkVersion=android-31
+Pbkdf2_compileSdkVersion=android-35
#!/bin/bash
# Verify compileSdk used by the Android app and submodules
set -euo pipefail
echo "compileSdk declarations in android build files:"
fd -a 'build.gradle*' apps/mobile/android | xargs -I {} rg -nP -C2 '(compileSdk(?:Version)?\s*[:=]\s*\d+|android\.compileSdk\s*=\s*["'\'']?android-\d+)' "{}" || true
echo "Property value in gradle.properties:"
rg -n '^Pbkdf2_compileSdkVersion=' apps/mobile/android/gradle.properties || true
packages/kit-bg/src/vaults/impls/ton/KeyringImported.ts (1)

66-66: Unify BOC encoding: pass { idx: false } for rawTxUnsigned.

Other paths use { idx: false }. Keep consistent to avoid subtle cross‑tool mismatches.

Apply:

-      serializeUnsignedTx.signingMessage.toBoc(),
+      serializeUnsignedTx.signingMessage.toBoc({ idx: false }),
packages/core/package.json (1)

36-38: Pin @ton/ to exact versions so the patch applies.*

You ship a patch for @ton/core 0.61.0. Carets can resolve to 0.61.x and skip the patch, breaking Cell.repr() at runtime.

Apply:

-    "@ton/core": "^0.61.0",
-    "@ton/crypto": "^3.3.0",
-    "@ton/ton": "^15.3.1",
+    "@ton/core": "0.61.0",
+    "@ton/crypto": "3.3.0",
+    "@ton/ton": "15.3.1",

Also add a root resolution to prevent hoist drift:

// package.json (root)
   "resolutions": {
+    "@ton/core": "0.61.0",
     // ...
   }
packages/core/src/chains/ton/CoreChainSoftware.ts (1)

80-81: Harden BOC parsing for clearer errors.

Wrap Cell.fromHex to surface a friendly message on bad input.

Apply:

-    const signingMessage = Cell.fromHex(rawTxUnsigned);
-    const hash = signingMessage.hash();
+    let signingMessage;
+    try {
+      signingMessage = Cell.fromHex(rawTxUnsigned);
+    } catch {
+      throw new OneKeyInternalError('rawTxUnsigned is not a valid TON BOC hex');
+    }
+    const hash = signingMessage.hash();
packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts (4)

4-4: Import TON types from @ton/core to avoid structural mismatches.

Keep Cell/MessageRelaxed/SendMode from the same package used to build Cells.

-import type { Cell, MessageRelaxed, SendMode } from '@ton/ton';
+import type { Cell, MessageRelaxed, SendMode } from '@ton/core';

28-30: Type sendMode as SendMode, not number.

Tighter typing prevents invalid flags.

-export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>(
-  args: T & { sendMode: number; walletId: number },
+export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>(
+  args: T & { sendMode: SendMode; walletId: number },

36-45: Replace 32 storeBit calls with a single storeUint.

Cleaner and faster.

-  if (args.seqno === 0) {
-    for (let i = 0; i < 32; i++) {
-      signingMessage.storeBit(1);
-    }
-  } else {
+  if (args.seqno === 0) {
+    signingMessage.storeUint(0xffffffff, 32);
+  } else {

49-52: Bug: storeRef needs a Cell; you pass a Builder.

End the inner builder before passing it.

-    signingMessage.storeUint(args.sendMode, 8);
-    signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m)));
+    signingMessage.storeUint(args.sendMode, 8);
+    signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m)).endCell());
packages/kit-bg/src/vaults/impls/ton/KeyringHd.ts (1)

67-69: Unify BOC encoding with idx: false.

Match other paths and device output.

-    params.unsignedTx.rawTxUnsigned = hexUtils.hexlify(
-      serializeUnsignedTx.signingMessage.toBoc(),
+    params.unsignedTx.rawTxUnsigned = hexUtils.hexlify(
+      serializeUnsignedTx.signingMessage.toBoc({ idx: false }),
       {
         noPrefix: true,
       },
     );
packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts (2)

261-267: Normalize BOC before comparing with HW output.

Use idx: false to avoid false mismatches.

-    const signingMessageHex = Buffer.from(signingMessage.toBoc()).toString(
-      'hex',
-    );
+    const signingMessageHex = Buffer.from(
+      signingMessage.toBoc({ idx: false }),
+    ).toString('hex');

237-242: Fix hex encoding: passing a hex string into bytesToHex is wrong.

bytesToHex expects bytes. Current code double-hexes and corrupts signingMessageRepr.

-      hwParams.signingMessageRepr = bufferUtils.bytesToHex(
-        // await TonWeb.boc.Cell.oneFromBoc(Buffer.from(signingMessage.toBoc())).getRepr(),
-        // only for hardware, only serialize for stateInit
-        // eslint-disable-next-line @typescript-eslint/no-unsafe-call
-        Buffer.from(signingMessage.repr()).toString('hex'),
-      );
+      // only for hardware, only serialize for stateInit
+      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+      hwParams.signingMessageRepr = Buffer.from(
+        signingMessage.repr(),
+      ).toString('hex');
+
+      // Optional: fallback if repr() is unavailable
+      // @ts-expect-error
+      if (typeof (signingMessage as any).repr !== 'function') {
+        hwParams.signingMessageRepr = Buffer.from(
+          signingMessage.toBoc({ idx: false }),
+        ).toString('hex');
+      }
packages/kit-bg/src/providers/ProviderApiTon.ts (4)

8-8: Rename lodash isNaN to avoid shadowing the global.

Prevents confusion and lint errors.

-import { get, isEmpty, isNaN } from 'lodash';
+import { get, isEmpty, isNaN as lodashIsNaN } from 'lodash';

And update usages:

-      (isNaN(validUntil) ||
+      (lodashIsNaN(validUntil) ||

50-57: Remove redundant declare from interfaces in a .ts file.

declare is unnecessary here.

-export declare interface ITonAddressItem {
+export interface ITonAddressItem {
@@
-export declare interface ITonProofItem {
+export interface ITonProofItem {

379-406: Use @ton/core for payload/stateInit validation.

Keep parsing consistent with the rest of the codebase.

-      if (message.payload) {
+      if (message.payload) {
         try {
-          void TonWeb.boc.Cell.oneFromBoc(
-            Buffer.from(message.payload, 'base64').toString('hex'),
-          );
+          void Cell.fromBase64(message.payload);
         } catch {
           throw new Web3RpcError(
             TonResponseError.BadRequest,
             'Payload is invalid',
           );
         }
       }
@@
-      if (message.stateInit) {
+      if (message.stateInit) {
         try {
-          void TonWeb.boc.Cell.oneFromBoc(
-            Buffer.from(message.stateInit, 'base64').toString('hex'),
-          );
+          void Cell.fromBase64(message.stateInit);
         } catch {
           throw new Web3RpcError(
             TonResponseError.BadRequest,
             'stateInit is invalid',
           );
         }
       }

423-438: Compare canonical friendly addresses, not raw.

Raw vs friendly will always mismatch.

-      try {
-        const fromAddr = new TonWeb.Address(encodedTx.from);
-        if (
-          fromAddr.toString(false, false, false) !==
-          account.account.addressDetail.baseAddress
-        ) {
+      try {
+        const fromAddr = new TonWeb.Address(encodedTx.from);
+        const canonicalFrom = fromAddr.toString(true, true, false);
+        const canonicalAccount = account.account.addressDetail.baseAddress;
+        if (canonicalFrom !== canonicalAccount) {
           throw new Web3RpcError(
             TonResponseError.BadRequest,
             'Wrong from address',
           );
         }
packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (5)

2-11: Unify TON imports: use @ton/core for Address and SendMode.

Avoid mixing @ton/ton and @ton/core types.

-import {
-  Cell,
-  beginCell,
-  external,
-  fromNano,
-  internal,
-  storeMessage,
-  toNano,
-} from '@ton/core';
-import { Address, SendMode } from '@ton/ton';
+import {
+  Cell,
+  Address,
+  SendMode,
+  beginCell,
+  external,
+  fromNano,
+  internal,
+  storeMessage,
+  toNano,
+} from '@ton/core';

211-215: Use bitwise OR for send-mode flags.

Flags are bitwise, not additive.

-  return max === '1'
-    ? SendMode.CARRY_ALL_REMAINING_BALANCE
-    : SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS;
+  return max === '1'
+    ? SendMode.CARRY_ALL_REMAINING_BALANCE
+    : (SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS);

223-233: Safer StateInit decode: code/data may be absent.

Don’t unconditionally load refs.

-  const initSlice = Cell.fromBase64(stateInit).asSlice();
-  return {
-    code: initSlice.loadRef(),
-    data: initSlice.loadRef(),
-  };
+  const initSlice = Cell.fromBase64(stateInit).asSlice();
+  const code = initSlice.loadMaybeRef();
+  const data = initSlice.loadMaybeRef();
+  return { code: code ?? undefined, data: data ?? undefined };

263-265: Use bitwise OR for send-mode in v4R2 path.

-      sendMode: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS,
+      sendMode: SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS,

245-251: Stop reading walletId from .options and drop TS ignores.

Use the new APIs and proper typings; remove @ts-expect-error/await-thenable suppressions.

-    // @ts-expect-error
-    const walletId = contract?.options?.walletId;
-    const stateInit =
-      // @ts-expect-error
-      // eslint-disable-next-line @typescript-eslint/await-thenable
-      (await contract.createStateInit()) as unknown as StateInit;
+    const walletId = await contract.getWalletId();
+    const stateInit = await contract.createStateInit();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 963977d and 3ff05a9.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (13)
  • apps/mobile/android/gradle.properties (1 hunks)
  • apps/mobile/package.json (1 hunks)
  • package.json (1 hunks)
  • packages/core/package.json (1 hunks)
  • packages/core/src/chains/ton/CoreChainSoftware.ts (3 hunks)
  • packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts (1 hunks)
  • packages/kit-bg/src/providers/ProviderApiTon.ts (7 hunks)
  • packages/kit-bg/src/vaults/impls/ton/KeyringHardware.ts (5 hunks)
  • packages/kit-bg/src/vaults/impls/ton/KeyringHd.ts (2 hunks)
  • packages/kit-bg/src/vaults/impls/ton/KeyringImported.ts (2 hunks)
  • packages/kit-bg/src/vaults/impls/ton/Vault.ts (2 hunks)
  • packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (5 hunks)
  • patches/@ton+core+0.61.0.patch (1 hunks)
🧰 Additional context used
🪛 Biome (2.1.2)
packages/kit-bg/src/providers/ProviderApiTon.ts

[error] 8-8: Do not shadow the global "isNaN" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: lint (20.x)
  • GitHub Check: unittest (20.x)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (8)
apps/mobile/android/gradle.properties (1)

68-68: LGTM: keep AsyncStorage DB at 1024 MB.

No issues.

apps/mobile/package.json (1)

99-99: Add react-native-fast-pbkdf2: OK.

Ensure Android compileSdk property is aligned (see gradle.properties comment). Run pod install for iOS.

package.json (1)

70-75: Bump cross‑inpage‑provider to 2.2.43: looks good.

Packages are in lockstep. Please ensure a single version resolves across workspaces after install.

Additionally, add a resolution for the TON patch (see core comment):

   "resolutions": {
+    "@ton/core": "0.61.0",
     ...
   }

Also applies to: 82-83

patches/@ton+core+0.61.0.patch (1)

1-18: Patch-package safety — add missing patch, lock @ton/[email protected], verify postinstall

  • patches/@ton+core+0.61.0.patch not found — commit the patch under patches/ or fix the patch filename.
  • package.json lists patch-package (v8.0.0) and scripts call patch-package (yarn setup:env && patch-package && yarn copy:inject) — ensure CI and local installs run that step so the patch actually applies.
  • No occurrences of Cell.repr() found in the repo.
  • No explicit "@ton/core": "0.61.0" pin found in package.json — add a dependency or resolutions entry to lock to 0.61.0.
packages/kit-bg/src/vaults/impls/ton/KeyringHd.ts (1)

85-87: LGTM: rawTx uses canonical BOC.

Using toBoc({ idx: false }) avoids index bytes and aligns with HW.

packages/kit-bg/src/vaults/impls/ton/Vault.ts (2)

421-421: Clearer error message.

“Transaction has expired” reads better. 👍


451-465: Good switch to signingMessage and canonical BOC.

Using signingMessage.toBoc({ idx: false }) for body/init is correct and consistent.

packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (1)

254-261: Remove the fromNano→toNano round‑trip; confirm message.amount unit.

toNano(fromNano(message.amount)) is a redundant conversion. If message.amount is nanotons use BigInt(message.amount); if it's TON use toNano(message.amount). Only match found at packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts:257.

Suggested change:

  • value: toNano(fromNano(message.amount)),
  • // if message.amount is nanotons (integer string)
  • value: BigInt(message.amount),

Or auto-detect:
value: message.amount.includes('.') ? toNano(message.amount) : BigInt(message.amount)

import { serializeData, serializeProof } from './sdkTon/tx';

import type { IEncodedTxTon } from './types';
import type TonWeb from 'tonweb';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix type‑only TonWeb import usage.

import type TonWeb + typeof TonWeb... fails type‑checking. Use a module‑type alias.

Apply:

-import type TonWeb from 'tonweb';
+type TonWebModule = typeof import('tonweb');
-      addressEncoding as keyof typeof TonWeb.Wallets.all,
+      addressEncoding as keyof TonWebModule['Wallets']['all'],

Also applies to: 131-134

🤖 Prompt for AI Agents
packages/core/src/chains/ton/CoreChainSoftware.ts around lines 34 and 131-134:
the file currently uses a type-only import "import type TonWeb from 'tonweb';"
and later uses "typeof TonWeb..." which fails because a type-only import has no
runtime value; replace the pattern by creating a module-type alias (e.g., import
type TonWeb from 'tonweb'; type TonWebModule = typeof import('tonweb');) and
then change any "typeof TonWeb" occurrences to "typeof TonWebModule" (or use the
module alias directly in type positions) so the type checker can resolve the
module shape correctly; update the import and all affected type references at
the noted lines.

Comment on lines +31 to +35
// Check number of messages
if (args.messages.length > 4) {
throw Error('Maximum number of messages in a single transfer is 4');
}

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Guard against empty message list.

Wallet transfers with zero messages should reject early.

   // Check number of messages
+  if (args.messages.length === 0) {
+    throw Error('At least one message is required');
+  }
   if (args.messages.length > 4) {
     throw Error('Maximum number of messages in a single transfer is 4');
   }
📝 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
// Check number of messages
if (args.messages.length > 4) {
throw Error('Maximum number of messages in a single transfer is 4');
}
// Check number of messages
if (args.messages.length === 0) {
throw Error('At least one message is required');
}
if (args.messages.length > 4) {
throw Error('Maximum number of messages in a single transfer is 4');
}
🤖 Prompt for AI Agents
In packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts around lines 31
to 35, add a guard that rejects empty message lists before the existing
max-length check: if args.messages is empty (length === 0 or falsy/undefined),
throw an Error indicating at least one message is required for a transfer; keep
the subsequent check for more than 4 messages intact and ensure the new check
runs first to provide a clear early rejection for zero-message transfers.

Comment on lines +54 to +55
return signingMessage;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Return a Cell, not a Builder.

Downstream calls use .toBoc(); returning a Builder will break at runtime.

-  return signingMessage;
+  return signingMessage.endCell();
📝 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
return signingMessage;
}
return signingMessage.endCell();
}
🤖 Prompt for AI Agents
In packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts around lines 54
to 55, the function currently returns a Builder which causes downstream callers
that expect a Cell and call .toBoc() to fail; change the return to the built
Cell by calling the builder's method that finalizes it (e.g.,
endCell()/build()/finish depending on your TON library) and return that Cell so
downstream .toBoc() works.

Comment on lines +145 to 211
if (
!request?.data ||
typeof request?.data !== 'object' ||
request.data === null ||
!('params' in request.data)
) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}

if (!Array.isArray(request?.data?.params)) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}

const [_, connectRequest] = request?.data?.params as [
string,
IConnectRequest,
];

if (!connectRequest.manifestUrl || isEmpty(connectRequest.manifestUrl)) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}

const manifest = await fetch(connectRequest.manifestUrl).then(
async (res) => {
if (res.status !== 200) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
return res.json();
},
);

if (isEmpty(get(manifest, 'name')) || isEmpty(get(manifest, 'url'))) {
throw new Web3RpcError(
TonResponseError.ContentManifest,
'App manifest content error',
);
}
try {
const manifestUrl = new URL(get(manifest, 'url'));
const originUrl = new URL(request.origin ?? '');

if (manifestUrl.host !== originUrl.host) {
throw new Web3RpcError(
TonResponseError.ContentManifest,
'App manifest content error',
);
}
} catch {
throw new Web3RpcError(
TonResponseError.ContentManifest,
'App manifest content error',
);
}

let accounts =
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Harden manifest fetch: validate scheme and add a timeout.

Avoid fetching non-http(s) URLs and hanging requests.

-    const manifest = await fetch(connectRequest.manifestUrl).then(
+    const urlObj = new URL(connectRequest.manifestUrl);
+    if (!/^https?:$/.test(urlObj.protocol)) {
+      throw new Web3RpcError(
+        TonResponseError.InvalidManifestUrl,
+        'App manifest not found',
+      );
+    }
+    const controller = new AbortController();
+    const timer = setTimeout(() => controller.abort(), 10_000);
+    const manifest = await fetch(connectRequest.manifestUrl, {
+      signal: controller.signal,
+    }).then(
       async (res) => {
         if (res.status !== 200) {
           throw new Web3RpcError(
             TonResponseError.InvalidManifestUrl,
             'App manifest not found',
           );
         }
         return res.json();
       },
     );
+    clearTimeout(timer);
📝 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
if (
!request?.data ||
typeof request?.data !== 'object' ||
request.data === null ||
!('params' in request.data)
) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
if (!Array.isArray(request?.data?.params)) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
const [_, connectRequest] = request?.data?.params as [
string,
IConnectRequest,
];
if (!connectRequest.manifestUrl || isEmpty(connectRequest.manifestUrl)) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
const manifest = await fetch(connectRequest.manifestUrl).then(
async (res) => {
if (res.status !== 200) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
return res.json();
},
);
if (isEmpty(get(manifest, 'name')) || isEmpty(get(manifest, 'url'))) {
throw new Web3RpcError(
TonResponseError.ContentManifest,
'App manifest content error',
);
}
try {
const manifestUrl = new URL(get(manifest, 'url'));
const originUrl = new URL(request.origin ?? '');
if (manifestUrl.host !== originUrl.host) {
throw new Web3RpcError(
TonResponseError.ContentManifest,
'App manifest content error',
);
}
} catch {
throw new Web3RpcError(
TonResponseError.ContentManifest,
'App manifest content error',
);
}
let accounts =
if (
!request?.data ||
typeof request?.data !== 'object' ||
request.data === null ||
!('params' in request.data)
) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
if (!Array.isArray(request?.data?.params)) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
const [_, connectRequest] = request?.data?.params as [
string,
IConnectRequest,
];
if (!connectRequest.manifestUrl || isEmpty(connectRequest.manifestUrl)) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
const urlObj = new URL(connectRequest.manifestUrl);
if (!/^https?:$/.test(urlObj.protocol)) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 10_000);
const manifest = await fetch(connectRequest.manifestUrl, {
signal: controller.signal,
}).then(
async (res) => {
if (res.status !== 200) {
throw new Web3RpcError(
TonResponseError.InvalidManifestUrl,
'App manifest not found',
);
}
return res.json();
},
);
clearTimeout(timer);
if (isEmpty(get(manifest, 'name')) || isEmpty(get(manifest, 'url'))) {
throw new Web3RpcError(
TonResponseError.ContentManifest,
'App manifest content error',
);
}
try {
const manifestUrl = new URL(get(manifest, 'url'));
const originUrl = new URL(request.origin ?? '');
if (manifestUrl.host !== originUrl.host) {
throw new Web3RpcError(
TonResponseError.ContentManifest,
'App manifest content error',
);
}
} catch {
throw new Web3RpcError(
TonResponseError.ContentManifest,
'App manifest content error',
);
}
let accounts =
🤖 Prompt for AI Agents
In packages/kit-bg/src/providers/ProviderApiTon.ts around lines 145 to 211, the
manifest fetch should reject non-http(s) schemes and avoid hanging requests;
validate connectRequest.manifestUrl by constructing a URL and ensure its
protocol is either "http:" or "https:" (throw InvalidManifestUrl otherwise),
then perform the fetch using an AbortController with a reasonable timeout (e.g.,
5–10s), clear the timer on completion, and treat non-200 responses or failed
JSON parsing as the same manifest error; also ensure any URL parsing is wrapped
in try/catch so invalid URLs produce the same Web3RpcError.

Comment on lines +2 to +11
import {
Cell,
beginCell,
external,
fromNano,
internal,
storeMessage,
toNano,
} from '@ton/core';
import { Address, SendMode } from '@ton/ton';
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

🧩 Analysis chain

Repo-wide check: eliminate remaining @ton/ton imports and additive send modes.

Search and fix remaining mixed imports and + on SendMode.

Also applies to: 217-221, 366-399


🏁 Script executed:

#!/bin/bash
# Find mixed TON imports
rg -nP -C2 "from\s+['\"]@ton/ton['\"]" --type=ts

# Find additive send-mode combinations
rg -nP --type=ts "SendMode\.[A-Z_]+\s*\+\s*SendMode\.[A-Z_]+" -n

# Verify Address helpers come from @ton/core
rg -nP -C2 "Address\.(parseFriendly|isFriendly)" --type=ts

Length of output: 1663


Eliminate @ton/ton imports and replace additive SendMode uses with bitwise OR

  • Replace "import { Address, SendMode } from '@ton/ton'" with the '@ton/core' equivalents in:

    • packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (line 11)
    • packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts (line 4)
  • Replace additive SendMode uses with bitwise OR in:

    • packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts — lines 214 and 263: use SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS
  • Ensure Address helpers (isFriendly / parseFriendly) are imported from '@ton/core' (utils.ts lines 218–219) and update any other files still importing them from '@ton/ton'.

  • Run a repo-wide search for remaining "from '@ton/ton'" and "SendMode.*+" and fix leftovers.

🤖 Prompt for AI Agents
packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts (imports at lines 2–11,
helpers at 218–219, SendMode usages at 214 and 263): replace "import { Address,
SendMode } from '@ton/ton'" with the equivalents from '@ton/core' (import
SendMode, Address helpers like isFriendly and parseFriendly from '@ton/core' and
remove '@ton/ton' import), change additive SendMode combinations (e.g.
SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS) to bitwise OR
(SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS) at lines 214 and 263,
update any Address helper imports to use isFriendly/parseFriendly from
'@ton/core' at lines ~218–219, and then run a repo-wide search/replace for
remaining "from '@ton/ton'" imports and any "SendMode.*\+" patterns to fix
leftovers (also apply the import change in
packages/core/src/chains/ton/sdkTon/createWalletTransfer.ts line 4).

Comment on lines +271 to +280
stateInit: Cell.fromHex(
Buffer.from(await stateInit.stateInit.toBoc(false)).toString('hex'),
),
init_code: Cell.fromHex(
Buffer.from(await code.toBoc(false)).toString('hex'),
),
init_data: Cell.fromHex(
Buffer.from(await data.toBoc(false)).toString('hex'),
),
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Optional: avoid hex bounce when lifting cells.

You can construct Cells directly from BOC to skip hex encode/decode.

-      stateInit: Cell.fromHex(
-        Buffer.from(await stateInit.stateInit.toBoc(false)).toString('hex'),
-      ),
-      init_code: Cell.fromHex(
-        Buffer.from(await code.toBoc(false)).toString('hex'),
-      ),
-      init_data: Cell.fromHex(
-        Buffer.from(await data.toBoc(false)).toString('hex'),
-      ),
+      stateInit: Cell.fromBoc(await stateInit.stateInit.toBoc(false))[0],
+      init_code: Cell.fromBoc(await code.toBoc(false))[0],
+      init_data: Cell.fromBoc(await data.toBoc(false))[0],
📝 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
stateInit: Cell.fromHex(
Buffer.from(await stateInit.stateInit.toBoc(false)).toString('hex'),
),
init_code: Cell.fromHex(
Buffer.from(await code.toBoc(false)).toString('hex'),
),
init_data: Cell.fromHex(
Buffer.from(await data.toBoc(false)).toString('hex'),
),
};
stateInit: Cell.fromBoc(await stateInit.stateInit.toBoc(false))[0],
init_code: Cell.fromBoc(await code.toBoc(false))[0],
init_data: Cell.fromBoc(await data.toBoc(false))[0],
};
🤖 Prompt for AI Agents
packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts around lines 271 to 280:
the code currently hex-encodes BOC bytes then reparses via Cell.fromHex causing
an unnecessary encode/decode bounce; replace each Cell.fromHex(Buffer.from(await
X.toBoc(false)).toString('hex')) with direct BOC parsing using
Cell.fromBoc(await X.toBoc(false))[0] (or the first element returned) for
stateInit, code and data so you construct Cells directly from the BOC buffers.

Comment on lines +302 to 310
let stateInit: StateInit | undefined;
// Activate Contract
if (encodedTx.sequenceNo === 0) {
// call createStateInit() return Promise<StateInit>
// not call static method createStateInit()
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/await-thenable
const deploy = (await contract.createStateInit()) as StateInit;
stateInit = deploy.stateInit;
stateInit = (await contract.createStateInit()) as StateInit;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Type the API and remove TS ignore in external message build.

Rely on createStateInit(): Promise<StateInit> on IWallet.

-    // @ts-expect-error
-    // eslint-disable-next-line @typescript-eslint/await-thenable
-    stateInit = (await contract.createStateInit()) as StateInit;
+    stateInit = await contract.createStateInit();

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

🤖 Prompt for AI Agents
In packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts around lines 302 to 310,
the code currently suppresses TypeScript with ts-expect-error and ESLint
comments and calls createStateInit potentially as a static method; instead
ensure the contract variable is typed as IWallet (whose createStateInit():
Promise<StateInit> is declared), remove the ts-expect-error and
eslint-disable-next-line lines, and call await contract.createStateInit()
assigning its result to stateInit (typed StateInit | undefined) so the method is
used as an instance async API and type-checked by the compiler.

Comment on lines +323 to +327
ext = external({
to: selfAddress,
init: stateInit ? { code: codeCell, data: dataCell } : undefined,
body,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Tiny cleanup: drop redundant ternary.

Inside the if (stateInit) branch, init is always present.

-    ext = external({
-      to: selfAddress,
-      init: stateInit ? { code: codeCell, data: dataCell } : undefined,
-      body,
-    });
+    ext = external({
+      to: selfAddress,
+      init: { code: codeCell, data: dataCell },
+      body,
+    });
📝 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
ext = external({
to: selfAddress,
init: stateInit ? { code: codeCell, data: dataCell } : undefined,
body,
});
ext = external({
to: selfAddress,
init: { code: codeCell, data: dataCell },
body,
});
🤖 Prompt for AI Agents
In packages/kit-bg/src/vaults/impls/ton/sdkTon/utils.ts around lines 323 to 327,
the ext external call sets init using a redundant ternary (init: stateInit ? {
code: codeCell, data: dataCell } : undefined) even though this call is only made
inside an if (stateInit) branch; remove the ternary and pass init directly as {
code: codeCell, data: dataCell } to simplify the code and avoid the unnecessary
conditional.

@ByteZhang1024 ByteZhang1024 marked this pull request as draft September 18, 2025 02:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants