-
Notifications
You must be signed in to change notification settings - Fork 435
Fix/ton issue OK-42883 #8383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: x
Are you sure you want to change the base?
Fix/ton issue OK-42883 #8383
Conversation
WalkthroughMigrates 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
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
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
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Pre-merge checks and finishing touches❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Comment |
🎉 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) |
f099b0f
to
062f7b9
Compare
No dependency changes detected. Learn more about Socket for GitHub. 👍 No dependency changes detected in pull request |
There was a problem hiding this 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.
⛔ 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 withas 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 becomesany
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*' || truepackages/kit-bg/src/vaults/impls/ton/Vault.ts (1)
451-465
: Verify fee estimators accept signingMessage BOCYou 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
const signingMessage = Cell.fromHex(rawTxUnsigned); | ||
const hash = signingMessage.hash(); | ||
const [signature] = await signer.sign(Buffer.from(hash)); |
There was a problem hiding this comment.
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.
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'; |
There was a problem hiding this comment.
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.
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.
export function createWalletTransferV4<T extends IWalletV4BasicSendArgs>( | ||
args: T & { sendMode: number; walletId: number }, | ||
) { |
There was a problem hiding this comment.
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.
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(), |
There was a problem hiding this comment.
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.
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.
return max === '1' | ||
? SendMode.CARRY_ALL_REMAINING_BALANCE | ||
: SendMode.PAY_GAS_SEPARATELY + SendMode.IGNORE_ERRORS; | ||
}; |
There was a problem hiding this comment.
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.
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.
const initSlice = Cell.fromBase64(stateInit).asSlice(); | ||
return { | ||
code: initSlice.loadRef(), | ||
data: initSlice.loadRef(), | ||
}; | ||
}; |
There was a problem hiding this comment.
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.
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.
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, | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
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, | ||
}); |
There was a problem hiding this comment.
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.
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, | |
}); |
062f7b9
to
963977d
Compare
There was a problem hiding this 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-35packages/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 -Spackages/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 redundantdeclare
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.
⛔ 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"
fiecho
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' || trueecho
echo "Searching for 'signning' or 'signing' tokens (broader):"
rg -nC2 -uu --hidden --no-ignore-vcs -P '\bsignning\b|\bsigning\b' || trueecho
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."
fiecho
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).
for (const m of args.messages) { | ||
signingMessage.storeUint(args.sendMode, 8); | ||
signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m))); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
// 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', | ||
); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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.
// 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', | |
); | |
} | |
} | |
} |
/* 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'; |
There was a problem hiding this comment.
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.
/* 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.
963977d
to
3ff05a9
Compare
There was a problem hiding this 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 addcreateStateInit(): 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 || truepackages/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 redundantdeclare
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.
⛔ 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'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
// Check number of messages | ||
if (args.messages.length > 4) { | ||
throw Error('Maximum number of messages in a single transfer is 4'); | ||
} | ||
|
There was a problem hiding this comment.
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.
// 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.
return signingMessage; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
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 = |
There was a problem hiding this comment.
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.
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.
import { | ||
Cell, | ||
beginCell, | ||
external, | ||
fromNano, | ||
internal, | ||
storeMessage, | ||
toNano, | ||
} from '@ton/core'; | ||
import { Address, SendMode } from '@ton/ton'; |
There was a problem hiding this comment.
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).
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'), | ||
), | ||
}; |
There was a problem hiding this comment.
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.
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.
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; | ||
} |
There was a problem hiding this comment.
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.
ext = external({ | ||
to: selfAddress, | ||
init: stateInit ? { code: codeCell, data: dataCell } : undefined, | ||
body, | ||
}); |
There was a problem hiding this comment.
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.
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.
OneKeyHQ/cross-inpage-provider#406
Summary by CodeRabbit
New Features
Improvements
Chores