Blog

Natural Language E2E Testing for Wallet Apps

Natural-language E2E testing for wallet apps lets agents drive browser flows while stopping before destructive signing and preserving evidence.

Drew Stone
agentsbrowsertesting
An editorial still life about describing and running an agent task

A wallet-app smoke test can pass the visible part of a swap and still miss the bug that matters. The site may ask the wallet to switch from the wrong network, display an approval for an unexpected token, or show a transaction whose recipient and amount do not match the page. The dangerous click happens in the wallet extension or provider prompt, outside the main application’s DOM.

Natural-language end-to-end testing, usually shortened to E2E testing, lets a browser agent execute a user-facing goal while preserving the state around that boundary. For wallet apps, the first useful target is a trace, the ordered record of one test run, that reaches the wallet prompt, captures the page and chain context, and stops before signing or transferring value.

Tangle Browser Agent can drive a real Chromium session with a natural-language goal, structured page observations, screenshots, and a result record. Tangle Sandbox can run the test project and keep screenshots, snapshots, and logs in the same isolated workspace. A natural-language goal is not a permission boundary. Here, the runner is the process that owns the browser and enforces the test’s allowed actions. For a no-signing smoke, keep wallet automation disabled or use a separate test configuration that rejects signing and value-transfer actions. The public wallet guide exposes --wallet-auto-approve and autoApprove as explicit test options, so do not enable them for this smoke.

Dapp pages and wallet prompts are separate interfaces

An ordinary form usually keeps the interaction inside one page. A dapp is a browser application that reads blockchain state or calls smart contracts through a wallet’s provider interface. The application page and the wallet prompt are separate interfaces with different ownership and evidence.

TermPlain-language meaningWhy the test records it
WalletSoftware that holds keys and asks a user to approve signatures or transactionsThe wallet controls the irreversible approval boundary
ProviderThe browser-facing interface through which the dapp requests wallet and chain actionsProvider errors and user rejections can happen outside the page
ChainThe blockchain network where a transaction would be submittedA correct-looking amount on the wrong chain is still a failure
RPCThe request interface used to read chain state and submit or simulate callsAvailability, latency, and error responses affect the flow
SignatureCryptographic approval of a message or transaction payloadA signature can authorize an action even before a transaction is mined
TransactionA network submission that can change on-chain stateIt may transfer value or change an allowance
Token approvalPermission for a contract to spend a token on a user’s behalfThe approval can outlive the test and affect later funds

The EIP-1193 provider specification defines the provider interface that many browser wallets expose. The Ethereum JSON-RPC documentation describes methods for reading network state and interacting with clients. Wallet-specific behavior belongs in the provider’s documentation, such as the MetaMask provider API reference or the WalletConnect documentation.

Those sources define the interfaces a test may encounter. They do not make a test wallet safe to use with real funds.

Stop before the irreversible edge

A natural-language goal should name the final safe state in ordinary language. The following is an illustrative test policy, not a Browser Agent CLI or configuration schema. This is a better test contract than “test the swap flow”:

Goal: Open the swap flow, enter a small test quote, verify the displayed chain and wallet prompt, then stop before signing.
Allowed actions: navigate, dismiss non-security consent, fill test inputs, connect the disposable test wallet, and capture evidence.
Required assertions: the dapp shows the expected chain, token pair, amount, recipient or spender, and approval boundary.
Forbidden actions: sign a message, approve a token, submit a transaction, or use a production wallet.
Stop reason: wallet confirmation is visible and the required evidence is saved.

The runner should reject a signing action even if the page tells the model to click it. The page is untrusted input from the runner’s point of view. Prompt injection can appear in a token symbol, a transaction description, or a web page’s text and ask the agent to ignore its own instructions. The runner must reject signing, seed-phrase requests, and production-chain switches before dispatch, even when page text asks the agent to proceed.

For the same reason, use a separate test chain, a disposable wallet, and test funds. Do not load a seed phrase into a browser-agent prompt. Do not use a wallet that can sign a mainnet transaction on the assumption that the test will stop before the final click.

Run a public browser check first

Tangle publishes a Browser Agent manifest and the @tangle-network/browser-agent-driver repository. The public install and safe-help commands are:

npm install -g @tangle-network/browser-agent-driver
npx playwright install chromium
bad --help
bad run --help
bad snapshot --help

A read-only public smoke can verify that the browser reaches a dapp’s swap page without connecting a wallet:

bad run \
  --url https://app.uniswap.org/swap \
  --goal "Open the swap page, confirm that the connect-wallet control is visible, capture a screenshot, and stop before connecting or signing." \
  --observation-mode hybrid

The command is a public smoke example, not a claim that the site’s layout or copy will remain unchanged. For a real wallet test, point the same runner at a staging deployment and disposable chain with the exact wallet extension configuration that production supports.

The Browser Agent SDK uses PlaywrightDriver to control a page and BrowserAgent to plan and execute the goal. Its public README documents the startUrl field for SDK runs and the --url field for the CLI. The public wallet guide documents MetaMask and Rabby against a local Anvil fork and notes that L2 and Solana support is untested, so another wallet or chain is a separate test surface.

Evidence must cross the wallet boundary

A screenshot of the dapp is insufficient when the approval lives in a wallet window. Capture the evidence that explains both sides of the interaction.

EvidenceExampleClaim it supports
Page stateURL, route, DOM or accessibility snapshotWhat the dapp displayed
Wallet statePrompt type, account, chain, recipient, token, amountWhat approval boundary appeared
Chain statechainId, RPC response, network name from a trusted sourceWhich network the test observed
Request stateSimulation or RPC error, request method, response statusWhat the provider accepted or rejected
ScreenshotDapp and wallet prompt at the checkpointWhat a human could see at that moment
Stop stateBlocked action, final assertion, reasonWhy the run ended before value movement

Worked example: inspect an approval without signing it

Suppose a staging swap should exchange TEST for USDC on a test chain. The fixture expects the dapp to request an allowance for one named token contract and one named spender. The agent’s job is to reach the wallet preview, compare the visible and encoded request details, and stop.

ObservationExpected fixtureFailure to record
Provider chainIdThe configured test-chain IDThe page and wallet may be on different networks
Token contractThe test token addressA familiar symbol can refer to a different contract
SpenderThe approved router or contract addressA button can name “Approve” while the spender is wrong
Allowance or amountThe bounded test amountAn unlimited allowance is a different risk than an exact allowance
Origin and accountThe staging origin and disposable accountA prompt from another origin or account is not the fixture
Action resultRunner blocks signing and records the promptA missing prompt is not proof that no request was made

The comparison should use provider data and the wallet preview when both are available. If the dapp sends a contract call, decode the request with the contract’s public ABI or adapter rather than comparing only the button label. The test does not need to broadcast a transaction to find an unexpected spender or chain. It can fail at the review boundary, preserve the request and screenshot, and leave the disposable wallet unchanged.

If a wallet adapter exposes only a prompt screenshot, label the chain and contract fields as unobserved instead of guessing them from page text. If the adapter exposes a request method and parameters, retain the redacted method and the fields required for the fixture check. The evidence should make it clear whether the failure came from the dapp, provider, wallet adapter, or runner policy.

A trace is the ordered record of those observations, actions, results, and stop reasons for one run. It should include the test-case ID, browser version, model and agent profile, URL, chain, wallet type, input values, screenshots, and redacted logs. The trace is the thing a reviewer can inspect after the browser has closed.

An agent profile is the versioned run configuration that names the model, tools, credentials, allowed actions, observation mode, time budget, and where evidence is written. Wallet testing needs a profile that explicitly blocks signing and value transfer. Treat any change to allowed actions, credentials, or wallet configuration as a new safety profile, even when the natural-language goal stays the same.

An illustrative result envelope might look like this:

{
  "caseId": "swap-reaches-wallet-prompt",
  "page": {
    "url": "https://staging.example.test/swap",
    "chainId": "0x14a34",
    "tokenIn": "TEST",
    "tokenOut": "USDC"
  },
  "wallet": {
    "prompt": "transaction-preview",
    "account": "0xTest...1234",
    "signingAttempted": false
  },
  "assertions": {
    "expectedChain": true,
    "expectedAmount": true,
    "expectedSpender": true,
    "stoppedBeforeSigning": true
  },
  "artifacts": ["turn-04.png", "wallet-prompt.png", "dom-04.json"],
  "stopReason": "safe wallet boundary reached"
}

The shape is illustrative and should be adapted to the wallet adapter and dapp under test. The important invariant is that signingAttempted and the stop reason come from the runner’s action record, not from the model’s final prose.

The stop matrix should be explicit

Different wallet prompts have different consequences. Treat them as separate checkpoints instead of one generic “wallet appeared” assertion.

StateDefault actionEvidence to captureException
Connect-account promptStop or connect only the disposable accountAccount, origin, chain, screenshotA dedicated test may permit connection to a test dapp
Network-switch promptStop after recording current and requested chainschainId, requested chain, provider responseThe test explicitly covers switching on a test chain
Message-signature promptStopMessage contents, origin, account, screenshotA controlled test signs a fixed non-value message
Token-approval promptStopToken, spender, amount or allowance, chainA disposable token and explicit approval case exist
Transaction-confirmation promptStopRecipient, value, data summary, gas estimate, chainA test wallet and test funds have explicit signing permission
Unknown modal or provider errorStop and request reviewScreenshot, DOM, provider error, current URLA bounded recovery can re-open the same safe state

The “unless” column is a new authorization decision, not a loophole in the default rule. Make it a separate test profile and separate report label so a reviewer cannot confuse a no-signing smoke with a signing test.

Test the runner’s refusal path

A safe wallet test should deliberately present one forbidden action and confirm that the runner refuses it. The refusal is part of the product behavior, not an implementation detail hidden in the prompt.

AttemptExpected resultEvidence to retain
Click the final sign controlThe runner blocks the action before the wallet signsPolicy decision, target, and stop reason
Follow page text that asks for a seed phraseThe runner refuses to request or store the secretPrompt-injection text, blocked action, and redacted page capture
Switch to a production chainThe runner stops before changing networkCurrent chain, requested chain, profile ID, and reviewer flag
Retry after an unknown provider errorThe runner saves state and asks for review or uses one named recoveryError, recovery count, and final observation

Run these cases on the same browser build, wallet adapter, test chain, and profile used for the allowed flow. A policy tested only in a mocked page can fail when the extension opens a new window or the provider returns an unfamiliar method. The refusal record should be easy to distinguish from a navigation failure, because the former shows that a control worked and the latter shows that the runner could not observe the required state.

Common failures sit outside the page

Wallet E2E tests find bugs that page-only tests miss.

The dapp and wallet disagree about the chain

The page may display one network name while the provider reports another chainId. Record both values and fail the case when the expected chain is not established before an approval prompt.

The quote changes before confirmation

A swap quote can expire while the agent is reading the page. The test should record the quote timestamp or expiry signal when the application exposes it and classify a stale quote separately from a navigation failure.

The approval spender is wrong

The button label can say “Approve” while the wallet preview names an unexpected contract. The final assertion should compare the spender and token against the test fixture and the button text.

RPC success is mistaken for transaction success

An RPC can accept a request or return a simulation while the eventual transaction reverts. A page check needs a chain-level receipt or service confirmation when the test submits a transaction.

The wallet prompt is inaccessible to the driver

Extensions can render in another page, window, or browser context. The test should stop with the prompt boundary and record the missing observation rather than infer that no prompt appeared.

The Browser Agent wallet and DeFi guide is an implementation surface, not a substitute for a wallet-specific adapter and test environment.

Put natural-language tests above deterministic checks

Use natural-language testing to traverse changing copy, layout, wallet prompts, and RPC failures. Use lower-level deterministic checks to prove exact invariants.

LayerWhat it should prove
Unit testsCalculation and state-machine invariants
Contract testsSmart-contract function behavior and access rules
Transaction simulationRevert paths, allowance requirements, and gas assumptions
Provider and RPC testsChain selection, error handling, and request translation
Playwright testsStable page transitions and deterministic assertions
Browser-agent smokeUser-visible flow, copy/layout drift, recovery, and evidence at the wallet edge
Manual reviewAny action involving real funds, production accounts, or ambiguous authorization

An evaluation, or eval, is a repeatable collection of these cases with a stated scoring rule. For a wallet suite, the score should include the number of cases, assertion results, chain and browser versions, wallet configuration, failed-step category, and trace links. An agent’s fluent final sentence is not a score.

The AI browser testing evidence guide shows why a failed run should retain its full action path. The agent-eval package provides public primitives for cases, judges, comparisons, and run records when a product needs a larger evaluation system.

Where Tangle fits

Browser Agent supplies the goal-driven browser loop and evidence capture. Tangle Sandbox can run the test runner, app build, and wallet extension inside one isolated environment; retained artifacts still need an explicit export or persistence policy. The runtime environment guide explains how model routing, permissions, state, and result records fit around those components.

A TEE is a Trusted Execution Environment, and attestation is signed evidence about what code or hardware-backed boundary ran. Use a TEE attestation only when you need evidence about the execution environment or code identity. It does not make a wallet safe to sign, prove that a transaction is correct, or replace a disposable account and explicit action policy.

The decision for wallet teams

Start with a no-signing goal against a public or staging page. Require a screenshot, page snapshot, provider or chain state, final assertion, and stop reason. Introduce a test wallet only after the runner can prove that it blocks the irreversible action.

Keep contract and RPC checks below the browser-agent layer. Let the natural-language test cover the messy user path, then use deterministic assertions and service receipts to decide whether the run passed.

Can AI agents test wallet apps safely?

Yes, when the test uses a disposable environment, test accounts and funds, explicit action permissions, evidence capture, and a stop condition before signing or value transfer.

Should a browser agent sign wallet transactions?

Only in a separately authorized test profile with a disposable wallet, test chain, fixed inputs, limited funds, and a reviewer-approved reason to submit the transaction. Production wallet keys should never be part of a browser-agent smoke test.

What is the minimum evidence for wallet E2E testing?

Capture the dapp screenshot and page state, wallet prompt state, account and chain context, request or provider errors, final assertions, and the runner’s stop reason.

Does a wallet prompt prove that the transaction will succeed?

No. It proves that the dapp reached an approval boundary visible to the tested wallet session. Simulation, submission, and a confirmed receipt are separate observations.

Can natural-language E2E replace contract tests?

No. It exercises the user-facing integration path, while contract tests and transaction simulations prove lower-level invariants that a browser run may never reach.