Skip to content

fix(wallet): normalize valid_until timestamp in TON sendMessage#973

Open
ignaciosantise wants to merge 1 commit intomainfrom
ton-valid-until-fix
Open

fix(wallet): normalize valid_until timestamp in TON sendMessage#973
ignaciosantise wants to merge 1 commit intomainfrom
ton-valid-until-fix

Conversation

@ignaciosantise
Copy link

Summary

  • Fix "bitLength is too small" error when dApps send valid_until in milliseconds
  • Add normalizeValidUntil() helper to convert milliseconds to seconds when needed
  • Update validation to normalize timestamp before checking expiration

Problem

The valid_until parameter in ton_sendMessage requests may be sent in milliseconds by some dApps, but TON uses 32-bit timestamps (seconds). Values over ~4.3 billion overflow, causing "bitLength is too small" errors.

Solution

Detect millisecond timestamps by checking if the value exceeds 10 billion (year 2286 in seconds), and convert to seconds when needed. This normalization is applied both in validation and when passing to createTransfer.

Test plan

  • Build the project to ensure no TypeScript errors
  • Test sending a TON message with a millisecond timestamp (e.g., Date.now()) - should work without overflow error
  • Test sending a TON message with a second timestamp - should still work correctly
  • Test that expired messages are still rejected

🤖 Generated with Claude Code

The valid_until parameter in ton_sendMessage requests may be sent in
milliseconds by some dApps, but TON uses 32-bit timestamps (seconds).
Values over ~4.3 billion overflow, causing "bitLength is too small" errors.

Changes:
- Add normalizeValidUntil() helper to convert ms to seconds when needed
- Update validation to normalize before checking expiration
- Use normalized value in createTransfer timeout parameter

The threshold of 10 billion seconds (year 2286) safely distinguishes
between milliseconds and seconds timestamps.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 30, 2026 16:14
@vercel
Copy link

vercel bot commented Jan 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
decentralized-relay-app Error Error Jan 30, 2026 4:19pm
decentralized-relay-wallet Ready Ready Preview, Comment Jan 30, 2026 4:19pm
pos-dapp Ready Ready Preview, Comment Jan 30, 2026 4:19pm
react-wallet-v2 Ready Ready Preview, Comment Jan 30, 2026 4:19pm
wallet-pay-dapp Error Error Jan 30, 2026 4:19pm
8 Skipped Deployments
Project Deployment Actions Updated (UTC)
appkit-react-wagmi-example Ignored Ignored Jan 30, 2026 4:19pm
appkit-solana Ignored Ignored Jan 30, 2026 4:19pm
chain-abstraction-demo Ignored Ignored Preview Jan 30, 2026 4:19pm
malicious-dapp-verify-simulation Ignored Ignored Preview Jan 30, 2026 4:19pm
react-dapp-v2 Ignored Ignored Preview Jan 30, 2026 4:19pm
react-dapp-v2-cosmos-provider Ignored Ignored Preview Jan 30, 2026 4:19pm
react-dapp-v2-with-ethers Ignored Ignored Jan 30, 2026 4:19pm
smart-sessions-demo Ignored Ignored Jan 30, 2026 4:19pm

Request Review

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes TON ton_sendMessage failures when dApps send valid_until in milliseconds by normalizing timestamps to seconds before validation and transfer creation.

Changes:

  • Normalize valid_until from milliseconds to seconds when it exceeds a threshold.
  • Apply normalization in both expiration validation and createTransfer timeout.
  • Minor formatting tweak in signData payload (split(':')).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +124 to +128
// Normalize to seconds if milliseconds were provided
const validUntilSeconds =
params.valid_until > 10_000_000_000
? Math.floor(params.valid_until / 1000)
: params.valid_until
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The valid_until normalization logic is duplicated here and again in normalizeValidUntil(). To avoid the two implementations drifting (threshold, rounding, etc.), reuse the helper inside validation (e.g., derive validUntilSeconds via this.normalizeValidUntil(...) and then validate against that).

Suggested change
// Normalize to seconds if milliseconds were provided
const validUntilSeconds =
params.valid_until > 10_000_000_000
? Math.floor(params.valid_until / 1000)
: params.valid_until
const validUntilSeconds = this.normalizeValidUntil(params.valid_until)

Copilot uses AI. Check for mistakes.
? Math.floor(params.valid_until / 1000)
: params.valid_until

if (validUntilSeconds < Date.now() / 1000) {
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

The expiration check compares an integer validUntilSeconds to Date.now() / 1000 (a fractional seconds value). This will treat valid_until = Math.floor(Date.now()/1000) as expired for most of that second. Elsewhere in this file timestamps use Math.floor(Date.now() / 1000) (e.g., signData), so consider flooring the current time here as well for consistent second-level semantics.

Suggested change
if (validUntilSeconds < Date.now() / 1000) {
if (validUntilSeconds < Math.floor(Date.now() / 1000)) {

Copilot uses AI. Check for mistakes.
Comment on lines +210 to +218
private normalizeValidUntil(validUntil?: number): number | undefined {
if (validUntil === undefined) {
return undefined
}
// If value > 10 billion, it's likely milliseconds (year 2286+ in seconds)
if (validUntil > 10_000_000_000) {
return Math.floor(validUntil / 1000)
}
return validUntil
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

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

normalizeValidUntil() (and the corresponding validation) accepts any number, including NaN, Infinity, or non-integers, which can silently pass validation and then be forwarded as timeout to createTransfer. Consider rejecting non-finite values (and, if required by the TON SDK, non-integer values) via Number.isFinite(...) (and possibly Number.isInteger(...)) before returning/using the timestamp.

Copilot uses AI. Check for mistakes.
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.

1 participant