Get your account
Under the hood

Smart Contracts

Beta. Parts of what these docs describe are still being built. The roadmap shows what is live today.

Every Cyphron account is built directly on the protocol's confidential token contract on Robinhood Chain, an ERC-20 wrapper with encrypted balances that follows the Zether approach. The EVM has no built-in confidential transfer, so Cyphron wrote this layer itself; it is open source and verified on-chain. Cyphron runs no shadow ledger and no private system of record alongside it. Once you understand the token contract, you understand exactly how balances and transfers work.


What the contract stores

ConfidentialToken wraps a plain ERC-20 (USDG on Robinhood Chain) and keeps each registered account's balance as an ElGamal ciphertext on the alt_bn128 curve. An account calls register once with its ElGamal public key and from that point carries a single two-point ciphertext:

struct Ciphertext {
    AltBn128.Point c1;
    AltBn128.Point c2;
}

mapping(address => AltBn128.Point) internal _publicKey; // ElGamal key the balance encrypts under
mapping(address => bool) public registered;
mapping(address => Ciphertext) internal _balance;       // encrypted balance, readable via encryptedBalanceOf
uint256 public totalWrapped;                            // public total of the underlying asset held

The balance is ciphertext. It can be opened only with the matching ElGamal private key (your view key, derived from your account key). For everyone else, Cyphron included, it is indistinguishable from noise.

Value enters through deposit(amount), which wraps the underlying asset into your encrypted balance, and leaves through withdraw(amount, proof, publicSignals), which unwraps against a proof that the encrypted balance covers the amount. Both of those amounts are public, because the ERC-20 transfers on either side of them are public anyway.


What a transfer submits

A confidential transfer never states its amount. The sender's client builds two encrypted deltas and a proof, and sends them as calldata:

function confidentialTransfer(
    address to,
    Ciphertext calldata senderDelta,     // -amount, encrypted to the sender's key
    Ciphertext calldata recipientDelta,  // +amount, encrypted to the recipient's key
    bytes calldata proof,
    uint256[] calldata publicSignals     // circuit public inputs, passed through to the verifier
) external;

In that same transaction, the contract asks its verifier (IConfidentialTransferVerifier.verifyTransfer) to confirm that both deltas encrypt one identical, non-negative amount and that the sender stays solvent. Only then does it apply each delta to the matching balance by point addition. The verifier is a separately deployed Groth16 contract generated from the protocol's circuits, and it sits behind setVerifier so the proof system can be upgraded without moving a single stored balance. Robinhood Chain settles to Ethereum through the rollup's fraud proofs, so the verifier's verdict is ultimately backed by Ethereum's security. The proofs reveal validity and nothing more.


Fees

FeeSchedule is the protocol's single price list, and the contracts that move value call its quoteFee view to price a transfer. The fee is flat: 0.10% of the amount, never more than 5 USDG. There are no tiers and no discounts. The schedule's authority can retune the rate and the cap with setSchedule(baseFeeBps, feeCap), and the role changes hands through a two-step beginAuthorityTransfer / acceptAuthority handoff.


The off-chain indexer

A PostgreSQL index built from contract event streams exists only so the interface is quick and searchable. It contains:

  • Mappings from addresses to @handles
  • Transaction metadata that is already public on-chain: the two addresses, the timestamp, and the hash
  • Webhook subscriptions and their delivery status

It contains no decrypted balances and no decrypted amounts. Readable figures live on your device alone; everywhere else, including the chain, they stay encrypted.


How agent accounts work on-chain

Agents spend through AgentController. Each agent gets an internal vault inside that contract and a dedicated agentSigner key held by whatever system runs it. That key can do exactly one thing: spend from its own vault, in a single nominated token, within the spend policy its owner wrote (per-transaction limit, daily limit, recipient allowlist, and approval threshold). It cannot withdraw, re-key itself, or widen its own limits. A spend outside the policy is rejected by the contract itself; client-side checks catch it sooner, but the on-chain rule is what keeps the limit in force even if the client is compromised. This is how autonomous software gets genuine spending power with bounded exposure. See Security and Threat Model.


Deployed addresses

The contracts are live on Robinhood Chain testnet. Every address links to the block explorer, where you can follow each transaction the contracts have processed.

Contract Testnet address
ProtocolAuthority 0xde0cc85F9F168576eC429D8E023871c623580F96
AccountRegistry 0x7d14b198B7D4Cf50287209d102cCE39388A861b0
AgentController 0x1458062e466128791044bE256eD7A866f3825Af7
RequestLedger 0xa365489ee4aAdA03BF4135c342e61cc8399ef004
DisclosureLog 0xf397327d4FD2Ff1B527be56D682Ba4E2b95B88A2
ConfidentialToken 0x75C62a67f543045D3A6C1229628Af5fAf0097633
Transfer verifier 0xd641b9D71ABBbA5D51c0a4dF7167f77a8Dc3E95C
USDG (testnet) 0xCDF02eC32cd2A6ad7610A2e6a8fd95A9DeC98d16

Testnet has no official USDG, so the testnet deployment settles in a six-decimal stand-in token with no real value.