The 2-of-3 Model
A fully set up Zhentan wallet has three owners and a threshold of 2:
Your two keys meet the threshold — so the agent is an advisory speed bump, not a gatekeeper. If it ever blocks something you want (or disappears entirely), you can execute from app.safe.global with your embedded + backup keys and go around it.
Two hard invariants:
- The agent never reaches the threshold alone — it cannot move funds without you. Enforced server-side on every proposal and transition.
- The agent never signs a transaction it didn’t screen — enforced cryptographically, next to the key itself: the signing key lives in a separate runtime service that refuses to sign unless it independently re-verifies the transaction hash, reads the Safe’s owners and nonce from chain, and finds its own record of having screened this exact transaction. When screening is off, your own keys must meet the threshold and the system only relays.
Wallet Profiles
Profiles are computed from the live owner set and threshold — never stored. Every wallet is exactly one of:Transitions
All transitions are hard-validated owner-management SafeTxs on the same address — your Safe address never changes:
Every account keeps an immutable creation snapshot (creation owners, threshold, salt nonce, derivation version — frozen by a database trigger), so its address stays re-derivable forever even after transitions rewrite the live owner set.
Transaction Flow
Every user transaction is a standard Safe transaction (EIP-712 SafeTx) — visible and actionable in the official Safe app at every stage: Key properties:- Zhentan relays and pays gas. Users never need BNB — a dedicated sponsor EOA submits
execTransactionon-chain. Signing and sending are different hands: the runtime signs, the sponsor sends. - One signature, verified twice. The runtime’s signature is checked by the server (it must recover to the registered agent address) before being used — once for the Safe-app confirmation and once on-chain. They are the same bytes over the same transaction hash.
- Rejections never leave nonce holes. Every proposal pre-signs a same-nonce empty cancel transaction, so a rejection consumes the nonce cleanly and later proposals aren’t blocked. The cancel’s co-signature comes from the runtime under a dedicated rule: under the rejection purpose it will sign nothing but the empty self-call at the current nonce.
- If the screening service is ever unavailable, nothing executes. Screened proposals simply wait (you’ll see them pending) and complete automatically when it returns — while relay-only and backup-key flows continue unaffected. Unavailable means paused, never unsafe.
- The Safe app is a first-class surface. A sync worker reconciles transactions confirmed or executed directly from app.safe.global — the override path is fully supported, not an afterthought.
Relay-Only Execution
Whenever your own signatures meet the threshold, the agent contributes no signature — it only submits the transaction and pays gas:- Starter (threshold 1): your signature alone.
- Protected with screening off: your embedded + backup signatures.
Screening Off: Queue and Co-Sign
In a Protected wallet you can pause screening. Your transactions then need both of your keys:- You propose and sign as usual — the transaction queues at 1 of 2 and is mirrored to the Safe Transaction Service.
- Complete it whenever you like, from either surface:
- In-app — open the transaction in your history and tap Sign with [your backup wallet]. If the backup wallet isn’t connected, the app walks you through a signature-free connection first. The agent then relay-executes.
- Safe app — confirm with your backup key at app.safe.global; Zhentan’s sync worker picks up the execution automatically.
Address Derivation
Safe addresses are derived server-side only, through a versioned registry:
The client never derives locally — it asks
GET /users/by-signer (returning users) or POST /safe/derive (new users, with their chosen profile). Owner order for derivation is canonical [embedded, backup, agent]; deployed Safes read their live owner set from chain. Derivation runs once at account creation; the frozen creation snapshot keeps every address auditable and re-derivable — the server ships safe:verify-derivations to re-derive and check every account.
Safes are deployed eagerly at onboarding (the agent pays deployment gas), because the Safe Transaction Service only indexes deployed Safes.