Pacto ships with a fully embedded EVM wallet that requires no separate setup or configuration. Every EVM account is derived from the same BIP-39 mnemonic that backs your Nostr identity, so a single recovery phrase gives you both your Nostr keypair and your on-chain accounts. Private keys never leave the Rust backend and are decrypted only for approved signing operations.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/covenant-gov/pacto-app/llms.txt
Use this file to discover all available pages before exploring further.
Key types
Pacto distinguishes two EVM account purposes. The rules are enforced at the backend — squad paths reject advanced signers, and advanced paths reject squad signers.| Key type | Purpose tag | Used for |
|---|---|---|
| Squad key | squad | DM WalletBar sends, treasury deploy, governance commands, roster shares. Only squad-purpose keys may be the active signer or a profile receiving address. |
| Advanced key | advanced | Settings → Advanced panel only (WalletAdvancedPanel.svelte). Backend command evm_send_advanced_contract_call refuses squad signers; squad paths refuse advanced addresses. |
Squad and advanced key paths are mutually exclusive by design. Squad keys cannot call advanced contract paths, and advanced keys cannot sign squad transactions or governance commands. This separation is enforced in the Rust backend (Phase G).
Read vs. write architecture
Pacto keeps a clean separation between on-chain reads (frontend) and signed writes (backend):Read-plane (frontend — viem)
All read-only chain access — balances, ERC-20 contract reads, Safe state reads, and receipt polling — runs through the TypeScript viem layer insrc/lib/evm/read-plane.ts. No private key is present in the frontend. The read-plane builds viem public clients from the resolved RPC URLs for the active chain, using createPublicClient with a fallback transport.
Rust send (backend — Alloy)
WalletBar sends, treasury deploys, and all governance writes are handled entirely in the Rust backend using Alloy. The EVM private key is decrypted inside Rust only for the duration of the signing operation. This approach avoids shipping or spawning a JavaScript runtime for signing and keeps the private key lifecycle entirely in the Tauri process.| Concern | Layer | Mechanism |
|---|---|---|
| RPC URL selection (UI reads) | Frontend | ALCHEMY_RPC_KEY + user prefs + getEffectiveRpcUrlsForChain |
| RPC URL selection (backend sends) | Backend | Same ALCHEMY_RPC_KEY via wallet_rpc_providers.rs + curated fallbacks |
| Read-only JSON-RPC | Frontend | viem createPublicClient + fallback via src/lib/evm/read-plane.ts |
| WalletBar send / raw tx | Backend | Rust (Alloy): encode, sign, eth_sendRawTransaction |
| Advanced opaque contract call | Backend | evm_send_advanced_contract_call — advanced-purpose signer only |
| Squad curated deploy / gov writes | Backend | Typed Alloy commands + squad-purpose gate |
WalletBar
The WalletBar is the in-thread wallet UI that appears inside DM conversations. From the WalletBar you can:- Send tokens — ETH, USDC, or USDT to the peer’s EVM address.
- Request payment — post a
wallet_tx_requestcard into the thread; the recipient can accept and pre-fill the send form. - Announce transactions — after broadcast, an optimistic
wallet_tx_announcementcard appears immediately, then updates to confirmed once the receipt arrives.
dm_peer_evm first, then profiles.evm_address. A raw 0x address from Settings bypasses npub resolution entirely.
Balance cache
The last successfulget_wallet_summary response is stored per account under the localStorage key:
loadAccountState in src/stores/persistence.ts reads the cache (via src/lib/wallet/wallet-summary-cache.ts) so the WalletBar shows data immediately while a background refresh runs. The key is cleared with all other npub-scoped storage on logout via clearAccountState.
USD spot pricing
Wallet balances are displayed with live USD values sourced from Chainlink on-chain price feeds on Ethereum mainnet. The backend callslatestRoundData() on the standard aggregator proxies for ETH/USD, USDC/USD, and USDT/USD, then caches results for 90 seconds to limit RPC load. There are no static price fallbacks — if the oracle read fails, the UI shows that live pricing is unavailable.
The Tauri command is wallet_get_usd_spot_prices; the TypeScript helper is getWalletUsdSpotPrices() in src/lib/wallet/pricing.ts.
Squad allowlisted contract calls
Beyond the standard native and ERC-20 transfer paths, squad keys can call explicitly allowlisted contract targets viaevm_send_squad_allowlisted_contract_call. The allowlist is managed in Dashboard → Settings → Smart contract security. Implicit deploy-infra targets are always permitted.
Learn more
Key Derivation
BIP-44 path, address hash rule, legacy history, and the reference test vector.
Transactions
Broadcast-then-release UX, background receipt polling, and optimistic DM wallet cards.
Governance
pacto-gov, Gnosis Safe treasury, Hats Protocol roles, and squad infra deployment.
Chain Config
Supported networks, RPC URL resolution, viem read/write split, and USD pricing.