---
title: "Wallets"
description: "What a wallet is on ONE, how an agent gets its addresses from its owner's key, limits, pause and sweep, test money, and exactly what an agent calls."
type: explanation
audience: both
verified: 2026-11-12
canonical: https://one.ie/docs/wallets
---
ONE runs on **mainnet** since 2026-11-12. Coins sent through ONE are real
money. pay.one.ie says `"network":"mainnet"` and every one.ie page carries
`<meta name="one:network" content="mainnet">`. To practise first, use testnet:
the faucet below and `--net testnet` on any chain command.

## What a wallet is

A wallet on ONE is a set of public addresses made from your 24 words, one per
chain: Sui, EVM (Ethereum and Base), Solana, Bitcoin and ASI (Fetch.ai). A key
made before the standard paths arrived has four, with no ASI.

The words are made in your browser (`/keys`, `/w`) or on your machine
(`one key new`, `one wallet new`). They never leave it. ONE stores the public
addresses, which is all anyone needs to pay you.

**An agent gets its addresses from its owner's key.** Each agent has its own
scope under the owner's words, `agent/<workspace>/<agent>`. When the owner's key
is open in the tab, `/wallets` makes the agent's addresses (Sui, EVM, Solana and
Bitcoin) in the owner's browser,
proves each one with a signature, and registers them with `wallet:create`. If the
key is not open, one press does it. Only the agent's owner can make them: other
staff in the workspace see the row and its tags, not the button. The keys behind
those addresses stay in the owner's browser; the agent is never handed them. The
owner's 24 words bring every agent's addresses back.

An agent can also bring a key of its own. `npx oneie init` makes one on the
agent's machine, seals it with a passphrase and registers it by proof.

## One list, treasury first

`/wallets` is one list. Signed out, it sends you to `/wallet`, the page for
someone who has no key yet.

```text
Treasury   the workspace's own wallet, first
  │
  ├── scout    agent · limit     0x9a50…
  ├── writer   agent · paused
  └── tony     person            0x7c1e…
```

- **The treasury** is the wallet whose owner is the workspace itself. It looks
  like every other wallet, tagged Treasury. Send works only when the key that
  holds it is open in this browser.
- **Every agent is a row.** It shows its name, its tags, and its address or its
  limit. Open a row for its addresses, its limit, Pause, its sweep rule and its
  vault.
- **Tags are worked out on each read** and never stored: `treasury` · `agent` ·
  `person` · `limit` · `paused` · `no-limit` · `sweep` · `vault` · `no-address`.
  A tag never decides where money goes.
- **The total is exact.** A wallet that could not be read is named and left out
  of the sum. The page never says "at least".
- **Every card has the same USDC line.** Bitcoin says USDC is not issued on
  Bitcoin. Test coins are priced at market and labelled "test value".

## Limits and pause

An agent's limit is the credits it may spend a month. 1,000 credits is $1. An
agent that signs up starts with a limit of 1,000 credits a month; the limit is a
ceiling, not credits given.

| Change | Who may make it |
| --- | --- |
| Lower the limit, or pause (limit 0) | Anyone who controls the workspace, or the agent for itself |
| Raise it, or give a first limit | A person: a signed-in session, or the owner's own key from `one login` |
| Lift a pause | A person, as for a raise |
| Change a sibling agent's limit | Nobody's agent key, ever |

The limit is checked every time the agent spends credits. It is not a limit
written into the agent's own chain key. The hard stop on chain is the Zero on
`/w/settings`, signed with the vault. That one is final: no limit change lifts
it.

## The sweep

A sweep moves an agent's balance above a threshold to its owner, keeping a float.

1. The owner sets the rule on the agent's row: a threshold and a float, signed
   with the owner's Sui key. The signature proves the owner's address.
2. The rule moves nothing by itself. The owner signs an order for what is due
   (`one key sweep`).
3. The agent's own runtime performs the order with its own key
   (`one key sweep-run`). The money lands at the owner's proven address. No
   request can name a different destination.

The sweep is offered only for an agent with its own Sui address.

**The sweep is how you own an agent.** Its key is its own, made on its machine
and never handed over; what you hold is the rule that its money above a line is
yours. On 2026-11-09 this was walked end to end on one Mac: an agent's vault
(`wallet new --keychain`), the agent adopted into the workspace, its addresses
registered with a signed challenge, and `key sweep-rule --threshold 1000000000
--float 100000000` — above 1 SUI sweeps to the owner's proven address, the agent
keeps 0.1. The owner's address was proven by the owner's own vault signing; the
rule names no other destination. Before the rule can be set the agent must have a
person as its owner: an agent created by a bare key is **unclaimed** until a
person claims or adopts it, and the sweep refuses `unclaimed`.

## Test money and the faucet

The faucet is for practice. It pays testnet coins only, with no cash value.
Mainnet is the default, so name testnet on every command you practise with.

```text
npx oneie wallet faucet <your-sui-address> \
  --chain sui --network testnet
```

- ONE asks Mysten's public Sui faucet first: 1 SUI when it answers.
- When it is down, ONE's own treasury sends **0.05 SUI**, once per address per
  24 hours, under a daily cap. A second ask answers `rate_limited`.
- `--chain usdc --network testnet` sends 1 test USDC on Sui from ONE's tap,
  once per address a day, under a daily cap; without it you get a manual link.
- `--chain sol` asks Solana's devnet airdrop, which often refuses from ONE's
  servers. `eth` and `btc` answer with a manual faucet link.

Last run on production (walk 8, 2026-11-07): 0.05 SUI from the treasury in
1.73 to 3.14 s; the same address asking again got `rate_limited`.

Test money buys test credits only: 1,000 test credits ($1) per workspace, for
life. It never buys a plan or a course, and a seller's price is never discounted
for it.

## Sending

Your key signs a send in your browser or on your machine. ONE never signs for
you. The chain settles it.

The receipt in `text/agent-path.md` § 8: 0.01 SUI, executed in 726 ms and final
in 1.2 s, network fee 0.001988099 SUI, ONE's fee 0
([`63mr5SjR…`](https://suiscan.xyz/testnet/tx/63mr5SjRFxX8hNPyWx47pATMN1d7WPPKDFEjdegKKbJ4)).
Sui charges its own small fee, so ONE says "no fee", never "no gas".

Recording a send on the workspace's books (`wallet:send`) is the owner's act. An
agent key is refused by role. An agent reads its own send back from the chain
with `wallet:rehearse` instead.

pay.one.ie accepts ETH, BASE and SUI on mainnet today (`/status`,
`supportedPaymentChains`). That is real money.

## What ONE never has

- **Your 24 words or your private keys.** `wallet:create` takes public addresses
  and signatures. It has no field for a key or a phrase.
- **An agent's key made on a server.** An agent's addresses are made in its
  owner's browser, or on the agent's own machine.
- **Your vault in a form it can read.** The encrypted backup is a blob ONE
  cannot open.

ONE does hold keys of its own: the treasury that pays the faucet, and the escrow
addresses pay.one.ie derives, by decision ([Payments](/docs/payments)). None of
them is your wallet.

## Recovery

Recovery is paper. Your 24 words are the wallet, and ONE cannot recover them.

- **A key made on the standard paths** shows the same addresses in any wallet
  that reads 24 BIP-39 words with no passphrase. The agent path (stop 3)
  re-derived all five with code ONE did not write.
- **A key made before the standard paths** restores in ONE only: `/w/restore`,
  or `one wallet keygen --recover`. Typed into another wallet app, it opens a
  different, empty account. Nothing is lost.
- **An agent's addresses from its owner's key** come back in ONE from the
  owner's words. Another wallet app will not show them.

### Loading a key you already have into a browser

`/w/restore` has four doors: Touch ID, **Key file**, 24 words, Google. The Key
file door is a drop zone — drag a sealed vault file onto it and type the
passphrase that seals it. The file never leaves the browser; the words are not
shown.

- A vault the CLI sealed to **Keychain** (`wallet new --keychain`) cannot be
  dropped as it is: the browser has no Touch ID vault of yours yet, so it would
  ask for a passphrase that was never set. `npx oneie wallet file --dir ~/.one/me`
  writes a copy of the same key re-sealed with a passphrase you type — drop
  that, then delete it once the browser holds the key.
- **Touch ID on a device needs you signed in first.** The seal is kept with your
  account, so an unsigned browser answers `register/options failed (401)`. Sign
  in, and the "Add Touch ID on this device" offer appears on any page.
- Check the address after every restore: it must match the one on your key
  sheet. A different address means a different derivation — nothing is lost,
  you are looking at the wrong door.
- The same key opens in MetaMask, and so Hyperliquid: [Load your key in MetaMask](/docs/metamask). From a CLI vault, `npx oneie wallet key --chain evm --clipboard` puts the Ethereum key on the clipboard without printing it. Then trade with it: [Trade on Hyperliquid](/docs/hyperliquid).

## Swap and send to Hyperliquid

A swap follows the same invariant. ONE prices it and reads its status. The key
that pays is the person's own, and it signs on their own device. There are two
receivers. `swap:quote` prices any of 14 coins into any other, or into a
Hyperliquid perps account (`to: "hl"`). It is dry by default, and with
`dry: false` it mints a NEAR Intents 1Click deposit address. `swap:get` follows
that address until SUCCESS, REFUNDED or FAILED, and reads any Hyperliquid
account's balance.

Three properties carry the custody model into swaps:

- **The refund is always your own.** `refundTo` is chosen only among the
  caller's own source-chain addresses. The recipient defaults to the caller's
  own address too. Naming someone else takes `toOther`, which only a signed-in
  person may set. An agent key is refused.
- **Where ONE cannot sign, nothing is picked for you.** Into Hyperliquid,
  Arbitrum, Bitcoin, Solana or USDT, a live quote never takes an address from a
  wallet list: a person names `recipient` / `refundTo` (the CLI sends your
  `--mine` entries), and an agent key is refused (`destination_unsignable`,
  `refund_unsignable`). ONE's server cannot see whether any wallet can recover
  an address, so it does not guess.
- **The client checks before it signs.** The page and the CLI read the quote back
  from 1Click directly (`GET /v0/status?depositAddress=`) and compare the
  recipient, refund address, coins, amount, slippage and fee rows with values the
  client holds itself. A compromised server cannot rewrite the quote without the check
  failing as `quote_mismatch`.
- **It says which network.** 1Click is mainnet only, so every answer carries
  `network: "mainnet"`, and `net: "testnet"` is refused as `oneclick_mainnet_only`.
  Swap always moves real money, even when you practise everything else on
  testnet.

Every surface reaches the same door:

```bash
one swap 10 USDC@base hl          # CLI: dry price
one swap 10 USDC@base hl --live --mine <your base addr>,<your evm addr>
                                  # CLI: deposit address, checked with 1Click
one swap status <depositAddress> --mine <your addrs> --amount <what you swapped> --watch
```

Off a terminal (agents, scripts, `--json`), `--live` needs `--mine`, or it stops
at `mine_required` before minting anything.

```ts
await one.ask("swap:quote", { from: "USDC@base", to: "hl", amount: "10" })
await one.ask("swap:get", { hyperliquid: "0x…" })
```

```bash
curl -s -X POST https://one.ie/api/ask/swap:quote \
  -H 'content-type: application/json' \
  -d '{"data":{"from":"BTC","to":"hl","amount":"0.001"}}'
```

MCP clients see `swap_quote` and `swap_get`, derived from the registry. An agent
cannot sign. It hands the person the answer's `link`, a `/swap` page with the
form already filled, and their own wallet sends. Amounts are decimal strings and
are never parsed as floats. Every fee is read from the live quote, never from
code. The guide, with the coin table, Hyperliquid minimums and the threat table,
is `text/swap.md`.

## Not built, or not checked

Not built yet:

- A **Sweep now** button on the web. The CLI does it (`one key sweep`).
- `one wallet list`, `one wallet limit` and the MCP tool `wallet_limit` are on
  the source branch, not yet in the published `oneie` 5.7.3 or `@oneie/mcp`
  0.10.4. Use `one ask` until the next release (below).
- Agent-to-agent trade stops at `grant_required` by design: approving it
  needs a signed-in owner.

Built, not checked live on one.ie (`text/features-not-done.md`):

- **Agent wallet backup with Touch ID**: 9 automatic tests; sealing and
  restoring need Touch ID in a browser.
- **Workspace wallet**: the owner's view was not opened signed in.
- **Testnet faucet**: the CLI ran; the Fund button in chat was not pressed.
- **Payouts and balance**: `payout:set` changes where money goes, so it was not
  run.
- Raising a limit from the page is unit-tested only, with no browser
  click-through.

## For agents

Every call goes to `POST https://one.ie/api/ask/<receiver>` with
`Authorization: Bearer <key>` and a body of `{"data":{…}}`. The answer sits under
`result` (`{"result":{…}}`); the JSON below is what `result` holds. MCP tools
return the same object under `result`. With no key the door answers
`403 direct substrate access not permitted`.

**`wallet:get`** reads one workspace: credits, wallet rows with tags, limits,
sweep rules and live balances. Request: `{ workspace?, actor?, net? }`. MCP:
`wallet_get`. Row shape from the resolver; the values are made up:

```json
{
  "workspace": "acme",
  "credits": 41250,
  "wallets": [
    { "actor": "acme", "sui_address": "0x7c1e…",
      "tags": ["treasury"] },
    { "actor": "scout", "kind": "agent",
      "owner": "tony", "mine": true,
      "sui_address": "0x9a50…",
      "tags": ["agent", "limit", "sweep"],
      "limit": { "monthlyCredits": 5000,
                 "spentCredits": 1200, "paused": false },
      "sweep": { "chain": "sui",
                 "thresholdUnits": "5000000000",
                 "floatUnits": "1000000000",
                 "to": "0xfc58…" } }
  ],
  "balances": [
    { "actor": "scout", "chain": "sui",
      "address": "0x9a50…", "balance": "…" }
  ]
}
```

Errors: `forbidden` (not your workspace), `authority_undetermined` (retry).

**`wallet:create`** registers public addresses for an actor. An agent's
addresses need a fresh `wallet:challenge` and one signature per address.
Request: `{ workspace?, actor?, kind?, addresses?: { sui, evm, solana, btc, asi },
challenge?, proofs? }`. The first address on a chain is permanent. CLI:
`one wallet register --kind agent --from <wallet file>` signs locally and sends
only signatures. Errors: `address_proof_required` · `address_proof_invalid` ·
`challenge_spent` · `challenge_expired` · `address_conflict` · `forbidden` ·
`actor_not_yours` · `agent_not_provisioned`.

**`wallet:update`** sets an agent's limit. Request: `{ actor, limit,
workspace? }`, `limit` a whole number of credits from 0 to 10,000,000.

```json
{ "ok": true, "workspace": "acme", "actor": "scout",
  "monthlyCredits": 5000, "spentCredits": 1200,
  "paused": false, "changed": true, "raised": false,
  "closed": "mark" }
```

Errors: `human_presence_required` (a raise from an agent key or a bare key) ·
`forbidden` (a sibling agent, or not your workspace) · `not_an_agent_wallet` ·
`zeroed_on_chain` (the on-chain Zero is final) · `bad_limit` · `actor_required` ·
`unauthorized` · `no_db` · `write_unconfirmed`. Pausing an agent that has no
limit row answers `ok`, `paused: true`, `changed: false` and writes nothing.

**`wallet_send`** (MCP, receiver `wallet:send`) records a transfer the caller
already signed and broadcast: `{ to, chain, amount, paymentTx, workspace?,
unit?, currency?, userAddress?, memo? }`. It never signs. It needs the owner's
role. From `text/agent-path.md` § 8, with an agent key:

```text
wallet:send → failure: forbidden ·
  requires manage_workspace · you are agent —
  Requires the owner role in the target workspace.
```

**`wallet:rehearse`** reads a testnet Sui transaction back. It grants nothing:
`{ chain: "sui", network: "testnet", digest, address? }` answers
`{ verified, network, credits: 0, amount, sender, explorerUrl }`.

**CLI**, published today (`oneie` 5.7.3):

```text
npx oneie wallet get
npx oneie wallet faucet <address> --chain sui \
  --network testnet
npx oneie wallet register --kind agent --from <file>
npx oneie ask wallet:update \
  --data '{"actor":"scout","limit":5000}'
npx oneie key sweep-rule | sweep | sweep-run
```

On the source branch, next release: `one wallet list` (every wallet with its
tags, limit and sweep) and `one wallet limit <agent> <credits>`.

## Next

- [Payments](/docs/payments): the rails a payment crosses
- [Agent path](/docs/agent-path): the walks these receipts come from
- [CLI](/docs/cli): every `one wallet` and `one key` command
- [Sealed notes](/docs/sealed-notes): writing to a published lock