# Tempo Wallet

Tempo Wallet is the first-party wallet for balances, transfers, deposits, and private balances on Tempo.

> [!NOTE]
> The reference layouts on this page compose library components with `style`. They are not shipped components, so `@tempoxyz/ds` exports no `WalletOverview` or `SendReview`. Copy the composition into your app and adapt it.

## Overview

Tempo Wallet runs at [wallet.tempo.xyz](https://wallet.tempo.xyz). You sign in with a passkey, then send, receive, deposit, and swap stablecoins, and manage private balances in Zones.

It serves two groups:

* **Account holders** who hold, send, and receive stablecoins such as pathUSD and USDC.e.
* **Developers and agents** who pay for API requests through MPP. Access keys let an app, CLI, or agent spend from the wallet within limits you approve.

## Surfaces

| Surface    | Where                                    | Build with           |
| ---------- | ---------------------------------------- | -------------------- |
| Web        | wallet.tempo.xyz on desktop browsers     | `@tempoxyz/ds`       |
| Mobile web | The same app in iOS and Android browsers | `@tempoxyz/ds`       |
| Terminal   | Tempo Wallet CLI                         | Outside this library |

No native iOS or Android app is documented. The Apple Pay deposit option is the one flow tied to a device: it needs a US debit card and an iOS device.

## Key Screens

### Overview

```tsx
import { Amount, Button, Grid, Hex, Identicon, Rows, style, Surface, Token, Tooltip } from '@tempoxyz/ds'
import { AddFunds, ArrowDownLeft, Privacy, Send, SwapHorizontal } from '@tempoxyz/ds/icons'
import { useState } from 'react'

export const fill = true
const account = '0x0b2c69c5c1bbd3f8b642c8d8a3c8a903f780f9b2'

const balances = [
  {
    address: '0x20c0000000000000000000000000000000000000',
    amount: '8420.00',
    name: 'PathUSD',
    symbol: 'pathUSD',
    units: '8,420.00',
  },
  {
    address: '0x20c000000000000000000000b9537d11c60e8b50',
    amount: '3170.32',
    name: 'Bridged USDC (Stargate)',
    symbol: 'USDC.e',
    units: '3,170.32',
  },
]

const charges = [
  { amount: '0.05', time: '9:12 AM' },
  { amount: '0.02', time: '9:10 AM' },
  { amount: '0.01', time: '9:04 AM' },
]

export function Demo() {
  const [open, setOpen] = useState(false)

  return (
    <div {...styles.page()}>
      <header {...styles.account()}>
        <Identicon address={account} aria-hidden size="32" />
        <Hex.Root value={account} {...styles.address()}>
          <Hex.Text />
          <Hex.Copy aria-label="Copy wallet address" />
        </Hex.Root>
      </header>

      <Surface>
        <p {...styles.caption()}>Total balance</p>
        <Amount currency="USD" size="xl" value="12840.32" />
        <div {...styles.actions()}>
          <Button>
            <Send />
            Send
          </Button>
          <Button variant="secondary">
            <ArrowDownLeft />
            Receive
          </Button>
          <Button variant="secondary">
            <AddFunds />
            Deposit
          </Button>
          <Button variant="secondary">
            <SwapHorizontal />
            Swap
          </Button>
        </div>
      </Surface>

      <Grid fit="large" gap="4">
        <Surface>
          <Surface.Header>
            <Surface.Title>Balances</Surface.Title>
          </Surface.Header>
          <Rows.Root>
            {balances.map((balance) => (
              <Rows.Row key={balance.address}>
                <Token address={balance.address} />
                <Rows.Info>
                  <Rows.Title>{balance.name}</Rows.Title>
                  <Rows.Description>
                    {balance.units} {balance.symbol}
                  </Rows.Description>
                </Rows.Info>
                <Amount currency="USD" value={balance.amount} />
                <Tooltip.Root>
                  <Tooltip.Trigger
                    aria-label={`Send ${balance.name}`}
                    render={<Button shape="icon" size="small" variant="text" />}
                  >
                    <Send />
                  </Tooltip.Trigger>
                  <Tooltip.Popup>Send {balance.name}</Tooltip.Popup>
                </Tooltip.Root>
              </Rows.Row>
            ))}
            <Rows.Row action="navigation" render={<a href="#zones" />}>
              <span {...styles.zone()}>
                <Privacy />
              </span>
              <Rows.Info>
                <Rows.Title>Private balance</Rows.Title>
                <Rows.Description>Held in a zone</Rows.Description>
              </Rows.Info>
              <Amount currency="USD" value="1250.00" />
            </Rows.Row>
          </Rows.Root>
        </Surface>

        <Surface>
          <Surface.Header>
            <Surface.Title>Activity</Surface.Title>
            <Button size="small" variant="text">
              View all
            </Button>
          </Surface.Header>
          <Rows.Root>
            <Rows.Row>
              <Rows.Info>
                <Rows.Title>Received from 0x3c44…93bc</Rows.Title>
                <Rows.Description>Today, 9:41 AM</Rows.Description>
              </Rows.Info>
              <Amount sign="+" symbol="pathUSD" value="250.00" />
            </Rows.Row>
            <Rows.Row action="expand" aria-expanded={open} onClick={() => setOpen(!open)}>
              <Rows.Info>
                <Rows.Title>Access Key activity</Rows.Title>
                <Rows.Description>3 transactions</Rows.Description>
              </Rows.Info>
              <Amount sign="−" symbol="pathUSD" value="0.08" />
            </Rows.Row>
            {open && (
              <Rows.Panel>
                {charges.map((charge) => (
                  <div key={charge.time} {...styles.charge()}>
                    <span>MPP charge, {charge.time}</span>
                    <Amount sign="−" symbol="pathUSD" value={charge.amount} />
                  </div>
                ))}
              </Rows.Panel>
            )}
            <Rows.Row>
              <Rows.Info>
                <Rows.Title>Sent to 0x91bd…07e1</Rows.Title>
                <Rows.Description>Yesterday, 4:18 PM</Rows.Description>
              </Rows.Info>
              <Amount sign="−" symbol="USDC.e" value="48.50" />
            </Rows.Row>
          </Rows.Root>
        </Surface>
      </Grid>
    </div>
  )
}

namespace styles {
  export const account = style({ alignItems: 'center', columnGap: '3', display: 'flex' })

  export const actions = style({
    display: 'flex',
    flexWrap: 'wrap',
    gap: '2',
    marginBlockStart: '6',
  })

  export const address = style({ minWidth: '0' })

  export const caption = style({
    color: 'gray.900',
    margin: '0',
    marginBlockEnd: '1',
    typography: 'label.14',
  })

  export const charge = style({
    alignItems: 'center',
    display: 'flex',
    justifyContent: 'space-between',
    paddingBlock: '1',
  })

  export const page = style({
    backgroundColor: 'background.primary',
    display: 'flex',
    flexDirection: 'column',
    padding: '4',
    rowGap: '4',
  })

  export const zone = style({
    alignItems: 'center',
    backgroundColor: 'grayAlpha.200',
    borderRadius: 'full',
    display: 'inline-flex',
    flexShrink: '0 !custom',
    height: '6',
    justifyContent: 'center',
    width: '6',
  })
}
```

[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Products/WalletOverview.tsx)

The overview leads with the total balance in USD, followed by the actions that move funds. Balances and Activity sit side by side on wide screens and stack when the column narrows.

* `Identicon` and `Hex` with `Hex.Copy` show the wallet address. Other Tempo Wallet users send to this address.
* `Amount` with `currency="USD"` and `size="xl"` shows the total.
* Send is the one `primary` `Button`. Receive, Deposit, and Swap are `secondary`.
* `Surface` and `Rows` list balances. Each row holds a `Token` icon, the token name, its units in `Rows.Description`, the USD value, and an icon button that starts Send for that token.
* The private balance is a `navigation` row that opens Zones.
* Grouped access key activity is an `expand` row. Its `Rows.Panel` lists each MPP charge.

This screen follows these patterns:

* [Payments](/docs/patterns/payments) for the Send, Receive, Deposit, and Swap entry points.
* [Blockchain Values](/docs/patterns/blockchain-values) for the address, token units, and USD values.
* [Tables and Lists](/docs/patterns/tables-and-lists) for balance and activity rows.

### Send Review

```tsx
import { Amount, Button, Callout, Hex, Identicon, Rows, style, Surface, Token } from '@tempoxyz/ds'
import { useEffect, useState } from 'react'

export const fill = true
const recipient = '0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc'

export function Demo() {
  const [state, setState] = useState<'review' | 'sending' | 'sent'>('review')

  useEffect(() => {
    if (state !== 'sending') return
    const timer = setTimeout(() => setState('sent'), 1200)
    return () => clearTimeout(timer)
  }, [state])

  return (
    <div {...styles.page()}>
      <Surface {...styles.panel()}>
        <Surface.Header>
          <Surface.Title>{state === 'sent' ? 'Sent' : 'Review send'}</Surface.Title>
        </Surface.Header>

        <div {...styles.hero()}>
          <Amount align="center" size="lg" symbol="pathUSD" value="250.00" />
        </div>

        <Rows.Root>
          <Rows.Row>
            <Rows.Label>To</Rows.Label>
            <span {...styles.recipient()}>
              <Identicon address={recipient} aria-hidden size="24" />
              <Hex.Root value={recipient} {...styles.hex()}>
                <Hex.Text />
                <Hex.Copy aria-label="Copy recipient address" />
              </Hex.Root>
            </span>
          </Rows.Row>
          <Rows.Row>
            <Rows.Label>Token</Rows.Label>
            <Token address="0x20c0000000000000000000000000000000000000" size="small" />
            PathUSD
          </Rows.Row>
          <Rows.Row>
            <Rows.Label>Network</Rows.Label>
            Tempo
          </Rows.Row>
          <Rows.Row>
            <Rows.Label>Note</Rows.Label>
            Invoice 1042
          </Rows.Row>
          <Rows.Row>
            <Rows.Label>Network fee</Rows.Label>
            <Amount currency="USD" precise value="0.001" />
          </Rows.Row>
        </Rows.Root>

        {state === 'sent' ? (
          <>
            <Callout intent="positive" {...styles.notice()}>
              <Callout.Icon />
              <Callout.Content>Sent 250.00 pathUSD to 0x3c44…93bc.</Callout.Content>
            </Callout>
            <div {...styles.actions()}>
              <Button.Link href="https://explore.tempo.xyz/" variant="secondary">
                View in explorer
              </Button.Link>
              <Button onClick={() => setState('review')}>Done</Button>
            </div>
          </>
        ) : (
          <>
            <p {...styles.warning()}>
              Check the address before you confirm. Funds sent to the wrong address may not be
              recoverable.
            </p>
            <div {...styles.actions()}>
              <Button disabled={state === 'sending'} variant="secondary">
                Back
              </Button>
              <Button loading={state === 'sending'} onClick={() => setState('sending')}>
                Confirm send
              </Button>
            </div>
          </>
        )}
      </Surface>
    </div>
  )
}

namespace styles {
  export const actions = style({
    columnGap: '2',
    display: 'flex',
    justifyContent: 'flex-end',
    marginBlockStart: '6',
    '@media (max-width: 40rem)': {
      flexDirection: 'column-reverse',
      rowGap: '2',
    },
  })

  export const hero = style({ paddingBlock: '6' })

  export const hex = style({ minWidth: '0' })

  export const notice = style({ marginBlockStart: '6' })

  export const page = style({
    backgroundColor: 'background.primary',
    display: 'flex',
    justifyContent: 'center',
    padding: '4',
  })

  export const panel = style({ maxWidth: '28rem !custom', width: '100% !custom' })

  export const recipient = style({
    alignItems: 'center',
    columnGap: '2',
    display: 'flex',
    justifyContent: 'flex-end',
    minWidth: '0',
  })

  export const warning = style({
    color: 'gray.900',
    margin: '0',
    marginBlockStart: '6',
    typography: 'copy.13',
  })
}
```

[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Products/WalletSendReview.tsx)

Send collects a recipient address, amount, token, and optional note. The review shows them with the network fee before you confirm, since funds sent to the wrong address may not be recoverable.

* `Amount` with `symbol` and `size="lg"` shows what the transaction transfers, in token units.
* `Rows` with `Rows.Label` lists the recipient, token, network, note, and network fee.
* `Identicon` beside `Hex` shows the recipient. `Hex` keeps both ends of the address visible, so you can compare them with the address you meant.
* `Amount` with `precise` renders a fee below one cent as `$0.001` instead of a bound such as `<$0.01`.
* Confirm send is the view's one `primary` action. It sets `loading` while the transaction submits, and a `positive` `Callout` with a `Button.Link` to Tempo Explorer replaces it afterwards.
* Below 40rem, the actions stack at full width with Confirm send on top.

This screen follows these patterns:

* [Payments](/docs/patterns/payments) for the review and confirmation step.
* [Blockchain Values](/docs/patterns/blockchain-values) for the truncated address and the fee.
* [Destructive Actions](/docs/patterns/destructive-actions) for stating what can't be undone before you confirm.

## Content

### Name Tokens as the Token List Does

Use the token list's name in labels, such as PathUSD or Bridged USDC (Stargate). Use its symbol after token amounts, such as 250.00 pathUSD. `Token` reads the same list, so the icon, name, and symbol stay consistent.

### Show USD for Holdings and Token Units for Transfers

Show balances in USD where you compare holdings. Show the token amount wherever funds move, since that is what the transaction carries. The review states 250.00 pathUSD, not $250.00.

### Label Every Address by What It Receives

The wallet uses two kinds of address. The wallet address receives from other Tempo Wallets. The Deposit crypto flow shows a separate deposit address for one chain and token.

**Do:** Label each address by purpose, such as Wallet address or Deposit address, and name the chain and
token beside a deposit address.

**Don't:** Show a bare 0x address without a label, or ask for the wallet address inside a deposit
flow.

### Say Where a Deposit Option Works

Deposit offers Buy MACH, Fiat/cards, Deposit crypto, and Claim referral code. Availability depends on your country and the enabled onramps. State a limit beside the option, such as US only for Apple Pay, rather than after you choose it.

### Group Machine Payments

An MPP session can produce several charges. Show the session as one row, such as Access Key activity with 3 transactions beneath it, and list the individual charges when the row expands.

### Mark Direction With Text and Signs

Prefix incoming amounts with + and outgoing amounts with − through the `sign` prop of `Amount`. Pair the sign with Received from or Sent to, so direction never depends on color.

### Don't Promise Recovery

Tempo can't recover a deleted passkey, and a transfer to the wrong address may be lost. Write sign-in and review copy that says so plainly. Tell people to save their passkey in a password manager that syncs across devices.

## Open Gaps

The library has no part for these wallet needs yet. Propose one as an issue in the Design System project before adding it.

* **Address field.** Nothing validates a 0x address or suggests recent recipients. Compose `Input` with `Field.Error`, or `Autocomplete` for recipients you have paid before.
* **QR scanning.** `Qr` renders a code but can't read one from the camera.
* **Mobile blockchain parts.** `@tempoxyz/ds/mobile` has no `Amount`, `Hex`, `Identicon`, `Rows`, `Token`, or `Qr` counterpart.
* **Activity grouping.** Grouped MPP rows are composed from `Rows` each time. No part names the session or its charge count.

## Related

* [Payments](/docs/patterns/payments) for send, receive, and deposit flows
* [Blockchain Values](/docs/patterns/blockchain-values) for addresses, amounts, and fees
* [Amount](/docs/components/amount) for USD and token values
* [Hex](/docs/components/hex) for addresses and hashes
* [Rows](/docs/components/rows) for balances and activity
* [Token](/docs/components/token) for token icons
