The swap page says “Connected,” but the wallet prompt shows a different account on a different chain. The test clicks through the page, reports success, and never inspects the prompt where the user would have noticed the problem.
DeFi wallet testing has to follow four states at once: the application, the wallet extension, the chain connection, and the user’s approval. DeFi means finance applications that use blockchain accounts and smart contracts, or programs deployed on a blockchain, for actions such as swaps, lending, borrowing, and liquidity provision. A token allowance is a limit on how much a contract may spend from an account. A wallet extension manages keys and displays the approval prompts. The browser page can request work from the wallet, but it does not own the key or the final approval.
Tangle is the platform around these tools: it provides a way to run agent workflows and inspect the evidence they produce.
Tangle Browser Agent is the browser-facing tool that opens pages, observes Document Object Model state, and executes bounded browser actions toward a stated goal.
After installing the public driver, run bad --help to inspect the Browser Agent commands available from a terminal.
The public driver repository documents wallet and DeFi testing support alongside screenshots, page observations, and browser actions.
The DOM, or Document Object Model, is the page’s structured tree of elements and attributes.
The rest of this article uses one toy run to show how page, wallet, provider, and chain evidence fit into one timeline.
For the narrower MetaMask fixture and release workflow, read MetaMask Automated Testing For Wallet Flows.
Track application, wallet, provider, and chain state
A form test usually reads and submits Document Object Model state from one page. A DeFi flow crosses several state owners whose values can disagree. RPC, or remote procedure call, is the request-and-response interface used to ask a wallet provider or chain client for account, chain, balance, and transaction information.
| State | What it controls | Example failure |
|---|---|---|
| application | selected asset, amount, route, and displayed status | The page keeps an old account after the wallet changes. |
| wallet extension | account access, network prompts, signatures, and transaction confirmation | The prompt names the wrong chain or spender. |
| provider and RPC client | requests between the page, wallet, and chain client | The request is rejected or points at a disconnected chain. |
| chain and contract | balances, allowances, transaction execution, and confirmation | The transaction reverts or the UI reports success too early. |
A dapp is a web application that calls smart contracts through wallet and provider interfaces from a web page. A spender is the contract allowed to use tokens under that allowance.
The test should name which state it is checking at each step. A sample instruction such as “Swap 10 test tokens” is incomplete if the test does not record the account, chain, token, route, approval, transaction state, and final balance or status.
Work through a safe swap example
Use a resettable account funded only on a local chain or named test network. Start with a small read-only quote. Stop at the wallet prompt before any signature unless the case explicitly authorizes a test-wallet transaction.
Goal: connect the disposable wallet, switch to the configured test chain,
enter a small token swap, verify the quoted route and approval details,
capture the wallet prompt, and stop before signing.
Starting state: fresh browser profile, known account, funded test balance,
known token contracts, and reset application data.
Forbidden actions: production-network (mainnet) signing, real-value transfer, unknown approval,
or accepting a prompt whose account or chain does not match the case.
The following is a deliberately fake run on a local test chain, not a production result. The addresses are toy identifiers chosen to make the evidence legible.
| Time | Observation | Evidence | Outcome |
|---|---|---|---|
| 09:00:01 | The injected provider emits accountsChanged with 0x0000000000000000000000000000000000000001, and the page observes it. | App screenshot and event log | The page and wallet show the same disposable account. |
| 09:00:02 | eth_chainId returns 0x7a69, which is chain 31337 in this fixture. | Provider response and chain label | The test is on the named local chain, not mainnet. |
| 09:00:04 | A quote requests 10 TST for 9.8 USDT through router 0x0000000000000000000000000000000000000002. | Quote response and page screenshot | The route and amount match the test case. |
| 09:00:05 | The wallet prompt says: “Approve 10 TST to 0x0000000000000000000000000000000000000002 on Local Test Chain (31337).” | Wallet screenshot and prompt record | The user can inspect the exact spender, asset, amount, and chain. |
| 09:00:06 | The fixture rejects the prompt with provider error 4001. | Provider error and accountsChanged/chainChanged event log | No signature or transaction identifier exists. |
| 09:00:07 | The page keeps the quote, shows “Approval cancelled,” and offers retry. | Final app screenshot and stop reason | The run passes: it did not claim a transaction that never happened. |
This timeline gives every claim an owner: the page owns its displayed state, the wallet owns the prompt, the provider owns the response, and a receipt or chain-state check establishes confirmation. For a different flow, keep the same fields: phase, application state, wallet state, provider or chain observation, outcome, and stop reason. This avoids creating a new checklist for every action.
Store sensitive values according to the test environment’s retention policy. Redact private keys, seed phrases, session cookies, and personal addresses that the reviewer does not need. Keep enough account and chain context to distinguish a wrong fixture from a product defect.
Inspect accountsChanged, chainChanged, and provider error 4001
An EIP, or Ethereum Improvement Proposal, is a public specification for an Ethereum interface or protocol change. The EIP-1193 provider specification defines a common JavaScript API, or application programming interface, between an Ethereum web application and a wallet or client. Its request method returns a result or rejects with a provider error. Its events include connect, disconnect, chainChanged, and accountsChanged.
Those events are test signals. If the chain changes, the app should update its network state. If the account changes, the app should update or invalidate account-specific state. If a request is rejected with code 4001, the app should treat it as user rejection rather than as successful approval.
Typed signing uses structured data rather than an opaque byte string. The EIP-712 standard defines typed data, a domain separator, and a signing method designed to let wallets present meaningful fields to users. It explicitly does not provide replay protection by itself. The dapp and contract still need a nonce, or one-use counter, expiry, chain binding, or another replay policy appropriate to the operation.
The following illustrative TypeScript provider listener makes the app-side evidence explicit:
type ProviderError = Error & { code?: number; data?: unknown }
const provider = window.ethereum
if (!provider) {
throw new Error('No injected wallet provider was found')
}
provider.on('accountsChanged', (accounts: string[]) => {
console.log({ event: 'accountsChanged', accounts })
})
provider.on('chainChanged', (chainId: string) => {
console.log({ event: 'chainChanged', chainId })
})
try {
const accounts = await provider.request({ method: 'eth_accounts' })
const chainId = await provider.request({ method: 'eth_chainId' })
console.log({ accounts, chainId })
} catch (error) {
const providerError = error as ProviderError
console.error({
message: providerError.message,
code: providerError.code,
data: providerError.data,
})
}
This is an illustrative browser-side example based on EIP-1193. The MetaMask Connect software development kit is MetaMask’s connection software for choosing supported wallet environments. An application using MetaMask Connect or another wallet library may expose a different integration object. The test should observe the public behavior that the application relies on, not assume that every wallet uses one implementation.
Test rejected, pending, reverted, and stale states
Create a small matrix before adding many actions.
| Starting state | Expected application behavior | Required evidence |
|---|---|---|
| no wallet extension | explain supported connection methods | app screenshot and stop reason |
| wallet access is unavailable | request access or stop the case | prompt or blocked state |
| wrong chain | request a switch or block the action | requested and selected chain |
| account changed | refresh account-specific data or require reconnect | account event and app state |
| empty test balance | prevent submission or explain the missing balance | balance state and disabled action |
| rejected signature | show a recoverable error and keep the quote state honest | provider error and app screenshot |
| pending transaction | show pending without claiming completion | transaction identifier and pending UI |
| reverted transaction | show failure and preserve retry context | receipt or local-chain result |
| stale allowance | show the requested spender and allowance change | approval prompt and post-action state |
Asset symbols and shortened addresses can hide a wrong contract target. An asset symbol in the app can differ from the contract address the transaction targets. A shortened address can hide the wrong contract. A friendly route name can conceal a different path after the quote expires. Capture the raw identifiers where a reviewer needs them, and show the human-readable prompt where the user makes the decision.
Separate approval from settlement
Wallet flows often collapse several statuses into one button label. A request can be created without being signed. A signature can be returned without a transaction being broadcast. A broadcast transaction can remain pending. A mined transaction can still revert.
Keep those states distinct in the case:
| State | Meaning | Evidence |
|---|---|---|
| requested | the dapp asked the provider or wallet to act | provider request and page context |
| presented | the wallet displayed the approval | wallet prompt screenshot |
| rejected | the user or wallet declined the request | provider error and app recovery |
| signed | a signature or transaction authorization was returned | response record in a test environment |
| broadcast | the transaction reached a chain client | transaction identifier and RPC result |
| pending | the chain has not produced the expected confirmation | pending UI and subsequent observation |
| confirmed | the owning system reports the expected result | receipt, event, or application-owned confirmation |
| failed | the request or transaction produced an error | error details and truthful app state |
The case may stop at any of these states.
For a smoke test, stop at presented unless signing is explicitly authorized.
Continuing to confirmed requires a controlled wallet, a known chain, test funds, and a check owned by the chain or application rather than by the model’s description.
Run the Tangle driver at the browser boundary
The live Browser Agent manifest names the package, the bad binary, and the documented safe discovery commands. The public driver README includes a wallet and DeFi testing section.
npm install -g @tangle-network/browser-agent-driver
npx playwright install chromium
bad --help
bad run \
--url https://app.example.com \
--goal "Connect the disposable wallet, verify the configured test chain, enter a small swap quote, capture the wallet prompt, and stop before signing"
The URL and wallet are illustrative. Run against a staging app, local chain, or another environment with test funds. Do not pass a production storage state or seed phrase to an exploratory agent.
The runtime is the environment that contains the browser process, model calls, files, network access, and wallet fixture. Record its browser version, extension version, chain endpoint, account class, and reset method. An agent profile is the named model, observation mode, permissions, turn limit, and stop policy used for the run. Record the model, observation mode, permissions, turn limit, and stop policy beside every run. Compare runs with identical settings before attributing prompt differences to the application.
Separate page, wallet, provider, and chain evidence
The public driver README shows package and CLI usage without the service-packaging layer described by Tangle Blueprints; verify installation details against that README as the driver evolves. If a team offers wallet testing as a service, a Blueprint can define the job inputs, runtime requirements, and returned artifacts, while an operator is the provider that runs the job. The Blueprint records Job inputs, runtime requirements, returned artifacts, and operator responsibility. It does not validate a wallet prompt, a contract, or a chain result.
Return application state, wallet state, provider responses, and chain confirmations as separate evidence fields so a reviewer can see which boundary produced each claim.
What browser wallet testing does not prove
A browser run can prove that a particular application and wallet fixture presented a particular flow. It cannot prove smart contract safety. It cannot replace contract tests, transaction simulation, audits, monitoring, or chain-level accounting. It cannot prove that a live-network transaction will settle at the quoted price. It cannot prove that a wallet extension version will preserve the same UI. It cannot make a signing action safe merely because a model reached the prompt.
The strongest claim is narrow: under a recorded runtime and disposable fixture, the application showed the expected wallet state, the wallet showed the expected approval details, and the app reported the resulting state honestly.
Set the approval boundary before you automate
Use a browser agent to inspect application state, wallet prompts, network changes, and recovery behavior. Use deterministic tests and chain-level tools for contract invariants and transaction semantics. Accept a wallet-flow result only when a reviewer can see the account, chain, requested action, outcome, and reason the run stopped.