# Events reference > [🇬🇧 English](./EVENTS.md) • [🇯🇵 日本語](./EVENTS.ja.md) This document is intended for users who want to inspect kiwa's event API. In v0.1.0, `packages/dapp/src/event-emitter.ts` and `packages/dapp/src/fixture.ts` work together to deliver the four EIP-1193 events to the page side. From tests, you can trigger them with `dappE2e.triggerEvent(name, payload)`. ## Four EIP-1193 events | Event | Payload | Purpose | |---|---|---| | `accountsChanged` | `string[]` | Notify account switching or an empty account list | | `chainChanged` | `0x${string}` | Reinitialize after a chain switch | | `connect` | `{ chainId: 0x{hex} }` | Notify provider connection | | `disconnect` | `{ code: number, message: string }` | Notify provider disconnection | On the page side, subscribe with `window.ethereum.on(eventName, handler)`. The injected provider also has `removeListener()`, so you can remove handlers when they are no longer needed. ```typescript window.ethereum.on('chainChanged', (chainId) => { console.log('changed to', chainId); }); ``` ## Test-side API The `dappE2e` object passed from the fixture includes helpers for event control. - `triggerEvent(event, ...args)` - `connect()` - `disconnect()` - `switchChain(chainIdHex)` - `waitForRpcIdle(timeoutMs?)` `connect()` forwards a `connect` event to the page side as-is, and `disconnect()` does the same for `disconnect`. `switchChain()` updates internal state before sending `chainChanged`, so after the event fires, the state stays consistent with `eth_chainId`. ## Basic example The following example is the minimum setup for receiving `accountsChanged` in a page-side handler. ```typescript import { expect } from '@playwright/test'; import { dappE2eTest as test } from '@kiwa-test/dapp'; test('accountsChanged event が page 側 handler を発火する', async ({ page, dappE2e }) => { await page.setContent(`

    
  `);

  const newAddr = '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826';
  await dappE2e.triggerEvent('accountsChanged', [newAddr]);

  const text = await page.locator('#event-result').textContent({ timeout: 5000 });
  expect(text).toBe(`accountsChanged: ${newAddr}`);
});
```

This follows the same idea as `T-E2E-006` in [examples/basic-connect/tests/connect.spec.ts](../examples/basic-connect/tests/connect.spec.ts).

## How to synchronize with handlers

`triggerEvent()` itself only calls `window.__dappE2eEmit()` inside the page through `page.evaluate()`.
Because of that, for synchronous handlers, waiting on a locator is enough.
If the event handler fires more RPCs, combining it with `waitForRpcIdle()` lets you proceed to assertions
only after pending RPCs are drained.

```typescript
await dappE2e.triggerEvent('chainChanged', '0xa86a');
await dappE2e.waitForRpcIdle();
```

In addition to waiting for pending RPCs to resolve,
`waitForRpcIdle()` also waits for two final `requestAnimationFrame` ticks,
which makes it easier to wait for subsequent DOM updates caused by RPC results as well.

## Notes on payload design

The payload shapes for `connect` and `disconnect` follow EIP-1193.
However, `disconnect()` emitted automatically by the v0.1.0 helper
always sends `{ code: 4900, message: 'Disconnected' }`.
If you want to test `4901`, specify it explicitly, for example with `triggerEvent('disconnect', { code: 4901, message: 'Chain disconnected' })`.

Because `accountsChanged` uses an array payload, you pass an array as a single argument,
for example `triggerEvent('accountsChanged', [addr])`.

## EIP-6963 announcement behavior

kiwa supports EIP-6963 (Multi Injected Provider Discovery), so you can inject multiple wallets into a single page in parallel.

### Events

| Event | Direction | Description |
|---|---|---|
| `eip6963:announceProvider` | kiwa → window | Each wallet announces itself, with `detail: { info, provider }` (`Object.freeze`) |
| `eip6963:requestProvider` | dApp → window | The dApp requests the wallet list again, and kiwa re-announces all registered wallets |

### `info` specification

`info` is immutable with `Object.freeze` (required by the EIP-6963 spec).

| field | Type | Description |
|---|---|---|
| `uuid` | string | Unique per wallet, generated with `crypto.randomUUID()` |
| `name` | string | Wallet display name (for example `MetaMask`) |
| `icon` | string | Icon data URI (`data:image/svg+xml;base64,...`) |
| `rdns` | string | Reverse-DNS naming (for example `io.metamask`) |

### `rdns` naming convention

For dApp compatibility, it is recommended to use the same `rdns` value as the real wallet for each wallet.

| Wallet | rdns |
|---|---|
| MetaMask | `io.metamask` |
| Rabby | `io.rabby` |
| Coinbase Wallet | `com.coinbase.wallet` |

### `window.ethereum` compatibility

`window.ethereum` is injected for **only the first wallet**.
This preserves legacy provider compatibility.
If you want to handle multiple wallets, acquire them through EIP-6963 instead of `window.ethereum`.

## Related

- [EIP-1193 events](https://eips.ethereum.org/EIPS/eip-1193#events)
- [RPC.md](./RPC.md)
- [ERRORS.md](./ERRORS.md)
- [examples/basic-connect/tests/connect.spec.ts](../examples/basic-connect/tests/connect.spec.ts)