Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
96a6d34
chore: rename `tinywallet` to `tinywallet_bus` across all wallet and …
senamakel Aug 23, 2026
4494d0e
chore(deps): replace tinywallet with tinywallet-bus contract crate
senamakel Aug 23, 2026
f49bb75
chore(deps): update tinywallet subproject commit
senamakel Aug 23, 2026
36b2a9f
chore(deps): update Cargo.lock for tinywallet-bus extraction
senamakel Aug 23, 2026
e2a08b0
refactor(wallet): replace inline method strings with constants from t…
senamakel Aug 23, 2026
95358d7
test(wallet): add contract tests for registry entry and method member…
senamakel Aug 23, 2026
1f1ba79
docs(AGENTS): document the tinywallet-bus extraction and its design r…
senamakel Aug 23, 2026
28a5fcc
docs(AGENTS.md): clarify bus crate description and update tinydocs re…
senamakel Aug 23, 2026
49e9deb
chore(registry): update tinywallet module to version 0.5.0
senamakel Aug 23, 2026
1f945e8
chore(tinywallet): update subproject commit
senamakel Aug 23, 2026
9860606
chore(deps): bump tinywallet and tinywallet-bus to 0.5.0
senamakel Aug 23, 2026
97b7799
chore(abi): reformat error mapping in encode_erc20_transfer
senamakel Aug 23, 2026
705c302
chore(deps): update tinywallet dependency to tinywallet-bus v0.5.0
senamakel Aug 23, 2026
46b519b
chore: files changed src/openhuman/modules/wallet_tests.rs
senamakel Aug 23, 2026
605276f
chore: update doc references from `tinywallet` to `tinywallet-bus` an…
senamakel Aug 23, 2026
ce4ad92
chore(docs): clarify feature-gating and dependency notes
senamakel Aug 23, 2026
c940dd0
fix(tests): update comment to reflect tinywallet crate location
senamakel Aug 23, 2026
a4fc0ca
fix(tests): reword comment in unhardened_paths_are_rejected
senamakel Aug 23, 2026
eebac33
fix(ops): correct incomplete sentence in evm_payment_payload doc
senamakel Aug 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 35 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ submodules consumed by `path` (not published to crates.io, so no
`[patch.crates-io]` entry — same shape as `tinyhumans-sdk`). After cloning:
`git submodule update --init vendor/tinydocs vendor/tinywallet`.

**What this binary takes from each repository is its `-bus` contract crate, not
its root crate.** A `-bus` crate is transport-free and holds the interface name,
the object path, one constant per member, the payload types and the contract
version — plus the pure rules a host genuinely runs itself. The root crate holds
the implementation the TinyBus module carries, and this binary does not link it.
`vendor/tinywallet/crates/tinywallet-bus` is the entry here; the same shape as
`tinyvoice-bus`, `tinyjuice-bus`, `tinyruntime-bus` and `tinydocs-bus`.

The split follows one rule, and it is worth stating because it decides where
the *next* extraction goes: **a crate owns what is the same for every host; the
host owns what depends on its own runtime, config, or threat model.** Both
Expand All @@ -215,7 +223,7 @@ crates are therefore synchronous, I/O-free, and runtime-free.
| Crate | Owns | OpenHuman keeps |
| --- | --- | --- |
| `tinydocs` | the `.docx` spec types, their size limits, validation, and OOXML synthesis (`docx-rs` sits behind it) | the artifact pipeline, the `spawn_blocking` hop, and the generation deadline — `src/openhuman/tools/impl/document/` |
| `tinywallet` | the BTC / EVM / Solana / Tron address formats: parsing, validation, encoding conversions | RPC endpoint resolution, transaction assembly and broadcast, key custody — `src/openhuman/web3/` |
| `tinywallet-bus` | the TinyWallet wire contract and bus member names, the BTC / EVM / Solana / Tron address formats, the EIP-712 and ERC-20 encoders, and the Tron verification codec | RPC endpoint resolution, transaction assembly and broadcast, key custody — `src/openhuman/web3/` |

Consequences worth knowing before touching either seam:

Expand All @@ -232,7 +240,7 @@ Consequences worth knowing before touching either seam:
`tinydocs`' `DocumentSpec` re-exported under its historical name, with field
names unchanged; `the_json_wire_shape_is_unchanged_by_the_extraction` pins
that.
- **`tinywallet` rejects an uppercase `0X` EVM prefix, matching the code it
- **`tinywallet-bus` rejects an uppercase `0X` EVM prefix, matching the code it
replaced, which rejected that prefix too.** The old path went through `ethers_core::types::Address`'s
`FromStr`, which is `fixed-hash`'s and strips only a lowercase `0x`
(`fixed-hash-0.8.0/src/hash.rs`, `input.strip_prefix("0x")`), so `0X…` failed
Expand All @@ -241,11 +249,31 @@ Consequences worth knowing before touching either seam:
- **Bitcoin has two rules, not one.** `btc::validate` is the recipient rule;
`btc::validate_sender` additionally requires P2WPKH. Using the first where
the second belongs accepts an address that only fails later, at signing time.
- **Each crate's gates ride OpenHuman's existing ones**: `tinydocs` is
exclusive to `documents`, `tinywallet` to `web3`. Both are default-ON and
already forwarded to the desktop shell. Note `tinydocs` is now taken with
`default-features = false` — the wire contract, not the writers, which run in
the TinyBus module instead (see the module host section).
- **`tinywallet-bus` holds logic, not only types, and that is deliberate.** Four
rules are the host's to run synchronously: validating an address before a spec
is sent (a rejected input rather than a failed call), hashing EIP-712 typed
data for the x402 payment path, encoding ERC-20 calldata, and verifying the
txid and contents of what a Tron node handed back. That last one is not
optional — Tron has the *node* build the transaction, so the check has to
happen wherever the decision to sign is made. Same precedent `tinydocs`'
spec validators set: a bus crate carrying host-side rules is established here,
not a novelty.
- **Member names come from `tinywallet_bus::names::methods`, never a literal.**
`src/openhuman/modules/wallet.rs` calls by constant, and
`wallet_tests.rs`'s `contract` module pins `registry.rs`'s `bus_name` /
`object_path` against `BUS_NAME` / `OBJECT_PATH` and every member it sends
against `METHODS` + `CONFIDENTIAL_METHODS`. The registry is a compiled-in
`const` table that cannot name a gated crate, so a drifted string is a
`NameHasNoOwner` in the field rather than a compile error.
- **The root `tinywallet` crate survives as a dev-dependency only.** Test
fixtures derive a known account through its `key` gate. Cargo does not link
dev-dependency features into the shipped binary, so this does not put
`bitcoin`, `coins-bip39` or a native `secp256k1` build back into the product.
- **Each crate's gates ride OpenHuman's existing ones**: the tinydocs entry is
exclusive to `documents`, `tinywallet-bus` to `web3`. Both are default-ON and
already forwarded to the desktop shell. Both are taken with
`default-features = false` — the wire contract, not the implementation, which
runs in the TinyBus module instead (see the module host section).

### Backend API access — `src/api/` over `tinyhumans-sdk`

Expand Down
24 changes: 18 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 21 additions & 19 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -523,24 +523,26 @@ tinyhosts = { path = "vendor/tinyhosts", default-features = false, features = ["
# published to crates.io, so there is no `[patch.crates-io]` entry for it.
# After cloning: `git submodule update --init vendor/tinywallet`.
#
# Optional: exclusive to the default-ON `web3` feature.
# Taken WITHOUT `tx` or `client`, which is the whole point: those are the only
# gates that pull the `bitcoin` crate and its native secp256k1 build, and
# transaction building now happens in the loaded `tinywallet` module instead.
# What stays is address validation, key derivation, the `Transport` seam, the
# wire contract, EIP-712 hashing and ERC-20 calldata — none of which needs a
# chain library.
# Key derivation (`key`) and transaction signing (`tx`) are deliberately absent:
# both happen inside the loaded tinywallet module now, so this binary links
# neither. What is left is the wire contract, address validation, the EIP-712 /
# ABI encoders the x402 payment path builds with, the `Transport` seam, and
# `tx-codec` — the Tron *verifier*, which runs host-side before a transaction is
# sent for signing and needs no `bitcoin` build.
#
# `key` is re-enabled under [dev-dependencies] so test fixtures can still derive
# a known account. Dev-dependency features are not linked into the shipped
# binary, so that does not undo the shed.
tinywallet = { path = "vendor/tinywallet", default-features = false, features = ["btc", "evm", "solana", "tron", "keccak", "net", "wire", "eip712", "abi", "tx-codec"], optional = true }
# Optional: exclusive to the `web3` feature — OFF in the contributor set
# (`[features] default`) and ON in the product set
# (`scripts/ci/product-features.txt`), so it ships but a bare `cargo check` does
# not pay for it. See the two-set note above `default`.
#
# Taken as `tinywallet-bus`, the contract crate, and NOT the root `tinywallet`
# crate. The root crate is where key derivation, transaction building, signing
# and the chain clients live, and those are the gates that pull the `bitcoin`
# crate and its native secp256k1 build; all of it happens inside the loaded
# `tinywallet` module now, so this binary links none of it. What the contract
# crate carries is exactly what a host still runs itself: the wire types that
# cross the bus, the bus member names, address validation, the EIP-712 and ERC-20
# encoders the x402 payment path builds with, the `Transport` seam this crate
# implements, and the Tron verifier that checks what a node handed back before
# a transaction is sent for signing.
#
# The root crate is still taken under [dev-dependencies], where test fixtures
# derive a known account. Dev-dependency features are not linked into the
# shipped binary, so that does not undo the shed.
tinywallet-bus = { path = "vendor/tinywallet/crates/tinywallet-bus", default-features = false, features = ["btc", "evm", "solana", "tron", "keccak", "net", "wire", "eip712", "abi", "tx-codec"], optional = true }

# secp256k1 signing over the digests the wallet module hands back. Pure Rust,
# and already in the graph beneath `coins-bip32` (which derives the key being
Expand Down Expand Up @@ -848,7 +850,7 @@ voice = [
# to derive a Solana ATA, which is address arithmetic rather than signing, and
# tinyplace pulls it in through `ed25519-dalek` regardless.
web3 = [
"dep:tinywallet",
"dep:tinywallet-bus",
"dep:curve25519-dalek",
"modules",
]
Expand Down
6 changes: 3 additions & 3 deletions app/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 28 additions & 26 deletions src/openhuman/modules/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@ const TINYDOCS: ModuleRecord = ModuleRecord {
///
/// **This host sends it the recovery phrase, over confidential calls, and never
/// derives or signs itself.** All four chains — Bitcoin, EVM, Solana and Tron —
/// derive and sign inside the module. This binary links neither `tinywallet`'s
/// `key` feature nor `k256`; see the note on the `tinywallet` dependency.
/// derive and sign inside the module. This binary does not link the root
/// `tinywallet` crate at all — it takes `tinywallet-bus`, the wire contract,
/// which carries no `key` gate — nor does it link `k256`; see the note on the
/// `tinywallet-bus` dependency.
///
/// The phrase is only sent to a module tinybus has attested *and* whose digest
/// matches one of the entries below — `super::wallet::attested_proxy` checks
Expand All @@ -131,63 +133,63 @@ const TINYWALLET: ModuleRecord = ModuleRecord {
description: "Transaction building and assembly for Bitcoin, EVM, Solana and Tron",
bus_name: "ai.tinyhumans.tinywallet.Wallet",
object_path: "/ai/tinyhumans/tinywallet/Wallet",
version: "0.4.0",
release_url: "https://github.com/tinyhumansai/tinywallet/releases/tag/v0.4.0",
version: "0.5.0",
release_url: "https://github.com/tinyhumansai/tinywallet/releases/tag/v0.5.0",
assets: &[
PlatformAsset {
host_key: "ubuntu-24.04-x86_64",
archive: "tinywallet-module-0.4.0-ubuntu-24.04-x86_64.tar.gz",
sha256: "737a18c258bb9013ad85006433c72a5dc83b94de8f15a0d37723a3b96cf047fa",
archive: "tinywallet-module-0.5.0-ubuntu-24.04-x86_64.tar.gz",
sha256: "03906b3e2bb6f24a230e29eefc916299d0e9269c166c8766c12769545fbe602d",
},
PlatformAsset {
host_key: "ubuntu-24.04-arm64",
archive: "tinywallet-module-0.4.0-ubuntu-24.04-arm64.tar.gz",
sha256: "72217d4f4dc1a2328de08c83d24998cd51729e8157cd2e9cb3b034ec1da2ea94",
archive: "tinywallet-module-0.5.0-ubuntu-24.04-arm64.tar.gz",
sha256: "8630d4d3bd49047606b19b53cc1c16eaf114ee12a7693ce14882c395cd6141de",
},
PlatformAsset {
host_key: "ubuntu-22.04-x86_64",
archive: "tinywallet-module-0.4.0-ubuntu-22.04-x86_64.tar.gz",
sha256: "e7d2d1a40331b5fea1dc9d8870c206d093c756af91790a15e3fcc9fc1b160158",
archive: "tinywallet-module-0.5.0-ubuntu-22.04-x86_64.tar.gz",
sha256: "a680eb8e52caa6e367c914f0c08505569022362bff3b7bcd8ca79f931fcc12bf",
},
PlatformAsset {
host_key: "ubuntu-22.04-arm64",
archive: "tinywallet-module-0.4.0-ubuntu-22.04-arm64.tar.gz",
sha256: "248fd13ba59ab9c00ccd605b60c533aabd41be0f82cd167758524842122510f1",
archive: "tinywallet-module-0.5.0-ubuntu-22.04-arm64.tar.gz",
sha256: "7e38bde187aba01cacac78c86fa2b29526f8feabb21c4992a1d240763f509aea",
},
PlatformAsset {
host_key: "macos-26-arm64",
archive: "tinywallet-module-0.4.0-macos-26-arm64.tar.gz",
sha256: "e6df7dc830d595a63af6864cbec6e3e22e51f35af558e7b62fa655d6b16d0581",
archive: "tinywallet-module-0.5.0-macos-26-arm64.tar.gz",
sha256: "40ff703a3f609db1b40083e1f03f3291a88a838b931602ebd19116c8dbaedf64",
},
PlatformAsset {
host_key: "macos-26-x86_64",
archive: "tinywallet-module-0.4.0-macos-26-x86_64.tar.gz",
sha256: "fd197ac908057b9b5b4c7aef1b86e74ea7369133eff2a4835c310c73e7816a01",
archive: "tinywallet-module-0.5.0-macos-26-x86_64.tar.gz",
sha256: "1d6a035bcf5a94591023536b974a5b594acec6c83939b2505730efe8cf1ae530",
},
PlatformAsset {
host_key: "macos-15-arm64",
archive: "tinywallet-module-0.4.0-macos-15-arm64.tar.gz",
sha256: "28a56ed94827b46a972c054b07e614684b7217d8f8c69373e93b957de336901b",
archive: "tinywallet-module-0.5.0-macos-15-arm64.tar.gz",
sha256: "3a56c28c29a4c9047be3fd730c28e7a8b07e27cd3655b5c2ff2832e762d2bf1a",
},
PlatformAsset {
host_key: "macos-15-x86_64",
archive: "tinywallet-module-0.4.0-macos-15-x86_64.tar.gz",
sha256: "2e97717f08efefb90a8be51f389cbf826fb132fc7111f11837e9ee717c527e58",
archive: "tinywallet-module-0.5.0-macos-15-x86_64.tar.gz",
sha256: "77e99f160f435cbf227d91a41738849d1d26f3ce0b607c73d32e505c54e5aa84",
},
PlatformAsset {
host_key: "windows-2025-x86_64",
archive: "tinywallet-module-0.4.0-windows-2025-x86_64.zip",
sha256: "c9393d6c0f171db34298950ad029c21ea6b41f3f77971cf6668ebbd7f34736b7",
archive: "tinywallet-module-0.5.0-windows-2025-x86_64.zip",
sha256: "9e677b63f3371728cf783cd7439f680d837e654e40eb42d6b1f12ec8dce7965a",
},
PlatformAsset {
host_key: "windows-2022-x86_64",
archive: "tinywallet-module-0.4.0-windows-2022-x86_64.zip",
sha256: "8ed5e86977f951a8c54dbde82914f6f936d4402564beb30406f4140d4be02872",
archive: "tinywallet-module-0.5.0-windows-2022-x86_64.zip",
sha256: "4fc049696ef9897a3aada0f4322b70b98d561b689bb51f90ebe30a9916f47fba",
},
PlatformAsset {
host_key: "windows-11-arm64",
archive: "tinywallet-module-0.4.0-windows-11-arm64.zip",
sha256: "7854dfeb1dd04afe99488616e223a0f3ce6d7c671e22f8fbc3089eb0523cbf51",
archive: "tinywallet-module-0.5.0-windows-11-arm64.zip",
sha256: "d22513e74c435ac541b1827c17c598c87df00fc2526e75afbdd18bdaf71c002b",
},
],
load: LoadPolicy::Lazy,
Expand Down
11 changes: 6 additions & 5 deletions src/openhuman/modules/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@
//! with which it is, and [`sign_payload`] dispatches on the tag rather than on
//! the chain — so a chain that changes scheme cannot silently sign wrongly.

use tinywallet::wire::{
use tinywallet_bus::names::methods;
use tinywallet_bus::wire::{
DerivedAccount, ExportRequest, ExportedKey, Scheme, SecretMaterial, SignMessageRequest,
SignRequest, Signature, SignedTransaction, TransactionSpec,
};
Expand Down Expand Up @@ -125,7 +126,7 @@ pub async fn sign_transaction_in_module(
);
proxy
.call_confidential(
"SignTransaction",
methods::SIGN_TRANSACTION,
(SignRequest {
secret: secret.clone(),
transaction: transaction.clone(),
Expand All @@ -150,7 +151,7 @@ pub async fn derive_account(
secret.chain
);
proxy
.call_confidential("DeriveAccount", (secret.clone(),))
.call_confidential(methods::DERIVE_ACCOUNT, (secret.clone(),))
.await
.map_err(|error| classify(&error))
}
Expand Down Expand Up @@ -181,7 +182,7 @@ pub async fn sign_message(
);
proxy
.call_confidential(
"SignMessage",
methods::SIGN_MESSAGE,
(SignMessageRequest {
secret: secret.clone(),
message_hex: hex(message),
Expand Down Expand Up @@ -213,7 +214,7 @@ pub async fn export_key(
);
proxy
.call_confidential(
"ExportKey",
methods::EXPORT_KEY,
(ExportRequest {
secret: secret.clone(),
},),
Expand Down
Loading
Loading