90 Commits
Author SHA1 Message Date
Jules 838d20c153 fix(wallet): recover from stuck transactions instead of breaking the session (#1316)
* fix(wallet): poll receipts on the node that took the broadcast

Chain 1 now proxies to the same `/alchemy` path `nodes.broadcastTransaction`
and `nodes.getNonce` use. A node that does not hold the transaction answers
receipt lookups with null forever.

* fix(wallet): only commit the nonce on a validated broadcast

The result was read unvalidated, so a broadcast that produced no transaction
still burned its nonce and gapped every later one. Failures now carry the
node's own reason instead of the status code.

* fix(wallet): reconcile the nonce down against an empty pool

Nothing lowered the local counter, so one dropped transaction left every
later one signed at a gapped nonce until the browser restarted. When the
node's pending and latest counts agree it holds nothing for the account, so
a counter above that is lowered once it has stayed ahead for 5 blocks.

* feat(wallet): detect and notify dropped transactions

A never-mined transaction kept its 30s alarm forever, since only receipt
`0x1` and `0x0` settled it. `eth_getTransactionByHash` is now the probe: a
null receipt means nothing on its own, but three consecutive polls where the
node has no record of the hash settle it as dropped. RPC failures do not
count towards that.

* fix(wallet): sync pending transactions from the background monitor

The page read the list once at mount and wrote its own copy back over the
monitor's removals, leaving settled transactions pending. It now watches the
key it shares with the monitor.

* fix(wallet): keep the earliest mark for a pending count

The post-send mark overwrote the block a held one carried, so every send
from the address restarted the 5-block window and the counter was never
lowered -- each retry only added another gapped transaction. `resolveNonce`
now reports when a mark for the pool count is already held.
2026-08-27 13:21:36 +00:00
Jules f7fc111115 fix(wallet): stop underpricing transactions in the fee estimator (#1315)
* fix(wallet): read gasUsedRatio as a float

`eth_feeHistory` returns it as a float, so `parseInt(hex)` stopped at the
decimal point and `averageGasUsedRatio` was always 0, pinning the ETA to
the idle branch. Also guard the empty-history average against NaN.

* fix(wallet): floor the tx tip and raise the fee ceiling

Tip is now max(eth_maxPriorityFeePerGas, feeHistory p50, 1 gwei); the
suggestion alone ran ~270x under the median tip paid and `0x0` was
signable. Ceiling goes 2x -> 3x base fee, ~14 -> ~23 blocks of headroom
before the tx becomes unincludable.

* fix(wallet): lower the tip floor to 0.1 gwei

1 gwei sat ~16x over the median tip paid at a ~0.11 gwei base fee, so it
set the quoted price rather than backstopping it. Only quiet markets
change: above 1 gwei the tip is max(suggestion, p50) either way, leaving
congested sends and LiFi swaps untouched.
2026-08-27 13:21:36 +00:00
Jules 630fe059e5 feat(wallet): route dApp reads to the chain the dApp is on (#1303)
* feat(wallet): add a chain registry

One source for chain id, name, icon, viem chain, proxy route and whether
sends are possible, replacing three lists that disagreed: the switchable
set in rpc/chain.ts, the mainnet pin in public-client.ts and the
name/icon maps in the approval popup.

No upstream proxy path is known for Status Network Sepolia, so it ships
with proxyChainId null.

* refactor(wallet): resolve chains through the registry

Drops the duplicate SUPPORTED_CHAIN_IDS set, the SIGNABLE_CHAIN_ID
constant and the CHAIN_NAMES/CHAIN_ICONS maps.

Chain ids are matched by value rather than by string, so 0x6300B5EA and
0x6300b5ea stop being two chains, and the registry's spelling is what
gets stored.

* feat(wallet): route dApp reads to the origin's chain

Forwarded reads went to a module-level mainnet client whatever chain the
dApp had switched to. getPublicClient memoizes one client per chain over
that chain's proxy route.

A chain with no route is refused with 4901, not 4902: it is recognised
and switchable, so 4902 would invite an add-and-retry that succeeds and
then fails the same way.

* test(wallet): cover multi-chain read routing

* chore(wallet): add changeset

* chore(wallet): drop redundant comments

* refactor(wallet): convert the origin chain id through the registry

The typed-data chain check used parseInt, which toChainId exists to
replace: it reads a bare '1' or a trailing-garbage '0x1zzz' as mainnet,
so a malformed id would compare equal to a chain it is not. Nothing
reaches the check with a non-canonical id today, so this is the
invariant landing in one place rather than a behaviour change.

The mismatch message now names the chain, falling back to the raw hex
so an unrecognised id does not print as NaN.
2026-08-21 19:53:23 +09:00
Jules c389fd9465 feat(wallet): support eth_sendTransaction for dApps (#1302)
* refactor(wallet): extract the transaction send policy

Moves the fee backfill, the LiFi quoted-priority rule, the EIP-1559 clamp
and the ERC20/contract/native branch out of signer-context so the service
worker can reach the same policy. Transports are injected: the page holds a
tRPC proxy client, the worker a caller.

requestFeeRate moves to lib/gas-fees.ts rather than being exported from
use-gas-fees.ts, which would drag React, react-query and ethers into the
background bundle.

No behavioural change.

* refactor(wallet): share the dApp signer guards

requireOriginAddress, requireWalletFor and assertNoPendingApproval were
private to sign.ts; eth_sendTransaction needs the same three.

* feat(wallet): send transactions for dApps

Replaces the 4200 stub. Fenced to mainnet, since nodes.getFeeRate,
broadcastTransaction and getNonce all pin z.enum(['ethereum']).

The fee is estimated once, before the popup, and the resolved values are
fed back into the send, so what the user approves is what gets broadcast.
Estimation always runs: it is also where a reverting call is caught.

'from' is checked against the origin's pinned account, not the wallet's
selection. Omitted 'value' defaults to 0; contract deployment is refused
by name rather than failing zod server-side.

* test(wallet): cover dApp transaction sending

* chore(wallet): add changeset

* fix(wallet): route only re-encodable transfers through sendErc20

`sendErc20` does not sign the calldata it is handed: it reads the
recipient and amount back out and re-encodes an `erc20Transfer`, a shape
carrying no ETH value. Dispatching on the 4-byte selector alone therefore
dropped every byte past the amount word, and the value entirely, after
the popup had already displayed both -- the signed transaction was not
the approved one.

Take that route only for a transfer that survives the round trip
byte-for-byte: canonical length, zero value, zero-padded recipient word.
Everything else goes through `sendContractCall`, which signs the calldata
verbatim and carries the value.

No delta on the paths that reach this through the wallet itself: the
in-wallet token send calls `sendErc20` directly, and the LiFi widget only
ever emits `approve` and router calldata.

* fix(wallet): refuse dApp transaction fields that cannot be honoured

The handler read a fixed set of fields and dropped the rest, so a request
could be signed as something other than what it asked for while the popup
still showed the original.

`nonce` is the sharp one: `nonceTracker` assigns its own, so a resubmit
meant to replace or cancel a pending transaction would have broadcast as
an additional spend at the next nonce. `gasPrice` has no route to the
backend, which takes only the EIP-1559 pair, and was being swapped for
our own estimate. A non-empty `accessList` changes gas semantics and was
not carried into the signing input.

`type` stays accepted -- every transaction we sign is Enveloped, so a
dApp asking for `0x2` is describing what it already gets.

* fix(wallet): accept input as an alias for data, as geth does

Some libraries send calldata only as `input`. Reading `data` alone left
it undefined, turning a contract call into a bare ETH transfer to the
contract address -- the dApp's call silently did nothing it asked for.

Two values that disagree is geth's error case rather than a precedence
rule: there is no way to tell which one the dApp meant.
2026-08-21 19:53:23 +09:00
Jules fd96d31c95 feat(wallet): sign EIP-712 typed data for dApps (#1301)
* refactor(wallet): drive the approval popup from the request type

A single `isSign` boolean set the title, the button label and the content
branch, so adding an approval type would have rendered it as "Connect dApp".
An exhaustive switch makes that a compile error instead.

* feat(wallet): sign EIP-712 typed data for dApps

`eth_signTypedData_v4` threw 4200. It now signs through the same per-origin
pinning and approval path as `personal_sign`.

Parameters are swapped relative to `personal_sign` -- address first, payload
second, per status-go `commands/sign.go`. The payload is parsed and validated
before the popup opens, `domain.chainId` included, so malformed input cannot
reach viem as an opaque -32603 after the user has already approved. The popup
renders it as hostile input, capped by depth, row count and value length.

* test(wallet): cover typed data signing

The swapped parameter order, malformed payloads refused before the popup, the
domain chain check across the three spellings dApps use, and the display caps.

* chore(wallet): add changeset

* fix(status.app): lint with eslint instead of next lint

`next lint` injects eslint-config-next, whose absolute-path `import/resolver`
entries replace the repo's own. Under pnpm's non-hoisted layout they resolve
from the config rather than the linted file, so an app-local dependency such as
`entities` reports as unresolved while root-hoisted ones do not.

The flat config already registers @next/next, so the Next rules still run.
`next lint` is deprecated and removed in Next 16.

* fix(wallet): render typed data rows from the signed type

Only the fields declared in types[primaryType] reach the EIP-712 hash.
Walking the raw message let a dApp pad it with unsigned keys until the
fields that are actually signed fell past the row cap, unseen. The
domain header had the same hole: a dApp-declared EIP712Domain decides
what the separator covers, so a name left out of it was displayed as
the dApp's identity without being signed.

* fix(wallet): bind the typed-data chain check to the signed domain

A dApp declaring its own EIP712Domain decides which domain fields reach
the separator, so a chainId present in domain but absent from that type
satisfied the chain check while binding the signature to no chain.

* test(wallet): cover the derived EIP712Domain chain check
2026-08-21 19:53:22 +09:00
Jules ef29a2a336 fix(wallet): gate dApp RPC on granted permission (#1299)
* feat(wallet): add status-go remote method allowlist

The 35 methods status-go forwards to the node, plus the local ones it
answers before a dApp connects. Transcribed from remote.go at 384a179.

* fix(wallet): gate dApp RPC on granted permission

The handler ended in a default: branch forwarding any unrecognised method
to the node for any origin, connected or not. Dispatch now mirrors
status-go's CallRPC: local registry, remote allowlist, then -32601.

Method bodies move to lib/rpc/ and rpc-handler.ts keeps a handler map, so
the gated set cannot drift from the implemented one.

* test(wallet): port status-go connector permission table

* chore(wallet): add changeset

* fix(wallet): notify the origin when a dApp revokes over RPC

`wallet_revokePermissions` dropped the grant without telling anyone, so
the calling page -- and every other tab on the origin -- kept showing the
account until a reload. It now goes through `disconnectDapp`, the same
revoke-and-notify pair the wallet's own Disconnect action uses, so there
is one revoke path rather than two that can drift.

`broadcast` swallows a failing `tabs.query`. The notification happens
after the permission is already deleted, so a push failure must not
surface to the dApp as a failed revoke.

* fix(wallet): keep the caveats a dApp asked for in wallet_getPermissions

The `eth_accounts` branch replaced the whole caveat list with the derived
`restrictReturnedAccounts`, so anything a dApp had stored through
`wallet_requestPermissions` was persisted but never reported back.

Only that one caveat is derived now; the rest are passed through. A
stored `restrictReturnedAccounts` is discarded rather than merged -- a
dApp can write one, and it must not shadow the account the wallet will
actually return.
2026-08-21 19:53:22 +09:00
Jules 3051af7796 fix(wallet): persist dApp connections per account (#1298)
* fix(wallet): keep dApp connections until explicitly revoked

Move permissions from chrome.storage.session to local, keyed per origin and
per account. A browser restart, a page reload or an account switch no longer
drops a connection -- only wallet_revokePermissions or the wallet's own
Disconnect action does.

Each origin is pinned to the account it was connected with, so switching
accounts exposes the new account only to dApps already connected to it and
pushes accountsChanged to their tabs. eth_accounts, eth_requestAccounts and
personal_sign all resolve through that pinned account.

* fix(provider): stop tearing down the dApp session on reload and errors

A fresh provider restores its session from eth_accounts rather than reporting
itself disconnected, close() no longer revokes the stored permission, and an
RPC error whose message reads "dApp is not permitted by user" no longer
disconnects. isConnected() reports transport readiness per EIP-1193 instead
of account state.

Adds a window listener for accountsChanged pushed by the wallet, so an
account switch reaches the page without a reload.

* feat(wallet): add dApp connections menu

Lists connected dApps behind their own trigger beside the wallet selector.
Each entry shows the account that dApp sees, offers Connect for the selected
account when it is not connected there, and Disconnect.

* fix(wallet): stop the approval popup rendering an empty window

The mount effect awaited the pending approval and the session status with no
rejection handling, so either one failing left the popup at `return null` --
a white window with no way out. A cold service worker rejecting the first
port message is enough to trigger it. Both reads are now guarded, and a
missing request renders an explicit "no longer available" state.

Claim the single approval slot synchronously as well. The stored record is
read asynchronously, so two requests in the same tick both saw it empty and
each opened a popup, then overwrote each other's record.

* fix(wallet): count approval popups in the mock instead of casting

The spy cast an async wrapper to the overloaded chrome.windows.create type,
which tsc rejects. The mock already owns that call, so it counts there.

* chore(wallet): add changeset
2026-08-21 19:53:21 +09:00
jinhojang6 6a58c042bb fix(wallet): fit onboarding layout to shell height to avoid clipping 2026-07-28 22:24:10 +09:00
jinhojang6 2d1ade5e35 fix(wallet): make onboarding card scrollable so long word grids stay reachable 2026-07-28 22:24:10 +09:00
jinhojang6 816bfcde18 feat(wallet): recovery phrase word grid input 2026-07-28 22:24:10 +09:00
Jules dc2703dbca feat(wallet): create account from the wallet selector
* fix(wallet): preserve trpc error messages over the chrome transport

trpc-chrome posts error shapes verbatim but its link deserializes them
with the configured transformer; with superjson every error collapsed to
"Unknown error". Pre-serialize the shape in errorFormatter so messages
round-trip.

* feat(wallet): extract derivation path helpers

Add pathAtIndex and nextDerivationPath so the next sequential path can
be computed without deriving keys; cover with unit tests.

* feat(wallet): add and preview derived accounts by wallet id

Extend wallet.account.ethereum.add with an optional custom derivation
path, a mnemonic-only guard, duplicate rejection, and selection of the
new account. Add wallet.account.ethereum.preview to derive an address
(next sequential or custom path) without persisting; the mnemonic stays
in the background session.

* feat(wallet): create account flow from the wallet selector

New /wallet-flow/add-account route: prefilled next sequential derivation
path, editable to a custom path, with live address and activity preview
before confirming. Entry point in the wallet selector dropdown, mnemonic
wallets only.

* chore(wallet-extension): make create account button full width

* fix(wallet): restore mobile wallet selector interaction

Allow clicks to pass through the transparent sticky header until its controls are visible.
2026-07-21 23:37:36 +09:00
Jules ea68132627 feat(wallet): mnemonic account discovery and selection
* feat(wallet): discover active accounts on mnemonic import

Scan successive derivation paths through rpc.proxy (transaction count +
balance) with a BIP-44 gap limit of 20, capped at index 100. Account 0 is
always imported. wallet.import accepts explicit derivationPaths; new
wallet.discoverAccounts and wallet.previewAccount endpoints support
reviewing accounts before import.

* feat(wallet): account selection step in import flow

Show discovered accounts with their activity status and allow adding
custom derivation paths before importing. Falls back to the default path
when the scan finds nothing or fails.

Discovery is triggered from the mnemonic submit handler, not a mount
effect: react-query's MutationObserver detaches from an in-flight
mutation under StrictMode's simulated unmount, leaving the UI pending
forever.

* fix(wallet): send plain JSON-RPC to rpc.proxy in tx monitor

The API route rejects tRPC-style bodies with an opaque 500, so receipt
polling silently never resolved and tx notifications never fired.

* test(e2e): handle account selection step in wallet onboarding

* feat(wallet): persist selected account for a wallet

Add setSelectedAccount metadata helper and wallet.account.select tRPC mutation.

* feat(wallet): account selector in wallet menu

Add account switcher to the wallet selector dropdown and wire currentAccount to the persisted selection via useSelectAccount.

* fix(wallet): scroll long account list in import step

Cap the discovered-accounts list height and scroll it so the custom derivation path input and Continue button stay reachable.

* chore: add changeset
2026-07-16 02:41:14 +09:00
Jules 21095a44d2 fix(wallet): honor LiFi-quoted fees when signing swap transactions
* fix(wallet): honor LiFi-quoted fees when signing swap transactions

Externally priced transactions (LiFi swaps) are trusted for every fee
field they provide. When the quote carries maxPriorityFeePerGas but the
SDK strips maxFeePerGas, derive the ceiling from the quoted priority
plus the estimator's base-fee headroom instead of mixing it with the
internal estimate, which could sign an invalid EIP-1559 tx
(priority > maxFee) rejected at broadcast. Wallet-originated
transactions still use internal estimation for all fields.

* test(e2e): cover swap with quote-time LiFi priority fee

Add a quoted-priority knob to the LiFi mock, expose it as a fixture,
and assert a swap quoted above the wallet's internal fee ceiling still
signs a valid tx that pays the quoted priority.

* chore: add changeset
2026-07-10 20:03:46 +09:00
7d880ebd96 feat(wallet): extract ScannerScreen and AccountSelect to packages/wallet (#1152)
* feat(wallet): extract ScannerScreen and AccountSelect to packages/wallet

* chore: remove debug information

* feat(wallet): use backButtonProps render prop in ScannerScreen

---------

Co-authored-by: Jules <78474837+JulesFiliot@users.noreply.github.com>
Co-authored-by: Jinho Jang <41753422+jinhojang6@users.noreply.github.com>
2026-05-18 16:02:17 +09:00
Jules 9e8d0fc75b feat(wallet): add token value chart
* chore(wallet): re-enable token value chart

* chore: add changeset

* feat(wallet): append latest spot and balance chart points

Ensure token charts include current spot price and current balance snapshots so rightmost points reflect fresh data.

* chore(wallet): align wallet query stale defaults

Set wallet fallback and token chart query stale windows to one minute to keep list and token views refreshed on the same cadence.

* feat(wallet): refresh token chart queries from toolbar

Include active price and balance chart queries in manual wallet refresh so token view latest values update immediately.

* chore(wallet): decrease short RPC requests rate-limit

From 30 RPM allowed to 40 RPM allowed

* chore(wallet): clean magic numbers in asset-chart

* feat(wallet): extract buildCanonicalTimestamps utility for uniform chart grid

* fix(wallet): anchor balance history grid at now and walk backward

Replaces the forward-walking timestamp loop (which had an uneven final gap
patched by pushing currentTime at the end) with buildCanonicalTimestamps.
The grid is now anchored at anchorTime (currentTime || now) and walks
backward in fixed intervals, so every gap is identical and the first point
is always exactly the anchor time.

* feat(wallet): add server-side tokenValueChart and nativeTokenValueChart procedures

Computes value (price x balance) on the canonical timestamp grid server-side,
replacing the client-side binary search in useValueChartData. Both procedures
share the same grid as the balance chart, so all three chart types have
homogeneous time gaps.

Key changes:
- Add tokenValueChart / nativeTokenValueChart async functions with shared
  buildStepBeforeLookup and buildValuePoints helpers
- Register both as tRPC procedures on assetsRouter
- Remove LATEST_BALANCE_POINT_OFFSET_MS hack and the duplicate
  getERC20TokensBalance / getNativeTokenBalance calls that appended a
  current-balance point at now-60s
- First point always uses live spot price + balance (same sources as
  assets.all) to prevent drift with AssetsList

* feat(wallet): query server-side value chart in AssetChart; remove useValueChartData

Adds a dedicated valueChart query that calls assets.tokenValueChart or
assets.nativeTokenValueChart. Loading and error states for the value tab now
derive solely from that query. Removes useValueChartData and the client-side
price x balance computation it performed.

* fix(wallet): forward-fill price lookup prefix to avoid $0 first value point

The canonical timestamp grid for value charts can extend slightly before the
earliest sample returned by CoinGecko's price history. In that case the
step-before lookup returned 0 for the oldest timestamp, producing a spurious
$0 value at the left edge of the chart. Fall back to the earliest known value
when the query precedes all data points.

* fix(wallet): show "No value" empty state only when address never held the asset

The value chart endpoints now return [] when no transfers exist at any point
in the address's history (mirroring the Balance chart's behavior). For any
asset the address has held at some point, every timeframe renders a full
chart, even if the value sits at 0 for that window.

* fix(packages/wallet): force-cache causing priceChart to return stale data

force-cache was causing `tokenPriceChart`/`nativeTokenPriceChart` calls to return stale data (for multiple days!). Using revalidate only still ensures coingecko price history data (`fetchTokenPriceHistory`) to be correctly cached for the revalidation time duration (1 hour).
2026-05-12 16:25:06 +09:00
Jules ab7a9fe4c9 fix(wallet): pending-then-dropped transactions
* refactor(wallet): extract padHex to shared package

Move the hex-padding utility out of ethereum.ts into @status-im/wallet/utils
so it can be consumed by both the signing layer and the nonce tracker.

* refactor(wallet): extract tRPC helpers to shared utils module

Move buildTrpcUrl, fetchTrpcData, and extractTrpcErrorMessage from
token-helpers.ts into apps/wallet/src/utils/trpc.ts so they can be
imported by any module without pulling in token-specific logic.

* fix(wallet): replace ad-hoc nonce tracker with mutex-scoped reservation

The old localNonceTracker bumped the nonce before broadcast, so a failed
broadcast left the counter permanently ahead, breaking every subsequent tx
until the extension reloaded. Concurrent dApp-bridge requests also raced on
the same counter and could both use the same nonce.

Replace it with a per-address mutex (promise-chain) that only commits the
incremented nonce after the broadcast callback resolves. Nonces are persisted
to chrome.storage.session via @wxt-dev/storage so the counter survives
popup reloads without losing progress.

* feat(wallet): add gas fees hook with polling and pre-sign refresh

Extract gas fee fetching into useGasFees with a 12s refetch interval and 8s
stale time so estimates stay current while the user reviews the modal.

Add checkAndRefreshGasFees(), called at sign time, which forces a fresh fetch
and returns the latest params. This ensures the broadcast always uses
up-to-date maxFeePerGas/maxPriorityFeePerGas regardless of how long the
user spent on the confirmation screen.

* feat(wallet): gate large gas price shifts with user confirmation

If gas rises >=30% between estimate and sign, checkAndRefreshGasFees throws
GasShiftedError. The send modal catches it, enters a 'gas-shifted' state, and
shows an inline warning with a 'Confirm with new gas price' button. A second
click resumes signing with the fresh params.

Smaller gas bumps (<30%) are accepted silently — the broadcast proceeds with
the refreshed values without user interaction.

* feat(wallet): expose imperative fetchGasFees from useGasFees hook

Stable callback for non-React callers to fire a one-off gas-fee
request without driving the reactive query. Accepts a data field
for arbitrary contract calls.

* refactor(wallet): replace 3-fetch gas fallback with single nodes.getFeeRate call

Drop the eth_getBlockByNumber + eth_maxPriorityFeePerGas + eth_estimateGas
trio in signAndSendTransaction in favor of one fetchGasFees call from the
useGasFees hook. Caller-supplied fee fields are now preserved per slot
via ??= instead of being overwritten as a pair.

* chore: add changeset

* feat(wallet): restore configurable gas limit buffering

Restore the 10% gas-limit safety margin via useGasFees (replacing the behavior removed from signer-context) and allow callers to disable it for native ETH transfers.
2026-05-11 14:29:22 +09:00
Jinho Jang e88747ec79 Import hardware wallet via QR (#1093)
* feat(wallet): import hardware wallet via QR (ERC-4527)

* refactor(wallet): clean up hardware-wallet import route

* fix(wallet): update layout of confirm import section for better alignment

* refactor(wallet): replace custom QR scanner with @qrkit/react hooks

* feat(wallet): add jsqr dependency for QR code scanning functionality

* refactor(wallet): remove unused QR code dependencies and improve hardware wallet import tests

* chore(wallet): upgrade @qrkit to 0.4.1 and drop secp256k1 override

* chore: update @qrkit/core and @qrkit/react to version 0.4.1 and remove deprecated dependency

* feat: add WatchOnlyActionTooltip component and integrate it into Token component for watch-only wallet handling

* chore: update onboarding documentation for hardware wallet compatibility and supported devices

* feat(wallet): enhance hardware wallet import with validation and error handling

* refactor(wallet): extract hardware wallet flow and WatchOnlyTag component

* feat(wallet): add hardware wallet import from wallet dropdown

* feat: upgrade @qrkit/core version and refactor wallet flow

* fix: update @qrkit/react version to ^0.5.0

* feat(wallet): implement password requirement for first hardware wallet import

* fix(wallet): improve vault existence check for hardware wallet import
2026-05-01 15:23:38 +09:00
Jules 242931b25c fix(wallet): use our custom RPC proxy instead of public RPC
* chore(wallet): update li.fi widget

* fix(wallet): use our custom RPC proxy instead of public RPC

Many JSON RPC were made to a public API instead of our private RPC API. This included the ones made by Li.Fi, which was causing rate-limiting on the widget.

This commmit also factorizes the use of viem PublicClient to not instantiate more than needed.

* fix(wallet): handle batch JSON-RPC requests

* refacor(wallet): create singleton for viem publicClient

* chore: add changeset

* chore(wallet): move extractTxHash to package

Factorize all get tx hash operations to the package wallet and extract eth tx hash check to package as well.

* chore(wallet): replace `extractTxHash` by `getTransactionHash`

`getTransactionHash` is more robust and exported form the wallet package. Import `getTransactionHash` from the same location everywhere and remove re-exports. Delete `tx-helpers.ts` since not used anymore.

* fix(wallet/build): revert back to relative import
2026-04-27 11:56:52 +09:00
Jinho Jang 647e9ec794 fix: restore SettingsIndexRoute in RootRouteChildren and update import path for NETWORK_TO_CHAIN_ID (#1138) 2026-04-23 01:38:27 +09:00
Jinho Jang af761a6994 Add browser notifications for wallet transactions (#1095)
* feat(wallet): add notifications permission to manifest

* feat(wallet): add Chrome notifications helper

* fix(wallet): await chrome.notifications.create and fix vi.mock ordering

* feat(wallet): add background transaction monitor

* feat(wallet): register tx-monitor alarm in background service worker

* fix(wallet): cast storage mock through unknown for TypeScript

* feat(wallet): notify user when transaction is sent

* feat(wallet): add settings page with notification toggle

* feat(wallet): add settings navigation and route guard

* fix(wallet): notify confirmation from popup cleanup hook to handle popup-open case

* fix(wallet): notification quality fixes (precision, disable, dedup, errors)

* chore(wallet): regenerate onboarding route token after settings route added

* fix(wallet): update pending transaction handling to use async/await for better error handling

* chore: add changeset for browser notifications for wallet transactions

* refactor(wallet): streamline pending transactions handling and improve notification logic

* refactor(wallet): enhance settings page layout and add back navigation

* fix(wallet): improve error handling for notifications and transaction checks

* fix(wallet): add hasPendingTransactions check before processing pending transactions

* refactor(wallet): optimize pending transactions handling and receipt fetching

* refactor(wallet): consolidate transaction hash extraction logic into tx-helpers

* refactor(notifications): unify notification creation logic and simplify transaction notifications

* feat(wallet): dynamic tx monitor alarm - start on pending, clear when settled

* test(wallet): remove outdated tx-monitor tests

* refactor(tx-monitor): extract interval constant for alarm creation

* fix: pass network parameter to fetchNetworkNonce in getNonceHex function

* feat(notifications): implement notification permission check and storage key

* fix(settings): add gap between notification toggle and description
2026-04-22 22:35:01 +09:00
Jinho Jang 4ebe9893d5 Implement NFT transfer (#1089)
* feat: implement NFT transfer functionality with gas fee estimation

* fix: fix bug with NFT send transfer

* refactor(wallet): tighten NFT send error handling and helpers

* chore(wallet): restore mainnet defaults

* refactor(wallet): align nft send UI with design system

* refactor(wallet): enhance error handling for NFT transfers and tRPC API calls

* chore: add changeset for nft transfer

* fix(wallet): improve error handling in fetchTrpcData function

* refactor(wallet): apply code quality improvements to NFT transfer

* feat(wallet): improve NFT transfer validation and cache handling

* fix(wallet): refactor collectible componentsa

* refactor(wallet): address remaining NFT transfer review findings

* refactor(wallet): use zod schema for NFT send validation

* feat(wallet): implement collectible data fetching and traits display

* feat(wallet): move send-collectibles-modal to wallet package

* refactor(wallet): use package send-collectibles-modal in collectible route

* refactor(wallet): simplify insufficient funds error handling in NFT send

* refactor(wallet): improve insufficient funds error parsing in NFT send

* refactor(wallet): move useCollectible hook, dedupe nft-helpers, derive chainId from network

* fix(wallet): use currentAccount for address retrieval in Collectible component
2026-04-22 18:59:12 +09:00
Jules da1c09d9f6 feat(wallet): implement multi-wallets
* fix(wallet): disable interation w/ sticky header

* feat(wallet): persist selected account and normalize legacy data

Extend wallet metadata & API with selected account persistence and normalization for legacy activeAccounts

* feat(wallet): expose and persist current account in provider

Update WalletProvider to expose/select current account and persist selections

* refactor(wallet): use selected account in portfolio and signer flows

Replace first-account assumptions in portfolio and signer flows with selected account

* feat(wallet): implement wallet selector UI

Implement wallet selector UI and integrate into splitted layout

* chore(wallet): update import and create hooks to match api

Update import and create wallet hooks to use optional password to match new API props

* feat(wallet): componentize create/import flows

Componentize create/import flows and create non-onboarding cases

* refactor(wallet): use components for onboarding flow

Use previously componentized flows for create/import wallet onboarding flows

* feat(wallet): add standalone create and import routes

Add create/import wallet routes for non-onboarding flows. Enables wallet setup outside of the initial onboarding flow

* chore: add changeset

* chore(wallet): hardcode account name for dapp connect

* chore(wallet): comment out account name tooltip

Comment current account's name tooltip displayed on address hover. Since multi-account support is not implemented yet, this feature is more confusing than helpful.

* chore(wallet): add TODO for multi-account support

* chore(wallet): use wallet name instead of account name

Replace all use of currently selected account name by currently selected wallet name. The purpose is to avoid any confusion in the UI/UX between accounts and wallets.

* chore(wallet): rename wallet-account-selector to wallet-selector

Since we don't have multi-account support right now, rename to wallet-selector to make it explicist and avoid any confusion.

* fix(wallet): remove `/wallet-flow` as a route

There was no point in having `wallet-flow` as an existing route, it was a mistake.

* chore(wallet): remove useless refetchQueries

invalidateQueries will already handle the refetch for active queries.

* chore(wallet): edit create-password-step to factorize code

* fix(wallet): rename legacy "Account 1" wallets to "Wallet N"

Legacy wallets stored with the default name "Account 1" are now
automatically renamed to the next available "Wallet N" during
normalization. The rename is persisted to the store so it only
runs once per legacy wallet.
2026-04-22 14:22:53 +09:00
Jules d3614111c3 chore(wallet): change layout & messaging when hide<$1
* chore(wallet): change layout & messaging when hide<$1

* chore: add changeset
2026-04-14 12:17:32 +09:00
Jules 1a74ebc9fa feat(wallet/assets-list): add hide assets < 1 USD filter
* feat(wallet/assets-list): add hide assets <  filter

* chore: add changeset

* chore(wallet/assets-list): remove unnecessary button's attributes

* feat(wallet): create ToggleButton component

* chore(wallet): move hide <1USD switch to actions & use ToggleButton
2026-04-09 17:05:19 +09:00
Jakub b3e3155c1f feat: connect wallet to dApp (#1033) 2026-04-08 12:59:17 +02:00
Jinho Jang 89df4a5048 Migrate community curation dapp into status-web monorepo (#1077)
* feat: migrate community dapp

* fix: change server protocol from https to http in webpack config

* fix: update build script in package.json and add output directory to turbo.json

* fix: update CI workflow to initialize git submodules recursively and add Foundry installation step

* chore: change from community to community-dapp

* feat: add global type declarations for wallet and API integration

* fix: correct syntax for not-first-child selector in Card component

* fix(wallet): use type-safe globalThis cast instead of direct property assignment

* fix(community-dapp): fix eslint FlatCompat config and include test files in tsconfig

* fix(ci): fix forge test args and disable broken community-dapp tests

* fix(community-dapp): resolve @libp2p/interface to v2.10.4 for webpack build

* fix: add @libp2p/interface as missing dependency for @waku packages via packageExtensions

* fix: address PR review feedback

* fix(wallet): restore type-safe globalThis cast for CI build

* fix: commit ABI artifacts and add vercel.json for SPA routing

* fix(community-dapp): add 404.html for SPA routing on Vercel

* fix(community-dapp): simplify vercel.json to rewrites only

* fix(community-dapp): update vercel.json with build and output configurations

* fix: add Foundry dependencies to .gitignore
2026-03-31 23:27:42 +09:00
JulesandFelicio 2836972877 chore(wallet): remove wxt version number (#1072)
* chore(wallet): remove wxt version number

* Remove wxt version number from wallet

---------

Co-authored-by: Felicio <felicio@users.noreply.github.com>
2026-03-23 14:35:12 +09:00
github-actions[bot]andgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> 5cae149418 Release (#883)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-09 11:41:24 +00:00
Jules ca76e7d8ca feat(status-wallet): add password context
* feat: add wagmi, viem and @lifi/widget dependencies

* feat: add ethereum contract call and signing API functions

* feat: add sendContractCall ethereum wallet function

* feat: add signer context for wallet password and transaction signing

* feat: add custom wagmi status connector

* feat: add wagmi provider setup

* feat: add exchange drawer UI component

* feat: integrate LiFi widget for token swaps

* feat: update root route with new providers

* feat: add exchange button to token component

* chore: update CSP and improve password modal accessibility

* feat(wallet): enhance exchange drawer UI

* feat: add password context provider

* feat: extend password modal with customizable props

* feat: integrate password provider in root

* feat: integrate password context in onboarding flows

* feat: integrate password context in exchange drawer

* feat: integrate password context in send assets modal

* feat: integrate password context in recovery phrase backup

* refactor: migrate signer context to use password context

* chore(wallet): update lifi, wagmi & viem versions

* chore(changeset): revert useless quotes change

* chore(changeset): remove useless formatting change

* chore(wallet): remove useless async prefix

* chore(wallet/status-connector): turn code handling provider requests into a function for better readability

* chore: add changeset

* fix(wallet): properly type status-connector connect method

* chore(wallet/exchange): update request password modal copy

* chore(packages/wallet): delete SignTransactionDialog dialog as it is now dead code

* chore(apps/wallet): rename usePasswordSession to usePassword

* chore(apps/wallet): remove unnecessary async/await in password-context

* chore(wallet): remove unused import

* chore: add changeset

* chore(wallet): remove unnecessary await

* feat(wallet): show insufficient funds error when user is missing funds

* fix(wallet): white page after onboarding

* fix(wallet): redirection to `/portfolio/assets` when opening wallet UI

If user is onboarded and closes then opens the wallet's UI, he would be shown the onboarding page. This is has been fixed

* feat(wallet): add browser-passworder dep and alarms permission

* feat(wallet): add vault encryption service

* feat(wallet): add wallet metadata store

* feat(wallet): add stateless key derivation helpers

* feat(wallet): add session manager with auto-lock and migration

* feat(wallet): add alarm listener for session auto-lock

* refactor(wallet): replace keystore with session-based vault

* chore(wallet): remove useless no-op method

* chore(package/wallet): change toasts on tx send & enable sign btn after tx success

* fix(wallet): add chrome.alarms mocks to fix failing tests

* fix(wallet): migrate remaining legacy wallets on every unlock & delete them

Run migration on every unlock to ensure wallets that use different passwords get migrated when user access them.

* chore(wallet): remove unsued type field

* chore(wallet): make variable name more explicit

* refactor(wallet): make password request flow return boolean

Also changed variable names of callers to isUnlocked to be more explicit.

* chore(wallet): invalidate gql query so it would not stale

* fix(wallet): unblock send CTA during gas fee refetch and clarify action label

* fix(wallet): send tx & nonce padding && nonce collision && broadcastTx JSON extract
2026-03-09 20:25:03 +09:00
jinhojang6 29399e4b81 refactor(collectibles): rename scroll handling hooks for consistency and improved clarity 2026-02-06 18:01:51 +09:00
jinhojang6 3e03b9d55c refactor(collectibles): implement scroll handling hooks for improved collectible navigation and state management 2026-02-06 18:01:51 +09:00
jinhojang6 f1dbf54b2e feat(collectibles): add scroll handling to Link components and improve navigation behavior 2026-02-06 18:01:51 +09:00
jinhojang6 770734fdf3 fix(collectibles): update OpenSea verification logic and remove hardcoded address for improved flexibility 2026-02-06 18:01:51 +09:00
jinhojang6 79649c5cdd fix(collectibles): update comments for clarity and consistency in sorting and filtering logic 2026-02-06 18:01:51 +09:00
jinhojang6 2422e3b1ee feat(collectibles): enhance pagination and filtering logic for collectibles retrieval 2026-02-06 18:01:51 +09:00
jinhojang6 4cb1f250eb feat(collectibles): enhance OpenSea integration with verification checks and improved filtering 2026-02-06 18:01:51 +09:00
jinhojang6 e8f33c20ec feat(collectibles): update collectible display logic to use displayName for better clarity 2026-02-06 18:01:51 +09:00
jinhojang6 64ec2f9b26 feat(collectibles): include display ID in collectible name for better identification 2026-02-06 18:01:51 +09:00
Jules d4b319fc34 chore(apps/wallet): force lifi widget to light mode
* chore(apps/wallet): force lifi widget to light mode

* chore: add changeset
2026-02-06 14:00:18 +09:00
Jules 5975aae28c fix(connector): two different zod instances being used
* fix(connector): two different zod instances being used leading to error in extension

* chore: add changeset

* fix(wallet): wrong zod version used

* fix: zod version resolution

* chore: add changeset

* fix: pnpm lock file to pass package/wallet build

* Revert "fix: pnpm lock file to pass package/wallet build"

This reverts commit a9455d4216.

* fix(connector): use dependency aliasing to isolate zod version

isolate zod's version of the connector project from other projects so it is not misused elsewhere

* fix(connector): zod imports
2026-02-04 14:43:41 +09:00
Jakub 74891e3aa3 feat(wallet): update tab management (#1011) 2026-01-26 08:33:24 +01:00
Jules 4614fcc1a5 feat: integrate li.fi widget to swap token on ETH network
* feat: add wagmi, viem and @lifi/widget dependencies

* feat: add ethereum contract call and signing API functions

* feat: add sendContractCall ethereum wallet function

* feat: add signer context for wallet password and transaction signing

* feat: add custom wagmi status connector

* feat: add wagmi provider setup

* feat: add exchange drawer UI component

* feat: integrate LiFi widget for token swaps

* feat: update root route with new providers

* feat: add exchange button to token component

* chore: update CSP and improve password modal accessibility

* feat(wallet): enhance exchange drawer UI

* chore(wallet): update lifi, wagmi & viem versions

* chore(changeset): revert useless quotes change

* chore(changeset): remove useless formatting change

* chore(wallet): remove useless async prefix

* chore(wallet/status-connector): turn code handling provider requests into a function for better readability

* chore: add changeset

* fix(wallet): properly type status-connector connect method
2026-01-15 13:16:51 +09:00
jinhojang6 be1cdddb01 refactor: enhance SplittedLayout with TokenSkeleton 2025-12-17 19:22:04 +09:00
jinhojang6 e6a2fe30aa refactor: introduce helper functions for token and gas fee data fetching to enhance code readability and maintainability 2025-12-17 19:22:04 +09:00
jinhojang6 01e76cf270 fix: enhance token data handling and optimize API calls 2025-12-17 19:22:04 +09:00
github-actions[bot]andgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> 4750dd0305 Release (#870)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-10 15:51:12 +09:00
Jinho Jang 850022297d Fix issues with the incorrect wallet data (#877)
* Enhance token data handling by integrating CoinGecko markets API

* Update token market data retrieval to use current price from CoinGecko API

* refactor: refactor wallet assets business logic for better readability

* fix: correct spelling of fully diluted in asset metadata

* fix: update fallback logic for total supply

* docs: add changeset

* chore: update React and ReactDOM to version 19.1.2 across multiple packages
2025-12-08 22:18:47 +09:00
Jinho Jang d92da0c61b Implement proxies (#860)
* Refactor API calls to use Status RPC for asset management

* Refactor API integration to utilize market-proxy service for token data retrieval

* Update environment variables to replace STATUS_RPC with ETH_RPC for proxy authentication

* Remove CryptoCompare API keys and references across multiple files, updating to use market-proxy service for token data retrieval.

* Add changeset for CRYPTOCOMPARE to MARKET_PROXY refactor

* Update ETH_RPC_PROXY authentication to make username and password optional

* Refactor proxy authentication to use server environment variables for credentials in assets and market-proxy services

* Refactor asset metadata fetching to use fallback mechanism and streamline token filtering by networks

* Enhance token metadata handling by implementing fallback

* Update ETH_RPC_PROXY authentication to require username and password in environment configuration

* Add environment variables

* Refactor nullish coalescing

* Remove unnecessary files

* Refactor token ID mapping in market-proxy

* Refactor market-proxy to introduce default values and improve token metadata handling

* Enhance token ID retrieval in market-proxy by integrating ERC20 token list and improving coin list caching mechanism

* Refactor market-proxy to utilize CoinGecko API responses directly, enhancing price

* Refactor asset and market-proxy modules

* Refactor to replace market-proxy with CoinGecko API integration

* Refactor CoinGecko API service to exclusively use proxy endpoint

* add ETH_RPC_PROXY_URL and MARKET_PROXY_URL
2025-12-05 10:47:23 +09:00
github-actions[bot]andgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> 5010cb448e Release (#666)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-26 17:57:07 +09:00
JulesandFelicio b94eaff8e4 chore: update waku dependencies
* chore: update waku dependencies => update to node 22

* chore: add changeset

* chore(ci.yml): update node version

* fix(build): use jsx runtime for rehype-react in production

* chore(release.yml): update node version

* fix(build): handle CoinGecko API failures in exchanges page

* chore(status-js): update waku dependencies

* fix(status-js): resolve Uint8Array ArrayBufferLike type errors

* feat(status-js): make package compatible with ethers v5 & v6

* fix(status-js): fix ethers v6 API compatibility

* chore(ethers): prevent webpack from analyzing ethers v5 & v6 compatible calls at build time to avoid false positive errors

* chore(preview-page): add console logs for debugging in vercel preview

* chore(status-js): add console log to ethereum client to debug on preview

* chore(status-js): add extra logs to request-client to debug in preview

* chore(status-js): add extra logs to debug in preview

* chore(status.app): remove unnecessary guard

* patch crypto deps

* chore(status.app): type rehype-react options in jobs and blog pages

* remove logs

* update readme

* remove nested resolutions

* c

* add node networkConfig

* refactor(status-js): migrate to @bufbuild/protobuf v2 API

* chore(status-js): remove console logs

* chore(status-js): revert compatibility update for ethers v5 & v6

* chore(status-js): update waku packages to latest version

* fix(apps/wallet): react-router-dom version mismatch

* chore(packages/wallet): remove useless peer dependency

* fix(status.app): upgrade remark packages to unified 11 for mdx compatibility

---------

Co-authored-by: Felicio <felicio@users.noreply.github.com>
2025-11-26 17:01:48 +09:00