Web Wallet website.
The build system assumes that you have at least Node.js v24.x installed. The LTS version is 24.13.0 at the time of writing.
All terminal commands assume that you are positioned in root folder of the repository.
Run npm install from the root folder to get the necessary dependencies.
As the application uses the Web Crypto API, in development mode @vitejs/plugin-basic-ssl is used to create a self-signed certificate to run the application in HTTPS. Being the certificate self-signed you need to create an exception in the browser to allow it to use the certificate.
The staging environment is at https://web-wallet-staging-oxs3z.ondigitalocean.app/
Web Wallet supports browser releases from the previous two years. The current minimum versions are Chrome 129, Edge 129, Firefox 130, and Safari 18. These versions must be reviewed with each major Web Wallet release.
The Vite build target enforces the corresponding JavaScript and CSS syntax level. It does not polyfill browser APIs or built-in methods.
The dusk-wallet-js library uses some environment variables.
The application defines these variables by reading a local .env file containing the same variables used in the dusk-wallet-js, with the addition of the VITE_ prefix.
N.B. the current 0.1.2 version of the library has no option to pick the network and uses the LOCAL_NODE only. The current testnet address is set in that variable in the example below:
VITE_BASE_PATH="" # can be empty string, must start with a slash otherwise, must not end with a slash
VITE_EVM_DEPOSIT_DELAY_THRESHOLD_MS=300000
VITE_EVM_DEPOSIT_STALL_THRESHOLD_MS=1800000
VITE_EVM_BRIDGE_GAME_SEARCH_DEPTH=64
VITE_FEATURE_ALLOCATE=true
VITE_FEATURE_BRIDGE=true
VITE_FEATURE_MIGRATE=true
VITE_FEATURE_STAKE=true
VITE_FEATURE_TRANSFER=true
VITE_FEATURE_TRANSACTION_HISTORY=true
VITE_GAS_LIMIT_UPPER=1000000000
VITE_GAS_PRICE_DEFAULT=1
VITE_GAS_PRICE_LOWER=1
VITE_SYNC_INTERVAL=300000
VITE_MODE_MAINTENANCE=false
VITE_REOWN_PROJECT_ID="" # the ID of the EVM project (as on Reown Cloud)
VITE_NODE_URL="" # connect to a specific node
The wallet finds its network by the chain ID the node gives in
/on/node/info, never by the node's address, and takes the network's
contracts, Sozu's hub, DuskEVM, the bridge routes, the contract labels and the
recipient blocklist from its file in @dusk-network/networks. For local work,
these variables set DuskEVM's values instead: VITE_BRIDGE_CONTRACT_ID,
VITE_EVM_OPTIMISM_PORTAL_CONTRACT_ID,
VITE_EVM_DISPUTE_GAME_FACTORY_CONTRACT_ID,
VITE_EVM_ANCHOR_STATE_REGISTRY_CONTRACT_ID, their *_DATA_DRIVER_URLs,
VITE_EVM_CHAIN_ID, VITE_EVM_BRIDGE_RPC_URL, VITE_EVM_L1_BRIDGE_RPC_URL,
VITE_EVM_BRIDGE_BLOCK_EXPLORER_URL and VITE_EVM_BRIDGE_CONTRACT_ADDRESS.
The transaction history is on unless VITE_FEATURE_TRANSACTION_HISTORY is
false. It reads the public account's history from the node's archive
(moonlightHistory over GraphQL) by block height, and the shielded history
from the notes the wallet has synced. Both are kept in the browser, so a
refresh only asks for what is new, and are cleared when the wallet is reset.
Each operation has its own default gas limit and minimum, defined in
src/lib/contracts/gasDefaults.js. VITE_GAS_LIMIT_UPPER caps the gas limit
for all of them.
For the DuskEVM bridge, the network file's l1_standard_bridge (or
VITE_BRIDGE_CONTRACT_ID) is the Dusk L1 L1StandardBridge contract id used
for DuskDS -> DuskEVM deposits, and its l2StandardBridge predeploy (or
VITE_EVM_BRIDGE_CONTRACT_ADDRESS) the DuskEVM L2 L2StandardBridge address
used for DuskEVM -> DuskDS withdrawals. The current wallet bridge
uses the pinned @dusk/evm-sdk beta for versioned Dusk asset-recipient
metadata, L2 withdrawal calldata, MessagePassed verification, and L1 Portal
argument serialization. The DuskEVM adapter's Ethereum-compatible L1 RPC is
at /l1 on DuskEVM's RPC host, unless VITE_EVM_L1_BRIDGE_RPC_URL points
elsewhere. The wallet uses it only for deposits: to
confirm a deposit's DuskDS receipt, derive the OP deposit transaction, and
track delivery on VITE_EVM_BRIDGE_RPC_URL. Withdrawals are read on Dusk
itself. VITE_EVM_DEPOSIT_DELAY_THRESHOLD_MS controls when
an in-transit deposit is labelled delayed; it defaults to five minutes. Generic
Dusk delivery envelopes for arbitrary L2 -> L1 contract messages are a
separate application surface and are not exposed by the bridge UI.
DuskEVM's explorer, or VITE_EVM_BRIDGE_BLOCK_EXPLORER_URL, must be a
Blockscout-compatible explorer for the DuskEVM network. Withdrawal activity uses its v2
address-transactions and transaction-detail APIs, including the
op_withdrawals data returned by Blockscout's Optimism module, and its list of
all withdrawals to find, by their nonces, the ones the account proved or
claimed on Dusk: Bridge › Activity lists those without an EVM wallet
connected. If the explorer is temporarily unavailable, the wallet retains and
displays a bounded local history of withdrawals initiated in that browser.
Withdrawal finalization uses the existing Dusk-native wallet path on L1.
Reown/Wagmi is only used for the DuskEVM/L2 side. The wallet uses the
OptimismPortal contract and its data driver to call
proveWithdrawalTransaction, finalizeWithdrawalTransaction, and withdrawal
status getters through w3sper. It uses the DisputeGameFactory contract and
its data driver to find a
dispute game/output proposal that covers the L2 withdrawal block. The respected
dispute game type is read from the portal at runtime. With the anchor state
registry the portal asks whether a game is proper and respected, a proof goes
against a
covering game that has already resolved and passes those checks, so its claim
date is exact; otherwise against the newest covering game.
VITE_EVM_ANCHOR_STATE_REGISTRY_CONTRACT_ID=none leaves it out. This flow requires Rusk
1.7.1 or later so read-only contract queries execute at the current chain tip
and can evaluate proof maturity correctly.
The data-driver URLs must point to browser-loadable Forge data-driver WASM
artifacts for the deployed contracts; they are not the deployed contract WASM
artifacts themselves. These VITE_* values are public build-time browser
configuration and must not be derived from user input. Data-driver URLs may be
relative/root-relative asset paths or http(s) URLs. If the URL variables are
unset or empty, the wallet uses the drivers the network's file names, bundled
under /drivers/.
A withdrawal can be claimed from the later of its proof plus the Portal's
proof maturity delay, and its dispute game's resolution plus the game's
finality delay. History, a withdrawal's details and Bridge › Activity show that
date: exact once the game has resolved, and before that the earliest it can be
(the game resolves once its maxClockDuration has run out), as "about". A game
unresolved more than an hour past that is late: "Waiting for DuskEVM to settle
this proof. Claim from … at the soonest." The Portal's reads and the game's, on
the hub the factory names for it (createdAtForGame, resolvedAtForGame,
statusForGame, maxClockDuration), go through Multicall for Dusk, one query
once the game's hub is known. The times are the contracts' own: the deployed
testnet contracts count Dusk block height × 10, which the latest block converts
to a date; others count block time, a second later for their strict
comparisons. A proof goes against a game that covers the withdrawal and has
already resolved, where there is one, so its claim date is exact when it's
made; otherwise against the newest that covers it.
To run a local node different steps are needed, so please read the related section.
npm run buildgenerates the production buildnpm run checksruns all checks (lint, typecheck and test)npm run devgenerates the development build and starts the dev servernpm run dev:hostgenerates the development build, starts the dev server and exposes it to the local networknpm run lint: performs the linting checksnpm run lint:fix: runs ESLint with the--fixflag to fix formatting errorsnpm run previewpreviews the production buildnpm testruns the test suitenpm run test:coverageruns the test suite and generates the code coverage report in thecoveragefoldernpm run test:watchruns the test suite in watch modenpm run typecheckruns the type checkernpm run typecheck:watchruns the type checker in watch mode
The DuskEVM withdrawal flow requires Rusk 1.7.1 or later. To run a local node, follow the instructions outlined in the Rusk's readme.