> For the complete documentation index, see [llms.txt](https://onchainlabs-tech-documentation.gitbook.io/wallettwo-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://onchainlabs-tech-documentation.gitbook.io/wallettwo-documentation/actions/examples.md).

# Examples

## Action Examples & Tests

This file contains ready-to-use URLs and test scenarios for every WalletTwo action endpoint.

Replace `http://localhost:5173` with your deployed origin when testing against a live environment.

***

### `auth`

#### Example URLs

**Minimal — postMessage only:**

```
http://localhost:5173/action/auth?iframe=true
```

**With redirect:**

```
http://localhost:5173/action/auth?iframe=true&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
```

**Direct navigation (non-iframe, shows branded container):**

```
http://localhost:5173/action/auth?redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
```

***

#### Test scenarios

| # | Scenario                      | How to trigger                                     | Expected result                                             |
| - | ----------------------------- | -------------------------------------------------- | ----------------------------------------------------------- |
| 1 | Successful auth — postMessage | Open minimal URL in an iframe, listen on `message` | Receive `{ event: "wallet_login", code, user, wallet }`     |
| 2 | Successful auth — redirect    | Open URL with `redirect_uri`                       | Iframe navigates to `redirect_uri?code=...&usr=...&wlt=...` |
| 3 | Not logged in                 | Open URL without an active session                 | Iframe redirects to `/auth/login`                           |
| 4 | Email not verified            | Open with logged-in but unverified user            | Iframe redirects to `/auth/email/verify`                    |
| 5 | No wallet created             | Open with verified user but no wallet              | Iframe redirects to `/auth/wallet/register`                 |
| 6 | Token exchange                | Use the `code` from test 1 on your backend         | Backend confirms valid session                              |

#### Test page

```html
<!DOCTYPE html>
<html>
<head><title>Auth Action Test</title></head>
<body>
  <h2>Auth Action Test</h2>
  <pre id="output">Waiting for event…</pre>

  <iframe
    id="w2"
    src="http://localhost:5173/action/auth?iframe=true"
    style="width:420px;height:700px;border:1px solid #ccc"
  ></iframe>

  <script>
    window.addEventListener("message", (e) => {
      if (e.origin !== "http://localhost:5173") return;
      document.getElementById("output").textContent = JSON.stringify(e.data, null, 2);
    });
  </script>
</body>
</html>
```

***

### `signature`

#### Example URLs

**Simple message:**

```
http://localhost:5173/action/signature?iframe=true&message=Hello%20WalletTwo
```

**With redirect:**

```
http://localhost:5173/action/signature?iframe=true&message=Verify%20my%20identity&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
```

**Long message (UI truncates after 100 chars):**

```
http://localhost:5173/action/signature?iframe=true&message=This%20is%20a%20very%20long%20message%20that%20exceeds%20one%20hundred%20characters%20and%20will%20be%20visually%20truncated%20in%20the%20signing%20UI
```

**Missing message (no-op):**

```
http://localhost:5173/action/signature?iframe=true
```

***

#### Test scenarios

| # | Scenario                      | How to trigger                               | Expected result                                                                           |
| - | ----------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------- |
| 1 | Successful sign — postMessage | Open URL with `message`, listen on `message` | Receive `{ event: "message_signed", signature, message, user, wallet }`                   |
| 2 | Successful sign — redirect    | Add `redirect_uri`                           | Iframe navigates to `redirect_uri?signature=...&usr=...&wlt=...` after 3-second countdown |
| 3 | Missing `message` param       | Open URL without `message`                   | Signing UI mounts but nothing happens. No event fired                                     |
| 4 | Signature verification        | Take `signature` and `message` from test 1   | `ethers.verifyMessage(message, signature)` returns the correct wallet address             |
| 5 | Message preview truncation    | Use a message longer than 100 characters     | UI shows first 100 chars followed by `...`                                                |

#### Test page

```html
<!DOCTYPE html>
<html>
<head><title>Signature Action Test</title></head>
<body>
  <h2>Signature Action Test</h2>
  <pre id="output">Waiting for event…</pre>

  <iframe
    id="w2"
    src="http://localhost:5173/action/signature?iframe=true&message=Please%20sign%20this%20message%20to%20confirm%20your%20identity"
    style="width:420px;height:700px;border:1px solid #ccc"
  ></iframe>

  <script>
    window.addEventListener("message", (e) => {
      if (e.origin !== "http://localhost:5173") return;
      if (e.data?.event !== "message_signed") return;

      document.getElementById("output").textContent = JSON.stringify(e.data, null, 2);

      // Optional: verify using ethers in console
      // ethers.verifyMessage(e.data.message, e.data.signature)
    });
  </script>
</body>
</html>
```

***

### `logout`

#### Example URLs

**Minimal — postMessage only:**

```
http://localhost:5173/action/logout?iframe=true
```

**With redirect:**

```
http://localhost:5173/action/logout?iframe=true&redirect_uri=https%3A%2F%2Fexample.com%2Fsigned-out
```

**Hidden iframe (programmatic sign-out, no visible UI):**

```html
<iframe src="http://localhost:5173/action/logout?iframe=true" style="display:none"></iframe>
```

***

#### Test scenarios

| # | Scenario                          | How to trigger                                        | Expected result                                                          |
| - | --------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------ |
| 1 | Successful logout — postMessage   | Open minimal URL in iframe, listen on `message`       | Receive `{ event: "wallet_logout", type: "wallet_logout" }`              |
| 2 | Successful logout — redirect      | Add `redirect_uri`                                    | Iframe navigates to `redirect_uri` (no extra params appended)            |
| 3 | Logout fires even on server error | Simulate auth server being slow                       | Event still fires (the view uses `.finally()`)                           |
| 4 | Post-logout navigation            | After event fires, open `/wallet/dashboard` in iframe | Redirected to `/auth/login` by `LoggedMiddleware`                        |
| 5 | No active session                 | Open with no logged-in user                           | Iframe redirects to `/auth/login` immediately (before logout logic runs) |

#### Test page

```html
<!DOCTYPE html>
<html>
<head><title>Logout Action Test</title></head>
<body>
  <h2>Logout Action Test</h2>
  <p>Status: <span id="status">Active session</span></p>
  <button onclick="triggerLogout()">Logout via WalletTwo</button>
  <iframe id="w2" style="display:none"></iframe>

  <script>
    const ORIGIN = "http://localhost:5173";

    window.addEventListener("message", (e) => {
      if (e.origin !== ORIGIN) return;
      if (e.data?.event === "wallet_logout") {
        document.getElementById("status").textContent = "Logged out ✓";
        document.getElementById("w2").src = "";
      }
    });

    function triggerLogout() {
      document.getElementById("w2").src = `${ORIGIN}/action/logout?iframe=true`;
    }
  </script>
</body>
</html>
```

***

### `ramp`

#### Example URLs

**Minimal (fiat onramp, all defaults):**

```
http://localhost:5173/action/ramp?iframe=true
```

**EUR fiat purchase on Polygon (chain 137):**

```
http://localhost:5173/action/ramp?iframe=true&cur=EUR&ntwk=137&use_fiat=true
```

**Crypto-only flow with external reference:**

```
http://localhost:5173/action/ramp?iframe=true&use_fiat=false&ext_id=order_8821
```

**Pre-fill amount and currency with redirect:**

```
http://localhost:5173/action/ramp?iframe=true&amt=50&cur=USD&ntwk=137&redirect_uri=https%3A%2F%2Fexample.com%2Framp-done
```

**With operator and payment method pre-selected:**

```
http://localhost:5173/action/ramp?iframe=true&op=my-operator&pm=credit_card&cur=EUR
```

**With additional bundled transaction:**

Build this URL programmatically (the JSON value must be URL-encoded):

```js
const additionalTxns = JSON.stringify([
  {
    method: "mint",
    params: ["0xUserAddress", "1000000000000000000"],
    address: "0xYourContract",
    network: "137",
    wait_tx: true
  }
]);

const url = new URL("http://localhost:5173/action/ramp");
url.searchParams.set("iframe", "true");
url.searchParams.set("ntwk", "137");
url.searchParams.set("cur", "EUR");
url.searchParams.set("additional_txns", additionalTxns);
url.searchParams.set("redirect_uri", "https://example.com/ramp-done");

console.log(url.toString());
```

***

#### Test scenarios

| # | Scenario                       | How to trigger                             | Expected result                                                                      |
| - | ------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------------------ |
| 1 | Default fiat flow              | Open minimal URL                           | Ramp UI loads after network resolves                                                 |
| 2 | Network loading spinner        | Open any URL before network state hydrates | Full-screen spinner until network is ready, then `RampProvider` mounts               |
| 3 | Invalid `additional_txns` JSON | Pass `additional_txns=not-valid-json`      | Parse error is swallowed, ramp continues with `[]`                                   |
| 4 | Non-numeric `ntwk`             | Pass `ntwk=abc`                            | `Number("abc")` → `NaN` → falls back to `undefined`, `RampProvider` uses its default |
| 5 | External reference passthrough | Pass `ext_id=order_xyz`                    | `_externalId` prop is set on `RampProvider`, confirmed in network logs               |
| 6 | Crypto-only                    | Pass `use_fiat=false`                      | `RampProvider` mounts with `useFiat=false`                                           |

#### Test page

```html
<!DOCTYPE html>
<html>
<head><title>Ramp Action Test</title></head>
<body>
  <h2>Ramp Action Test</h2>

  <iframe
    src="http://localhost:5173/action/ramp?iframe=true&cur=EUR&ntwk=137"
    style="width:420px;height:700px;border:1px solid #ccc"
  ></iframe>
</body>
</html>
```

***

### `transaction`

#### Example URLs

**Single ERC-20 transfer (Polygon):**

```js
const transactions = JSON.stringify([
  {
    method: "transfer",
    params: ["0xRecipientAddress", "1000000000000000000"],
    address: "0xERC20ContractAddress"
  }
]);

const url = new URL("http://localhost:5173/action/transaction");
url.searchParams.set("iframe", "true");
url.searchParams.set("network", "137");
url.searchParams.set("transactions", transactions);

console.log(url.toString());
```

**Two transactions batched:**

```js
const transactions = JSON.stringify([
  {
    method: "approve",
    params: ["0xSpenderAddress", "1000000000000000000"],
    address: "0xTokenContract"
  },
  {
    method: "deposit",
    params: ["1000000000000000000"],
    address: "0xVaultContract",
    wait_tx: true
  }
]);

const url = new URL("http://localhost:5173/action/transaction");
url.searchParams.set("iframe", "true");
url.searchParams.set("network", "137");
url.searchParams.set("transactions", transactions);
url.searchParams.set("redirect_uri", "https://example.com/tx-done");
url.searchParams.set("auto_forward", "true");

console.log(url.toString());
```

**With custom ABI:**

```js
const transactions = JSON.stringify([
  {
    method: "customMint",
    params: ["0xRecipient", "5"],
    address: "0xNFTContract",
    abi: [
      {
        name: "customMint",
        type: "function",
        inputs: [
          { name: "to", type: "address" },
          { name: "quantity", type: "uint256" }
        ],
        outputs: [],
        stateMutability: "nonpayable"
      }
    ]
  }
]);

const url = new URL("http://localhost:5173/action/transaction");
url.searchParams.set("iframe", "true");
url.searchParams.set("network", "137");
url.searchParams.set("transactions", transactions);

console.log(url.toString());
```

***

#### Test scenarios

| #  | Scenario                                | How to trigger                                                               | Expected result                                                                              |
| -- | --------------------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| 1  | Valid transaction — user confirms       | Build URL with valid params, click Confirm                                   | Receive `{ type: "transactions_executing" }` then `{ type: "transaction_complete" }`         |
| 2  | User cancels — iframe mode, no redirect | Open with `iframe=true`, no `redirect_uri`, click Not Now                    | Receive `{ type: "transaction_cancelled" }`                                                  |
| 3  | User cancels — with `redirect_uri`      | Open with `redirect_uri`, click Not Now                                      | Iframe navigates to `redirect_uri?status=cancelled`                                          |
| 4  | Success with `auto_forward=true`        | Include `redirect_uri` and `auto_forward=true`                               | After completion, iframe auto-navigates to `redirect_uri?status=success&txId=...&hashes=...` |
| 5  | Invalid network                         | Pass `network=9999999` (non-existent chain ID)                               | `TransactionNetworkError` screen shown                                                       |
| 6  | Missing `method` on a transaction       | Omit `method` from one transaction object                                    | `TransactionParameterError` screen shown                                                     |
| 7  | Missing `address`                       | Omit `address` from one transaction                                          | `TransactionParameterError` screen shown                                                     |
| 8  | `params` is not an array                | Pass `params: "0xAddr"` (string)                                             | `TransactionParameterError` screen shown                                                     |
| 9  | `wait_tx: false` on any tx              | Set `wait_tx: false` on one tx in a batch                                    | Whole batch is submitted without waiting for confirmation                                    |
| 10 | Network auto-switch                     | Open with a valid `network` that differs from the currently selected network | WalletTwo silently switches to the requested network before showing the confirmation screen  |

#### Test page

```html
<!DOCTYPE html>
<html>
<head><title>Transaction Action Test</title></head>
<body>
  <h2>Transaction Action Test</h2>
  <pre id="output">Waiting for events…</pre>
  <button onclick="load()">Load transaction iframe</button>
  <div id="slot"></div>

  <script>
    const ORIGIN = "http://localhost:5173";

    const transactions = JSON.stringify([
      {
        method: "transfer",
        params: ["0xRecipientAddress", "1000000000000000000"],
        address: "0xERC20ContractAddress"
      }
    ]);

    function load() {
      const url = new URL(`${ORIGIN}/action/transaction`);
      url.searchParams.set("iframe", "true");
      url.searchParams.set("network", "137");
      url.searchParams.set("transactions", transactions);

      const iframe = document.createElement("iframe");
      iframe.src = url.toString();
      iframe.style = "width:420px;height:700px;border:1px solid #ccc";
      document.getElementById("slot").appendChild(iframe);
    }

    window.addEventListener("message", (e) => {
      if (e.origin !== ORIGIN) return;
      const log = document.getElementById("output");
      log.textContent += "\n" + JSON.stringify(e.data, null, 2);
    });
  </script>
</body>
</html>
```

***

### Error state reference

| Error state                          | Action(s)     | Cause                                                                     |
| ------------------------------------ | ------------- | ------------------------------------------------------------------------- |
| Iframe shows `/auth/login`           | All           | User is not logged in                                                     |
| Iframe shows `/auth/email/verify`    | All           | User email not verified                                                   |
| Iframe shows `/auth/wallet/register` | All           | User has no wallet                                                        |
| `TransactionNetworkError`            | `transaction` | `network` param does not match any known chain ID                         |
| `TransactionParameterError`          | `transaction` | A transaction is missing `method`, `address`, or `params` is not an array |
| No `message_signed` event fired      | `signature`   | `message` param was not provided                                          |
| `additional_txns` silently ignored   | `ramp`        | JSON in `additional_txns` was malformed                                   |

***

### Quick URL builder (copy-paste into browser console)

```js
function w2Action(action, params = {}) {
  const url = new URL(`http://localhost:5173/action/${action}`);
  url.searchParams.set("iframe", "true");
  for (const [k, v] of Object.entries(params)) {
    if (v !== undefined && v !== null) {
      url.searchParams.set(k, typeof v === "object" ? JSON.stringify(v) : String(v));
    }
  }
  return url.toString();
}

// Examples
w2Action("auth");
w2Action("auth", { redirect_uri: "https://example.com/cb" });
w2Action("signature", { message: "verify me" });
w2Action("logout");
w2Action("ramp", { cur: "EUR", ntwk: 137 });
w2Action("transaction", {
  network: 137,
  transactions: [{ method: "transfer", params: ["0xAbc", "1000"], address: "0xToken" }],
  redirect_uri: "https://example.com/done",
  auto_forward: true
});
```
