Ship on Sapphire.
Operate on ROFL.
A typed SDK, a scriptable CLI and direct access to confidential signing, hardware custody and TEE-attested agents — wired into the same primitives the desktop wallet uses.
From zero to a signed transaction in two minutes.
Install the CLI, link your wallet keystore, then sign a Sapphire transaction with one command. No bridge daemons, no browser extension.
"tok-com"># macOS · Linux · Windows (WSL)
$ curl -fsSL https:"tok-com">//oasiswallet.io/install.sh | sh
$ oasis --version
oasis 3.2.0 (sapphire, rofl, ledger)
$ oasis wallet link ~/Library/Application\ Support/OasisWallet
✓ linked · 4 accounts available$ oasis tx send \
--paratime sapphire \
--to 0x9a7c…1f04 \
--amount 1240 ROSE \
--confidential \
--signer ledger:0
▶ encrypting payload (Sapphire)
▶ awaiting on-device approval on Ledger Nano X
✓ signed · broadcast in 1.84s
hash 0x4f2c8a01…b81dA scriptable surface for every wallet primitive.
The oasis CLI exposes every action the wallet UI performs — accounts, signing, staking, ROFL ops — with stable JSON output for CI and treasury automation.
$ oasis accounts list --json
[
{
"name": "treasury",
"paratime": "sapphire",
"address": "0x9a7c…1f04",
"balance": "12,480 ROSE",
"signer": "ledger:0"
}
]$ oasis stake delegate \
--validator oasis1qp…k7w \
--amount 5000 \
--signer keystore:treasury
✓ delegation queued · epoch 24891
unbonding window: 14 daysTyped, tree-shakeable, framework-agnostic.
Drop the SDK into any TypeScript or Rust project. The browser build talks to the desktop wallet over a localhost authenticated channel — no extension required.
$ bun add @oasisprotocol/wallet @oasisprotocol/sapphireimport { connect, paratimes } from class="tok-str">"@oasisprotocol/wallet";
const wallet = await connect({
origin: class="tok-str">"https:class="tok-comclass="tok-str">">//my-dapp.xyz",
scopes: [class="tok-str">"accounts:read", class="tok-str">"sign:sapphire"],
});
const account = await wallet.account({
paratime: paratimes.sapphire,
});
console.log(account.address, account.signer);
class=class="tok-str">"tok-com">// class="tok-num">0x9a7c…1f04 ledger:class="tok-num">0use oasis_wallet::{Wallet, Paratime, ConnectOptions};
"tok-com">#[tokio::main]
async fn main() -> anyhow::Result<()> {
let wallet = Wallet::connect(ConnectOptions {
origin: "https:">//my-service.io",
scopes: vec!["sign:sapphire"],
}).await?;
let account = wallet.account(Paratime::Sapphire).await?;
println!("address: {}", account.address);
Ok(())
}Confidential by default. Auditable on demand.
The signer accepts EIP-712 typed payloads, raw Sapphire calldata and arbitrary ParaTime transactions. View-keys let auditors verify without ever holding spending authority.
import { sapphire } from class="tok-str">"@oasisprotocol/sapphire";
const tx = sapphire.encodeCall({
to: class="tok-str">"class="tok-num">0x9a7c…1f04",
amount: 1240n * 10n ** 9n,
data: class="tok-str">"0x",
confidential: true,
});
const signed = await wallet.sign({
paratime: class="tok-str">"sapphire",
payload: tx,
display: {
summary: class="tok-str">"Treasury rebalance · Q2",
fields: [[class="tok-str">"recipient", class="tok-str">"class="tok-num">0x9a7c…1f04"], [class="tok-str">"amount", class="tok-str">"class="tok-num">1240 ROSE"]],
},
});
const receipt = await sapphire.broadcast(signed);
console.log(receipt.hash); class=class="tok-str">"tok-com">// class="tok-num">0x4f2c…b81dDeploy and operate TEE-attested agents from your dev loop.
ROFL agents run off-chain inside a TEE, attested every session. The wallet refuses to bind a session until the measurement matches the signed manifest.
$ oasis rofl deploy ./manifest.toml \
--signer ledger:0 \
--paratime sapphire
▶ packaging agent (sha256 9f4c2a01…b6d5)
▶ requesting attestation challenge
▶ TEE quote OK · measurement matches manifest
✓ agent live · oasis1rofl…7q2
telemetry: oasis:"tok-com">//rofl/oasis1rofl…7q2/log{
"tee_type": "Intel-TDX",
"mr_enclave": "0x9f4c2a01…1b6d5",
"nonce": "0x71ad…03e",
"quote_status": "OK",
"signed_by": "Oasis Attestation Service v2",
"valid_until": "2026-05-13T08:00:00Z"
}import { rofl } from class="tok-str">"@oasisprotocol/wallet";
class=class="tok-str">"tok-com">// Stream live agent telemetry over an authenticated channel.
const stream = await rofl.telemetry({
agent: class="tok-str">"oasis1rofl…7q2",
since: class="tok-str">"now-5m",
});
for await (const event of stream) {
console.log(event.t, event.kind, event.message);
}Talk directly to Ledger — no bridge, no daemon.
The hardware bridge speaks native USB/WebHID through the wallet. Every confirmation happens on the device; the host never sees a raw private key.
import { hardware } from class="tok-str">"@oasisprotocol/wallet";
const devices = await hardware.list();
class=class="tok-str">"tok-com">// [{ id: class="tok-str">"ledger:class="tok-num">0", model: class="tok-str">"Nano X", firmware: class="tok-str">"class="tok-num">2.4.class="tok-num">2" }]
const signer = await hardware.signer(class="tok-str">"ledger:class="tok-num">0", {
paratime: class="tok-str">"sapphire",
path: class="tok-str">"m/class="tok-num">44'/class="tok-num">474'/class="tok-num">0'/class="tok-num">0/class="tok-num">0",
});
const signed = await signer.sign(tx, {
display: { summary: class="tok-str">"Send class="tok-num">1240 ROSE · confidential" },
});Keep going.
Build what only Oasis can run.
Confidential EVM, hardware custody and attested compute — exposed through one coherent SDK.