Tongo Documentation (Under Construction)
Welcome to the official Tongo documentation. Tongo is a confidential payment system for ERC20 tokens on Starknet, providing privacy-preserving transactions while maintaining auditability and compliance features. Tongo is heavily based on this paper.
Waring This documentation is under costruction. It is mostly done but we have to come back to round some rough edges. Use it wisely.
What is Tongo?
Tongo wraps any ERC20 token with ElGamal encryption, enabling private transfers while maintaining full auditability. Built on zero-knowledge proofs and homomorphic encryption over the Stark curve, Tongo enables users to transact with hidden amounts while preserving the ability to verify transaction validity.
Key Features:
- No Trusted Setup: Built entirely on elliptic curve cryptography
- Hidden Amounts: All transfer amounts are encrypted
- Flexible Compliance: Global auditor support and selective disclosure
Quick Links
- GitHub: github.com/fatlabsxyz/tongo-docs
- npm Package: @fatsolutions/tongo-sdk
- Website: tongo.cash
Getting Started
If you're a developer looking to integrate Tongo into your application, start with the SDK Quick Start.
If you want to understand the protocol and cryptography, begin with the Protocol Introduction.
Introduction to Tongo
Tongo is a confidential payment system for ERC20 tokens on Starknet, providing privacy-preserving transactions while maintaining auditability and compliance features. Built on ElGamal encryption and zero-knowledge proofs, Tongo enables users to transact with hidden amounts while preserving the ability to verify transaction validity. Tongo is heavily based in this paper.
What Makes Tongo Different
No Trusted Setup
Unlike many ZK systems, Tongo requires no trusted ceremony. All cryptography is built on the discrete logarithm assumption over the Stark curve, with no hidden trapdoors or setup parameters.
Native Starknet Integration
Tongo leverages Starknet's native elliptic curve operations, making verification extremely efficient (~120K Cairo steps per transfer) compared to other privacy solutions that require expensive proof verification.
Flexible Compliance
The protocol supports multiple compliance models:
- Global auditor: All transactions encrypted for regulatory oversight
- Selective disclosure: Optional viewing keys per transaction
- Ex-post proving: Retroactive transaction disclosure without revealing keys
How It Works
1. Key Generation
Each user generates a keypair \((x, y = g^x)\) where \(g\) is the Stark curve generator. The public key \(y\) serves as their account identifier.
2. Encrypted Balances
Balances are stored as ElGamal ciphertexts:
$$\text{Enc}[y](b, r) = (g^b y^r, g^r)$$
The encryption is additively homomorphic, allowing on-chain balance updates without decryption. Each Tongo account has two balances: the current balance and the pending balance.
The current balance stores the amount of Tongos the account can use to perform Transfers/Withdraw operations. Zero-Knowledge proofs are check againts this balance and only the owner of the Tongo account can modify this balance thought Fund/Rollover operations.
The pending balance stores the amount of Tongos that the account has received through Transfer operations. To use this balance the account has to transform the pending balance in current balance. This is done by a Rollover operation.
Core Operations
Tongo has four user operation needed to operate a Tongo account. All these operations requires some kind of Zero-Knowledge proof to be validated by te contract. The operations are:
Funding
Convert standard ERC20 tokens to encrypted balances: In this operation some amount of ERC20 are send to the Tongo contract. The contract the mint for the given Tongo account some amount of tongo based on the ERC20-Tongo rate defined in the same contract. At this stage the amount sent is public, so the contract creates a encryption with a fixed random and adds the newly minted Tongos to the user account. This operation can only be performed by the owner of the Tongo account.
Transfers
Performs confidential transfers between accounts: In this operation some amount of Tongos are sent to the given receiver. The sender creates a encryption of the amount for the receiver and a encryption of the same amount for themself. These encryption are added to the receiver and subtracted from the receiver respectively. The sender also provides a ZK proof that shows:
- Ownership of the sender account.
- Both encrpytion are valid encryptions for the same amount under the correct public keys.
- The amount encrypted in positive.
- The sender has enough balance to perform the operation.
Rollover
In this operation the pending balance is added to the current balance of a given Tongo account and then emptied.
This operation can only be performed by the owner of the Tongo account.
Withdrawals
Convert back to standard ERC20 tokens: In this operation some amount of Tongo are converted back to ERC20 and sent to the given starknet account. The whitdrawn amount is public, so the contract creates a encryption of the amount for the user public key and subtract it from the user balance. The user has to provide a ZK proof that shows:
- Ownership of the Tongo account.
- The account has enough balance to perform the operation.
Security Measures
No double usage of proofs
In Tongo, all operation are validated by a ZK proof. If a ZK proof from a given operation is valid, the operation is performed. For this reason, to preven reusage of previous valid proofs (or some parts of a valid proof) some data is included and signed in each ZK proof. This includes:
- The chain_id: A valid proof in sepolia is not valid in mainet.
- The tongo contract address: A valid proof constructer for a particular instance of Tongo is not valid for another one.
- A user account nonce: ZK proof are construted for a given Tongo account nounce. With each performed operation the Tongo account nonce is increased. So a valid proof will no be valid in the future.
Whitelisting of Tx sender
When constructing the ZK proof the tongo account owner choses the straknet account that will execute the tx. This addres is incorporated and signed in the ZK proof. The contract checks this againts the caller address. This guarantees that the ZK proof will be valid only if it is executed by the starknet account chosed by the tongo account owner.
Balance Integrity
Each time a balance is going to be modified by adding/subtracting an encryption, the encrpytion has to pass a ZK proof that shows:
- The encryption is a valid ElGamal encryption
- The amount encrypted is positive
- The encryption is made for the correct public key
Use Cases
Individual Privacy
- Personal transactions: Hide transfer amounts from public view
- Salary payments: Confidential payroll systems
Compliance
- Optional Compliance for Institutions: Deployer can chose weather to deploy with or without auditor keys
- Treasury Management: Confidential transfers with auditability for stakeholders
Potential Integrations
- Private AMM trading: Hidden trade sizes
- Neo-Bank Confidential Payments: By design tongo can support payment procesors required speeds
- DAO governance: Confidential voting systems
Getting Started
To start building with Tongo, proceed to the SDK Documentation for installation and usage guides.
To understand the cryptographic foundations, continue to the Encryption System chapter.
Encryption System
Tongo uses ElGamal encryption over elliptic curves to maintain confidential balances while enabling homomorphic operations on-chain.
ElGamal Encryption
Each user's balance is encrypted using a public key derived from their private key. ElGamal encryption of an amount \(b\) under a public key \(y\) is a pair point of the group of elliptic curves points \(G\). The encryption function is defined as:
$$\begin{aligned} \text{Enc}[y]&: [0, b_{\max}) \times \mathbb{F}_p \rightarrow G^2 \\ \text{Enc}[y]&\left(b,r\right) = (L, R) = (g^b y^r, g^r) \end{aligned}$$
Where:
- \(y = g^x\) is the user's public key (derived from private key \(x\))
- \(g\) is the generator of the Stark curve
- \(b\) is the balance amount in the range \([0, b_{\max})\)
- \(r\) is a random blinding factor
- \(p\) is the curve order
Additive Homomorphism
The key property of this encryption is additive homomorphism. Given two encryptions under the same public key, their product is a valid encryption of the sum:
$$\text{Enc}[y]\left(b,r\right) \cdot \text{Enc}[y]\left(b',r'\right) = (g^{b+b'} y^{r+r'}, g^{r+r'}) = \text{Enc}[y]\left(b+b', r+r'\right)$$
This allows the contract to:
- Add encrypted amounts without decryption
- Subtract encrypted amounts homomorphically
- Update balances while maintaining privacy
Balance Decryption
To read their balance, a user recovers \(g^b\) using their private key \(x\):
$$\frac{L}{R^x} = \frac{g^b y^r}{(g^r)^x} = \frac{g^b (g^x)^r}{g^{rx}} = g^b$$
Since \(b\) is bounded by \([0, b_{\max})\), the discrete logarithm \(b\) can be brute-forced. The time requiered to decript a balance depends on \(b_{\max}\). Tongo is parametrized in this variable that we call bit_size the current implementation of Tongo uses bit_size = 32. A naïve JavaScript implementation can decrypt ~100k units per second, while optimized algorithms handle the full 32-bit range much faster. The common algorithms used for this kind of decryption are:
- Brute force: Iterate \(g^i\) for \(i = 0, 1, 2, \ldots\) until matching \(g^b\)
- Baby-step Giant-step: More efficient \(O(\sqrt{n})\) algorithm
- Pollard's rho: Probabilistic algorithm with similar complexity
Storage Architecture
For each Tongo account, the Tongo contract maintains multiple encrypted representations of each balance. Here we have a description of them.
Current Balance
ElGamal encryption of the balance that the owner can freely use. Only the owner can modify this balance through different operations (Fund/Withdraw/Transfer/Rollover). Zero-knowledge proofs are checked against this balance. We just call this balance the user's balance.
Pending Balance
ElGamal encryption of incoming transfers. This serves as a buffer to hold all transfers an account has received. We just call this balance the user's pending.
Upon owner's request, this balance is added to the user's balance. The separation between current and pending balances is needed because zero-knowledge proofs are checked against the current balance. If an incoming transaction were able to modify it, a malicious actor could render all ZK proofs of a user invalid by spamming transfers to the account.
Autenticated Encrypted Balance (Hint)
XChaCha12 encryption of the amount encrypted in the user's balance. This encryption uses a key derived from the user's private key, allowing instant balance recovery without discrete log computation. This is only intended to be used as a hint for fast decryption of th user's balance. This ae_balance is not cryptographically enforced by the protocol, since there is no way to prove that the user provided the correct hint. It is purely a convenience feature. We just call this encryption hint.
Audit Balance
If the Tongo instance has an auditor, the audit_balance is an ElGamal encryption that the owner creates in each operation, encrypting the state of the user's balance under the auditor’s key. In each case, the owner provides a Zero-Knowledge proof showing that the amount encrypted for the auditor is the same as that encrypted in the current balance.
Audit Hint
This is an XChaCha12 encryption of the audit_balance using a key symmetrically derived from the owner's and auditor's key. This is a hint for the auditor, equivalent to the ae_balance for the owner. It is not cryptographically enforced by the protocol.
Auditing & Compliance
Tongo provides flexible auditing mechanisms that enable compliance without sacrificing user privacy. Through viewing keys and ex-post proving, regulators can verify transaction details while preserving confidentiality for all other parties.
Global Auditor
The Tongo contract can designate a global auditor with public key \(y_a\), the owner of the Tongo instance can rotate the auditor key anytime. If a Tongo instance was deployed without an auditor, it cannot be added after.
Auditor Encryptions
-
Each time the balance of an account is modified, the owner of the account must provide a encryption of the new balance for the auditor public key. A Zero-Knowledge proof that shows the encryption is correct and is indeed encrypting the new balance must be provided.
-
Each time a Transfer operation is made, the sender has to also provide an encryption of the transfered amount for the auditor public key. A Zero-Knowledge proof must also be provided.
Theese two kind of encryptions allow the auditor to reconstruct all transactional values while keeping those values confidential to third parties.
Multi-Signature Auditing
For enhanced security, auditor keys can be distributed across multiple parties:
$$y_a = g^{a_1 + a_2} = g^{a_1} \cdot g^{a_2} = y_{a_1} \cdot y_{a_2}$$
Individual auditors can compute partial decryptions:
- Auditor 1: \(R^{a_1} = (g^r)^{a_1}\)
- Auditor 2: \(R^{a_2} = (g^r)^{a_2}\)
The balance is recovered by combining: \(g^b = L_a / (R^{a_1} \cdot R^{a_2})\)
This prevents any single auditor from unilaterally accessing transaction data.
Ex-Post Proving & Viewing Keys
After a transfer is completed, participants may need to prove a specific transaction detail to a third party without revealing their private keys. Ex-post proving enables this through cryptographic proofs. These proofs can be created for diferent viewings keys if the user desires so.
Protocol
Consider a completed transfer with ciphertext \((TL, TR) = (g^{b_0} y^{r_0}, g^{r_0})\). To prove the transfer amount to a third party with public key \(\bar{y}\). The sender must creates a new encryption of the transfer amount for \(\bar{y}\):
$$(\bar{L}, R) = \text{Enc}[\bar{y}](b, r)$$
The sender must provide a comprehensive proof \(\pi_{\text{ExPost}}\) demonstrating:
1. Ownership Proof
Prove knowledge of private key \(x\) such that \(y_s = g^x\). This proof can only be constructed with knowledge of the private key \(x\).
2. Same Encryption Proof
Prove that the given encryption is a correct ElGamal encryption under \(\bar{y}\). It also shows that this encryption and \((TL, TR)\) are encrypting the same amount.
Off-Chain Verification
Ex-post proofs require no on-chain interaction:
- Transaction data is retrieved from chain state
- Proofs are generated and verified off-chain
- Only requires the original transaction hash as reference
Regulatory Compliance
AML/KYC Integration
Tongo supports various compliance frameworks:
Real-Time Monitoring
- Global auditor receives all transaction encryptions
- Automated threshold detection (encrypted amounts)
- Pattern analysis on transaction graphs
Selective Disclosure
- Users can voluntarily encrypt for compliance officers
- Jurisdiction-specific reporting requirements
- Time-limited viewing key access
Retroactive Investigation
- Ex-post proving enables transaction reconstruction
- User cooperation required for private key revelation
- Court-ordered disclosure mechanisms
Advanced Features
Threshold Auditing
Multiple auditors with threshold decryption:
$$y_a = \sum_{i=1}^n w_i \cdot y_{a_i}$$
Where \(w_i\) are threshold weights and \(t\) out of \(n\) auditors are required for decryption.
Zero-Knowledge Compliance
Prove compliance properties without revealing amounts:
- Range compliance: Prove transfer amount below threshold
- Velocity limits: Prove cumulative amounts within bounds
- Whitelist compliance: Prove recipient authorization
These advanced features demonstrate Tongo's flexibility in balancing privacy and regulatory requirements across diverse jurisdictions and use cases.
Tongo Instances
The class hash of the current version of Tongo is
Mainnet
We listed here a set of instances of Tongo deployed in mainnet
| STRK | |
|---|---|
| ERC20 | 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d |
| Tongo | 0x3a542d7eb73b3e33a2c54e9827ec17a6365e289ec35ccc94dde97950d9db498 |
| rate | 50000000000000000 |
| ETH | |
|---|---|
| ERC20 | 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 |
| Tongo | 0x276e11a5428f6de18a38b7abc1d60abc75ce20aa3a925e20a393fcec9104f89 |
| rate | 3000000000000 |
| wBTC | |
|---|---|
| ERC20 | 0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac |
| Tongo | 0x6d82c8c467eac77f880a1d5a090e0e0094a557bf67d74b98ba1881200750e27 |
| rate | 10 |
| USDC.e | |
|---|---|
| ERC20 | 0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8 |
| Tongo | 0x72098b84989a45cc00697431dfba300f1f5d144ae916e98287418af4e548d96 |
| rate | 10000 |
| USDC | |
|---|---|
| ERC20 | 0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb |
| Tongo | 0x026f79017c3c382148832c6ae50c22502e66f7a2f81ccbdb9e1377af31859d3a |
| rate | 10000 |
| USDT | |
|---|---|
| ERC20 | 0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8 |
| Tongo | 0x659c62ba8bc3ac92ace36ba190b350451d0c767aa973dd63b042b59cc065da0 |
| rate | 10000 |
| DAI | |
|---|---|
| ERC20 | 0x00da114221cb83fa859dbdb4c44beeaa0bb37c7537ad5ae66fe5e0efd20e6eb3 |
| Tongo | 0x511741b1ad1777b4ad59fbff49d64b8eb188e2aeb4fc72438278a589d8a10d8 |
| rate | 10000000000000000 |
Sepolia
We listed here a set of instances of Tongo deployed in mainnet
| STRK | |
|---|---|
| ERC20 | 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d |
| Tongo | 0x408163bfcfc2d76f34b444cb55e09dace5905cf84c0884e4637c2c0f06ab6ed |
| rate | 50000000000000000 |
| ETH | |
|---|---|
| ERC20 | 0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 |
| Tongo | 0x2cf0dc1d9e8c7731353dd15e6f2f22140120ef2d27116b982fa4fed87f6fef5 |
| rate | 3000000000000 |
| USDC | |
|---|---|
| ERC20 | 0x53b40a647cedfca6ca84f542a0fe36736031905a9639a7f19a3c1e66bfd5080 |
| Tongo | 0x2caae365e67921979a4e5c16dd70eaa5776cfc6a9592bcb903d91933aaf2552 |
| rate | 10000 |
Deployment
You can deploy another instance of Tongo with your own set of parameters. The constructor of the contract is
#![allow(unused)] fn main() { #[constructor] fn constructor( ref self: ContractState, owner: ContractAddress, ERC20: ContractAddress, rate: u256, bit_size: u32, auditor_key: Option<PubKey>, ) { self.owner.write(owner); self.ERC20.write(ERC20); self.rate.write(rate); assert!(bit_size <= 128_u32, "Bit size should be 128 at max"); self.bit_size.write(bit_size); if let Some(key) = auditor_key { self._set_auditor_key(key); } } }
Contract ABI
The main Tongo contract implements the ITongo interface and manages all confidential payment operations:
#![allow(unused)] fn main() { #[starknet::interface] pub trait ITongo<TContractState> { // Tongo general setup: /// Returns the contract address that Tongo is wraping. fn ERC20(self: @TContractState) -> ContractAddress; /// Returns the rate of conversion between the wrapped ERC20 a tongo: /// /// ERC20_amount = Tongo_amount*rate /// /// The amount variable in all operation refers to the amount of Tongos. fn get_rate(self: @TContractState) -> u256; /// Returns the bit_size set for this Tongo contract. fn get_bit_size(self: @TContractState) -> u32; /// Returns the contract address of the owner of the Tongo account. fn get_owner(self: @TContractState) -> ContractAddress; // User operations: /// Funds a tongo account. Callable only by the account owner /// /// Emits FundEvent fn fund(ref self: TContractState, fund: Fund); /// Withdraw Tongos and send the ERC20 to a starknet address. /// /// Emits WithdrawEvent fn withdraw(ref self: TContractState, withdraw: Withdraw); /// Withdraw all the balance of an account and send the ERC20 to a starknet address. This proof /// avoids the limitations of the range prove that are present in the regular withdraw. /// /// Emits RagequitEvent fn ragequit(ref self: TContractState, ragequit: Ragequit); /// Transfer Tongos from the balanca of te sender to the pending of the receiver /// /// Emits TransferEvent fn transfer(ref self: TContractState, transfer: Transfer); /// Moves to the balance the amount stored in the pending. Callable only by the account owner. /// /// Emits RolloverEvent fn rollover(ref self: TContractState, rollover: Rollover); // State reading functions /// Returns the curretn stored balance of a Tongo account fn get_balance(self: @TContractState, y: PubKey) -> CipherBalance; /// Returns the current pending balance of a Tongo account fn get_pending(self: @TContractState, y: PubKey) -> CipherBalance; /// Return, if the Tongo instance allows, the current declared balance of a Tongo account for /// the auditor fn get_audit(self: @TContractState, y: PubKey) -> Option<CipherBalance>; /// Returns the current nonce of a Tongo account fn get_nonce(self: @TContractState, y: PubKey) -> u64; /// Returns the current state of a Tongo account. fn get_state(self: @TContractState, y: PubKey) -> State; // Auditor handling /// Returns the current auditor public key. fn auditor_key(self: @TContractState) -> Option<PubKey>; /// Rotates the current auditor public key. fn change_auditor_key(ref self: TContractState, new_auditor_key: PubKey); } }
Storage Structure
#![allow(unused)] fn main() { #[storage] struct Storage { /// The contract address that is owner of the Tongo instance. owner: ContractAddress, /// The contract address of the ERC20 that Tongo is wrapping. ERC20: ContractAddress, /// The conversion rage between the wrapped ERC20 a tongo: /// /// ERC20_amount = Tongo_amount*rate rate: u256, /// The bit size this contract will work with. This limites the values that cant be proven /// by a range proof. If is set to 32 that means that range proof will only work for values /// between 0 and 2**32-1. /// Note: The computational cost of verifying a tranfers operation (the most expensive one) /// is about (30 + 10*n) ec_muls and (20 + 8n) ec_adds, where n is the bit_size bit_size: u32, /// The encrypted balance for the given pubkey. balance: Map<PubKey, CipherBalance>, /// The encrypted pending balance for the given pubkey. The pending balance is the sum of /// incoming transfer. User has to execute a rollover operation to convert this to usable /// balance. pending: Map<PubKey, CipherBalance>, /// The nonce of the given pubkey. Nonce is increased in every user operation. nonce: Map<PubKey, u64>, /// Hint to fast decrypt the balance of the given pubkey. This encrypts the same amount that /// is stored in `balance`. It is neither check nor enforced by the protocol, only the the /// user can decrypt it with knowledge of the private key and it is only usefull for /// attempting a fast decryption of `balance. ae_balance: Map<PubKey, AEBalance>, /// The balance of the given pubkey enrypted for the auditor key. /// /// If the contract was deployed witouth an auditor, the map is empty and all keys return /// the Default CipherBalance {L: {x:0, y:0}, R:{x:0,y:0}}; audit_balance: Map<PubKey, CipherBalance>, /// Hint to fast decrypt the audited balance of the given pubkey. This encrypts the same /// amount that is stored in `audit_balance`. It is neither check nor enforced by the /// protocol, only the auditor can decrypt it with knowledge of the auditor private key and /// it is only usefull for attempting a fast decryption of `audit_balance`. ae_audit_balance: Map<PubKey, AEBalance>, /// The auditor pubkey. If the contract was deployed without auditor this will be an /// Option::None without a way to change it. auditor_key: Option<PubKey>, /// The increasing number that identifies the public key key_number: u128, } }
Events
The contract emits events for all operations to enable off-chain monitoring:
#![allow(unused)] fn main() { /// Event emited in a Fund operation. /// /// - to: The Tongo account to fund. /// - nonce: The nonce of the Tongo account. /// - amount: The ammount of tongo to fund. #[derive(Drop, starknet::Event)] pub struct FundEvent { #[key] pub to: PubKey, #[key] pub nonce: u64, pub amount: u128, } /// Event emited in a Rollover operation. /// /// - to: The Tongo account to rollover. /// - nonce: The nonce of the Tongo account. /// - rolloverred: The cipherbalance of the rolloverred amount. #[derive(Drop, starknet::Event)] pub struct RolloverEvent { #[key] pub to: PubKey, #[key] pub nonce: u64, pub rollovered: CipherBalance, } /// Event emited in a Withdraw operation. /// /// - from: The Tongo account to withdraw from. /// - nonce: The nonce of the Tongo account. /// - amount: The ammount of tongo to withdraw. /// - to: The starknet contract address to send the funds to. #[derive(Drop, starknet::Event)] pub struct WithdrawEvent { #[key] pub from: PubKey, #[key] pub nonce: u64, pub amount: u128, pub to: ContractAddress, } /// Event emited in a Transfer operation. /// /// - to: The Tongo account to send tongos to. /// - from: The Tongo account to take tongos from. /// - nonce: The nonce of the Tongo account (from). /// - transferBalance: The amount to transfer encrypted for the pubkey of `to`. /// - transferBalanceSelf: The amount to transfer encrypted for the pubkey of `from`. /// - hintTransfer: AE encryption of the amount to transfer to `to`. /// - hintLeftover: AE encryption of the leftover balance of `from`. #[derive(Drop, starknet::Event)] pub struct TransferEvent { #[key] pub to: PubKey, #[key] pub from: PubKey, #[key] pub nonce: u64, pub transferBalance: CipherBalance, pub transferBalanceSelf: CipherBalance, pub hintTransfer: AEBalance, pub hintLeftover: AEBalance, } /// Event emited in a Ragequit operation. /// /// - from: The Tongo account to withdraw from. /// - nonce: The nonce of the Tongo account. /// - amount: The ammount of tongo to ragequit (the total amount of tongos in the account). /// - to: The starknet contract address to send the funds to. #[derive(Drop, starknet::Event)] pub struct RagequitEvent { #[key] pub from: PubKey, #[key] pub nonce: u64, pub amount: u128, pub to: ContractAddress, } /// Event emited when users declare their balances to the auditor. /// /// - from: The Tongo account that is declaring its balance. /// - nonce: The nonce of the Tongo accout. /// - auditorPubKey: The current public key of the auditor. /// - declaredCipherBalance: The balance of the user encrypted for the auditor pubkey. /// - hint: AE encryption of the balance for the auditor fast decryption. #[derive(Drop, starknet::Event)] pub struct BalanceDeclared { #[key] pub from: PubKey, #[key] pub nonce: u64, pub auditorPubKey: PubKey, pub declaredCipherBalance: CipherBalance, pub hint: AEBalance, } /// Event emited when users declare a transfer to the auditor. /// /// - from: The Tongo account that is executing the transfer. /// - to: The Tongo account that is receiving the transfer. /// - nonce: The nonce of the Tongo accout (from). /// - auditorPubKey: The current public key of the auditor. /// - declaredCipherBalance: The transfer amount encrypted for the auditor pubkey. /// - hint: AE encryption of the balance for the auditor fast decryption. #[derive(Drop, starknet::Event)] pub struct TransferDeclared { #[key] pub from: PubKey, #[key] pub to: PubKey, #[key] pub nonce: u64, pub auditorPubKey: PubKey, pub declaredCipherBalance: CipherBalance, pub hint: AEBalance, } /// Event emited when the owner sets a public key for the auditor. /// /// - keyNumber: An increasing number that identifies the public key /// - AuditorPubKey: The newly set auditor public key. #[derive(Drop, starknet::Event)] pub struct AuditorPubKeySet { #[key] pub keyNumber: u128, pub AuditorPubKey: PubKey, } }
Operations
1. Fund Operation
Converts ERC20 tokens to encrypted balances:
#![allow(unused)] fn main() { This code is a simplification of the actual code /// Funds a tongo account. Callable only by the account owner /// /// Emits FundEvent fn fund(ref self: ContractState, fund: Fund) { verify_fund(/* public inputs */, proof); self._transfer_from_caller(amount); let cipher = CipherBalanceTrait::new(to, amount, 'fund'); self._add_balance(to, cipher); self.emit(FundEvent); if self.auditor.is_some() { self._handle_audit(auditPart); } } }
2. Transfer Operation
Performs confidential transfers between accounts:
#![allow(unused)] fn main() { This code is a simplification of the actual code /// Transfer Tongos from the balance of the sender to the pending of the receiver /// /// Emits TransferEvent fn transfer(ref self: ContractState, transfer: Transfer) { verify_transfer(/* public inputs */, proof); self._subtract_balance(from, transferBalanceSelf); self._add_pending(to, transferBalance); self.emit( TransferEvent ); if self.auditor.is_some() { self._handle_audit(auditPart); } } }
3. Rollover Operation
Merges pending transfers into main balance:
#![allow(unused)] fn main() { This code is a simplification of the actual code /// Moves to the balance the amount stored in the pending. Callable only by the account /// owner. /// /// Emits RolloverEvent fn rollover(ref self: TContractState, rollover: Rollover) { verify_rollover(/* public inputs */, proof); self._pending_to_balance(to); self.emit( RolloverEvent ); } }
4. Withdraw Operation
Convert back to standard ERC20 tokens:
#![allow(unused)] fn main() { This code is a simplification of the actual code /// Withdraw Tongos and send the ERC20 to a starknet address. /// /// Emits WithdrawEvent fn withdraw(ref self: ContractState, withdraw: Withdraw) { verify_withdraw(/* public inputs */, proof); let cipher = CipherBalanceTrait::new(from, amount, 'withdraw'); self._subtract_balance(from, cipher); self._transfer_to(to, amount); self.emit( WithdrawEvent ); if self.auditor.is_some() { self._handle_audit(auditPart); } } }
SHE Cryptography Library
The Starknet Homomorphic Encryption (SHE) library provides low-level cryptographic primitives for proving and verification of Sigma protocols over the Stark elliptic curve. This includes Zero-Knowledge proof of ElGamal encryption.
SHE is the cryptographic foundation of Tongo but its building blocks can be used in other products that rellies on basic Sigma Protocols.
Package Information
- Package:
@fatsolutions/she - Version: 0.4.0
- License: Apache-2.0
- Repository: github.com/fatlabsxyz/she
Protocols
Implemented zero-knowledge proofs:
- POE: Proof of Exponent (knowledge of discrete log)
- POE2: Proof of double exponent
- POEN: Proof of N exponents
- Bit: Proof that committed value is 0 or 1
- Range: Proof that value is in [0, 2^n)
- ElGamal Proof that a encryption is a correct ElGamal encryption
- SameEncryption: Proof that two ElGamal encryptions encrypt the same value
Sigma Protocols
All protocols implemented in SHE are Sigma protocols. A Sigma protocol is a protocol in which a prover \(P\) and a verifier \(V\) interact, after the interaction the verrifier can be convinced that the prover has knowledge of some witness \(x\) that satisfies a statement \(y\), the general strutcute of the interaction between the prover and the verifier is
- \(P\) computes a message \(A\) called commitment and sends it to \(V\)
- Upon receiving \(P\)'s commitment \(A\), \(V\) chooses a challenge \(c\) at random and sends it to \(P\)
- Upon receiving \(V\)'s challenge \(c\), \(P\) computes a response \(s\) and sends it to \(V\)
- Upon receiving \(P\)'s response \(c\), \(V\) outputs either
acceptorrejectbased only on the statement \(y\) and the interaction \((A, c, s)\).

Fiat-Shamir Euristics
The protocol descrived above is interactive because the prover and the verifier are forced to respond one to another. The standar way of converting an interactive protocol into a non-interactive one is by using the Fiat-Shamir heuristic. Broadly speaking this means to use as a challenge \(c\) some hash of the commitment \(A\). This means the prover can compute \(c\) and there is not need to wait for the verifier to create a full proof.
Warning Appliying the Fiat-Shamir transformation is very critical. There is a fundamental reason in the original protocol for the verifier to wait upon reception of the commitment \(A\) to create a challenge \(c\). Usually a prover that has seen the challenge before commiting anything can forge a proof without knowledge of the witness \(x\).
Implementation in SHE
In the cairo implementation of the SHE protocols, for each protocol we expose a verify() function. This function mimics the last step for the verifier. It accepts or rejects the proof and take as imputs the statement \(y\), the commitment \(A\), the challenge \(c\) and the response \(s\).
We have also exposed functions called verify_with_prefix(). These functions dont take the challenge \(c\) as an input, in its place the take a prefix. Internally the function computes the challenge \(c\) by hashing the comitment \(A\) with the given prefix
$$
c = \text{Hash}(\text{prefix}, A)
$$
The prefix is useful to bind some external data to the proof (the proof at this stage can also be seen as a signature of the prefix). For example in Tongo part of the prefix is the chain_id so any proof intended to be validated in mainet will not be valid in sepolia.
Here there is a example of the implementation of this function for the POE protocol
#![allow(unused)] fn main() { pub fn verify_with_prefix(inputs: PoeInputs, proof: PoeProofWithPrefix) -> Result<(), Errors> { let PoeInputs { y, g } = inputs; let PoeProofWithPrefix { A, prefix, s } = proof; let commitments = array![A]; let c = compute_challenge(prefix, commitments); verify(y, g, A, c, s) } }
Proof of Exponent (POE)
The POE protocol is a building block of other protocols. Given the relation
$$ y = g^x $$
where \(g\) is a known generator point, \(y\) is a known public point and \(x\) is the secret witness. A Zero-Knowledge proof of exponent is used to show knowledge of \(x\) such that the previous relation holds.
Protocol (Interactive)

Cost Analysis (EC Operations)
Prover Complexity
- 1 EC multiplications
Verifier Complexity
- 2 EC multiplications
- 1 EC addition
Usage in Tongo
POE is directly used in Tongo to prove account ownership in all operations. It is also used indirectly as a building block of other SHE protocols.
Proof of 2 Exponent (POE2)
The POE2 protocol is the fisrt generalization of the POE protocol. Given the relation
$$ y = g_1^{x_1} g_2^{x_2} $$
where \(g_1, g_2\) are two generator points with discrete log relation unknown, \(y\) is a known public point and \(x_1, x_2\) are the secret witnesses. The POE2 procolo is used to show knowledge of \(x_1, x_2\) such that the previous relation holds. The POE2 protocol is used by SHE in the ElGamal protocol, which is a ZK protocol that shows the correctness of an ElGamal encryption.
Protocol (Interactive)

Cost Analysis (EC Operations)
Prover Complexity
- 2 EC multiplications
- 1 EC addition
Verifier Complexity
- 3 EC multiplications
- 2 EC addition
Usage in Tongo
POE2 is not used directly by Tongo, it is used in an indirect way as a part of ElGamal protocol to verify encryption related Zero-Knowledge proofs.
Proof of N Exponent (POEN)
The POEN protocol is the generalization of POE to N exponents. Given the relation
$$ y = \prod_{i=1}^{N} g_i^{x_i}$$
where \(g_i\) are \(N\) different generatos with discrete log relation unknown, \(y\) is a known public point and the set of \(x_i\) are the secret witnesses. The POEN procolo is used to show knowledge of \(x_i\) such that the previous relation holds.
Design choice: Althoug POE and POE2 are particular cases of this protocol and having only the protocol POEN should be enough, we have decided to have the three protocols to reduce some overhead.
Protocol (Interactive)

Cost Analysis (EC Operations)
Prover Complexity
- N EC multiplications
- N-1 EC addition
Verifier Complexity
- N+1 EC multiplications
- N EC addition
Usage in Tongo
POEN is not directly used by Tongo and it is not used by another SHE protocol at the moment.
ElGamal
Given a public key \(y\), an ElGamal encryption of an amount \(b\) with randomness \(r\) is a pair of elliptic curve points of the for
$$ (L, R) = \left( g^b y^r,\ g^r\right) $$ where $g$ is the chosen generator. This protocol is used to prove to a verifier that a given ElGamal encryption is exactly of this form. To show this, the prover must prove knowledge of:
- \(r\) such that \(R = g^r\)
- \(b\) such that \(L = g^b y^r\) for the given public key and with the same \(r\) as before.
Note that the first assertion can be proven with a POE protocol and the second one with a POE2 protocol. We just need to combine the both of them in a single protocol.
Aclaration: The two generators used in the POE2 procol must satisfy that there is not know discrete log relation between them. Here, the two generators would be \(g\) and \(y\) whose discrete log relation is the secret key \(x\) known by the owner of the public key. A simple POE2 protocol in this setup would be insecure. The extra restriction that the blinding factor \(r\) is encoded in the $R$ part of the encryption is enoguh to avoid any posible attack to the POE2 protocol.
Protocol (Interactive)
The checks the verifier performs are actually a POE check and a POE2 check. This protocol delegates the assertions to the fundamental building blocks POE and POE2.
Cost Analysis (EC Operations)
Prover Complexity
- 3 EC multiplications
- 1 EC addition
Verifier Complexity
- 5 EC multiplications
- 3 EC addition
Usage in Tongo
All encryptions given by the user in Tongo pass through ElGamal protocol. Most of them are invoked by another SHE protocol that shows that two given encryptions are valid and they are indeed encrypting the same amount.
Same Encryption Proof
Given two ElGamal encryptions of an amount \(b\) for two (possibly different) public keys \(y_1\) and \(y_2\), this protocol is used to convince a verifier that both encryptions are valid ElGamal encryptions under their respective public key and that both of them encrypt the same amount. In this proof, the prover is assumed to know the randomness of both encryptions and, of course, the amount \(b\).
The encryptios are $$ (L1, R1) = \left( g^b y_1^{r_1},\ g^{r_1}\right)\\ (L2, R2) = \left( g^b y_2^{r_2},\ g^{r_2}\right) $$ The prover must show that:
- \((L1, R1)\) is a valid encryption under \(y_2\) for and amount \(b\)
- \((L2, R2)\) is a valid encryption under \(y_2\) for the same amount \(b\)
Note that the both assertion can be proven with ElGamal protocol. We just have to combine them in a way that the share the same secret value \(b\)
Protocol (Interactive)
The checks the verifier performs are actually two ElGamal protocol checks. This protocol just delegates these assertions.
Cost Analysis (EC Operations)
Prover Complexity
- 6 EC multiplications
- 2 EC addition
Verifier Complexity
- 10 EC multiplications
- 6 EC addition
Usage in Tongo
In a trnasfer operation in Tongo, the sender must provide two encryption of the same amount, one is to be added to the pending balance of the receiver and the other one to be subracted from the sender's balance. The sender uses this protocol to show the validity of those encryptions.
Variation: Unknown Random
This variation of the previous protocol allows the prover to ignore the randomness of one of the encryptions. The price to pay is that the prover must know the secret \(x\) of the public key \(y= g^x\) the encryptions is made for. This works because the \(L\) point in a valid ElGamal encryption can be seen as a commitment to \(b\) and \(x\) with generators \(g\) and \(R\), that is
$$ (L, R) = \left( g^b y^{r},\ g^{r}\right) = \left( g^b R^{x},\ R\right) $$ In the RHS of the previous equation, the randomness is unknown. We can prove that this is a correct encryption with a POE2 protocol for the \(L\) part of the encryption
Aclaration: The two generators used in the POE2 procol must satisfy that there is not know discrete log relation between them. Here, the two generators would be \(g\) and \(R\) whose discrete log relation is the randomness \(r\) that might be known by the constructor of the original encryption. A simple POE2 protocol in this setup would be insecure. The extra restriction that the secret \(x\) is exactly the secret of the public key \(y\) is enoguh to avoid any posible attack to the POE2 protocol.
We have in this setup two encryptions $$ (L1, R1) = \left( g^b R1^{x},\ R1\right) \\ (L2, R2) = \left( g^b y_2^{r_2},\ g^{r_2}\right) $$
The prover must show that:
- Knowledge of \(x\) such that \(y = g^x\)
- Knowledge \(b\) such that \(L1 = g^b R1^x\) for the given \(R1\) key and with the same \(x\) as before.
- \((L2, R2)\) is a valid encryption under \(y_2\) for the same amount \(b\)
Design choice There is a way to reuse the original SameEncrypt protocol to prove this. The way of do it requires swapping \(r_1 \leftrightarrow x\) and \(R1 \leftrightarrow y_1\). We think that making these changes to call provers/verifiers would be very confusing and error prone. So we have decided to write a separate protocol to handle this case.
Protocol (Interactive)

Verifier Complexity
- 10 EC multiplications
- 6 EC addition
Usage in Tongo
In transfers/withdraw operations in Tongo, user must show that the remaining balance, after the operation, is positive. Zero-Knowledge proof in these cases are to be checked against the current balance of the account. Users generaly dont know the randomness of the encryption of the current balance: is the sum of all randomness generated by the senders, of previous incoming transfers that had them as receiver.
To prove that the remaining balance is positive, the Zero-Knowledge proof consists in the creation of an auxiliar encryption, for this encryption a Range protocol shows that is encrypting a positive balance, and then the prover shows that this auxiliar encryption and the current balance of the account are the same. For this last step, the UnknownRandom version of the SameEncrypt protocol is used
Bit Proofs
This protocol is usded to prove that a committed value is either zeor or one without revealing which one is correct. It is a fundamental piece of the Range protocol. Given a commitment of the form $$ V = g^b h^r $$ where \(g\) and \(h\) are two generator points with discrete log relation unknown, \(b\) is either \(0\) or \(1\) and \(r\) is a blinding factor. We have two path here
- \(b\) is \(0\), then \(V = h^r\)
- \(b\) is \(1\), then \(\frac{V}{g} = h^r\)
Note that in each cases it is enough to use a POE to show knowledge of \(r\). Two show that one of the paths is the correct one without revealing which one is the case, we have to combine two POE protocols with an OR statement.
Note: Given two Sigma protocols, the way of combine them in an OR statement is well known. The idea is that the prover will use the standard protocol for the statement that is true (i.e. that can be proven) and simulate a valid transcript for the statment that is false. On validation, the verifier will know that only one of the statement is simulated but it will be imposible to known which one it is.
Bit proofs demonstrate that a committed value is either 0 or 1 using OR proof construction.
Simulator for POE
A valid transcript for the POE protocol is a triad \((A, c, s)\) that passes the validation check of the verifier. The POE protocol is used to show, given a \(y\), knowledge of \(x\) shuch that \(y = g^x\). A simulator for this protocols is an algorithm that, given \(y\), produces a valid transcript, without knowledge of \(x\). In this case the simulator is $$\begin{array}{ll} Sim\left(y,g\right) &\rightarrow (A, c,s ): \lbrace \\ & c \leftarrow \mathbb{F}_p^{*}\\ & s \leftarrow \mathbb{F}_p^{*} \\ & A = g^s/y^c \\ & \text{returns} (A,c,s) \\ \rbrace & \end{array} $$ this transcript will pass the verifier because $$ g^s = A y^c = \dfrac{g^s}{y^c} y^c = g^s $$
Aclaration: Even though the simulaton can produce a valid proof for the POE protocol, it can never be used to convince to a verifier by engaging the a real interaction. In Sigma protocols the challenge \(c\) is chosen by the verifier after all the messages \(A\) were committed by the prover. The flow of the simulator implies that the message \(A\) is to be selected after the prover sees the challenge.
Protocol (Interactive)
Let say we are in the fisrt case, that is, we have \(b=0\), the commitment \(V\) is $$ V = h^r $$

If we are in the second case, that is, we have \(b=1\), the commitment \(V\) is $$ V = g\ h^r $$

Note that in both cases the verifier performs the same steps. The prove must perform different steps according what POE is going to be simulated. In both cases the prover sends $c_0$, even in the case that $c_0$ is given by the simulator. This is part of the protocol and it is to avoid revealing which $c_i$ was simulated.
Cost Analysis (EC Operations)
Prover Complexity
- 3 EC multiplications
- 1 EC addition
Verifier Complexity
- 4 EC multiplications
- 3 EC addition
Usage in Tongo
This protocol is not directly used in Tongo. It is used in an indirect way as part of Range protocol.
Range Proofs
This protocol allows a prover to convince a verifier that a commited value \(b\) belongs to a range \([0, 2^n)\). The protocol does this by ussing the binary decomposition of \(b\). We call bit_size to the integer \(n\). The commitment is of the form
$$
V = g^b h^r
$$
where \(g\) and \(h\) are two generator points with discrete log relation unknown, \(b\) is the number we want to show belongs to the range \([0, 2^n)\) and \(r\) is a blinding factor.
Protocol
Any value \(b \in [0, 2^n)\) can be written as:
$$b = \sum_{i=0}^{n-1} b_i \cdot 2^i$$
Where each \(b_i \in {0, 1}\). For each bit \(b_i\), the prover creates a commitment with independent randomness \(r_i\) of the form
$$V_i = g^{b_i} \cdot h^{r_i}$$
for each one of this commitments, the prover uses a Bit protocol to show that \(V_i\) is encoding either zero or one. Note that we can construct a commitment \(V\) with the commitments \(V_i\) by computing $$ V = \prod_{i=0}^{n-1} V_i^{2^i} = g^b h^{r_{total}} $$ where \(r_{total} = \sum_{i=0}^{n-1} r_i 2^i\). A verifier that constructs this \(V\) after all commitments \(V_i\) verify the Bit protocol will be convinced that $V$ is a comitment to a value \(b \in [0, 2^n)\). This commitment $V$ can now be used as part of other protocols depending on what the prover wants to show.
Cost Analysis (EC Operations)
Prover Complexity
- 3n EC multiplications
- n EC addition
Verifier Complexity
- 5n EC multiplications
- 4n EC addition
Usage in Tongo
In a transfer operation in Tongo, the sender must show that the sended amount is positive and the remaining balance is positive. To prove anyone of this, the prover submits a Range proof, then the reconstructed commitment \(V\) is used as the \(L\) part of a ElGamal encryption
$$
(L, R) = (V, g^{r_{total}})
$$
this is a valid encryption for the publick key \(h\). The sender then uses the SameEncrypt protocol to shows that it encrypts the same amount as the encryption created for a transfer (showing then that the transfered balance is positive), or the UnknownRandom version to show that it encrypts the same amount of the remaining balance (showing then that the remainign balance is positive)
Tongo TypeScript SDK
The Tongo TypeScript SDK provides a comprehensive interface for building confidential payment applications on Starknet. It handles key management, encryption, proof generation, and transaction serialization.
Features
- Simple API: High-level methods for all Tongo operations
- Type Safety: Full TypeScript support with complete type definitions
- Proof Generation: Automatic ZK proof creation for all operations
- Encryption Handling: Transparent management of encrypted balances
- Starknet Integration: Seamless integration with Starknet wallets and providers
Package Information
- Package:
@fatsolutions/tongo-sdk - Current Version: 1.3.1
- License: Apache-2.0
- Repository: github.com/fatlabsxyz/tongo
Supported Networks
The SDK works on:
- Starknet Mainnet - Production deployments
- Starknet Sepolia - Testnet for development
Check the deployed Tongo Instances for information about Tongo contracts wrapping different tokens.
Quick Links
- Installation - Install the SDK
- Quick Start - Your first Tongo transaction
- Core Concepts - Understand the fundamentals
- API Reference - Complete API documentation
- Examples - Real-world code examples
Quick Start
Installation
Using npm
To use the SDK you need to install it together with a starknet.js version superior to v8.
npm install @fatsolutions/tongo-sdk
npm install starknet@8.x.x
Basic Concepts
Starknet Account Class
This is the starknet account that will pay the transanction costs of the tx you send to starknet. To set it up the first step is to set a provider and then initializathe an Account class from the starknet library
import { Account, RpcProvider } from "starknet";
// Setup Starknet provider
const provider = new RpcProvider({
nodeUrl: "YOUR_RPC_PROVIDER",
specVersion: "0.8.1",
});
// Your Starknet account (for paying gas fees)
const signer = new Account({
provider,
address: "YOUR_STARKNET_ADDRESS",
signer: "YOUR_STARKNET_PRIVATE_KEY"
});
At this step, the signer can execute a well constructed call with signer.execute(call). You can read more about how to interact with a starknet contract in the starknet.js documentation
Tongo Account Class
This class represents a user's Tongo Account. The main feature is that it can construct the payloads for different Tongo operations (Fund/Transfer/Rollover/Withdraw/Ragequit). To create an instance to a Tongo account class you need the private key of the account, the Tongo address to interact with and a rpc provider.
import { Account as TongoAccount } from "@fatsolutions/tongo-sdk";
const tongoAddress = "TONGO_CONTRACT_ADDRESS";
const privateKey = "USER_TONGO_PRIVATE_KEY";
const tongoAccount = new TongoAccount(
privateKey,
tongoAddress,
provider
);
console.log("Your Tongo public key is:", tongoAcount.publicKey);
You can read more about the Tongo Account Class here
Basic Interactions: Operations
Whit a Tongo Account and a Starkenet Account set up, you can start to create and execute Tongo Operations (Fund/Transfer/Rollover/Withdraw/Ragequit). You can read more about Operations and the way of creating them here. To execute an operation need to create the call with the Tongo Account class and the execute it with the Starknet Account class.
const operation = tongoAccount.someOperation({...params});
const call = operation.toCalldata();
signer.execute(call)
Account Class
The Account class is the main interface for interacting with Tongo. An instance of a Tongo Account represents a user's account for a specific Tongo contract. Some of the functionalities of the Account are:
- Decrypting the balance of the user.
- Creating the Operations with the ZK proofs needed.
- Decrypting and showing the transaction history for the user.
Creating an Account
import { Account as TongoAccount } from "@fatsolutions/tongo-sdk";
import { RpcProvider } from "starknet";
const provider = new RpcProvider({
nodeUrl: "YOUR_RPC_URL",
specVersion: "0.8.1",
});
const tongoAddress = "TONGO_CONTRACT_ADDRESS";
const privateKey = "USER_TONGO_PRIVATE_KEY";
const tongoAccount = new TongoAccount(
privateKey,
tongoAddress,
provider
);
Public Key
Each instance of a Tongo Account class is identified by its public key. At low level the public is the elliptic curve point
$$
pk = g^{sk}
$$
where \(pk\) is the public key, \(sk\) is the secret key and \(g\) is the stark curve generator. This form of the public key is used at low level to create the Zero-Knoledge proofs. To see this representation of the public key of an instance of a Tongo Account you can view the publicKey property:
console.log(account.publicKey);
// { x: bigint, y: bigint }
To have a cleaner repesentatin of the public key we offer a base58-encoded one. We call it the Tongo address of a given account:
const address = account.tongoAddress();
console.log(address);
// "Um6QEVHZaXkii8hWzayJf6PBWrJCTuJomAst75Zmy12"
Note We offer the utility functions
pubKeyBase58ToAffine()andpubKeyAffineToBase58()in the filetypes.tsto facilitate the conversion betweer the two representations of the public key.
Account State
The high level state of a Tongo Account at a given time is its current and pending balances and its nonce. We offerd a state() method to get this
const state = await account.state();
console.log(state);
/*
{
balance: bigint, // Decrypted balance
pending: bigint, // Decrypted pending
nonce: bigint // Account nonce
}
*/
when ussing this method, the Account first quieries the Tongo contract for the its current raw state and then then decrypts the balances using the encrypted hints stored in the contract. The raw state of the account (the state that is stored in the Tongo contract and it is visible to anyone) can be get with the rawState() method:
const rawState = await account.rawState();
console.log(rawState);
/*
{
balance: CipherBalance, // Encrypted balance
pending: CipherBalance, // Encrypted pending balance
audit: CipherBalance | undefined, // Encrypted balance for auditor
nonce: bigint,
aeBalance: AEBalance | undefined, // Encrypted hint to decrypt `balance`
aeAuditBalance: AEBalance | undefined //Encrypted hint to decrypt `audit`
}
*/
Account Operations
Accounts can create various operations. Operation are the the only way of transact within a Tongo contract. You can read more about them here.
Transaction History
The Tongo Account class has a method that allow the user to get its transaction history. Each operation made in Tongo emits an event with the relevant (generally encrypted) information. The getTxHistory() method fetches the emitted events starting from a given block_number, parses the data and decrypts it when necesary. The method returns a block-ordered array of all Tongo transactions involving the Tongo Account
// The initial block number to fetch the history from
const initial_block = 0;
const tx_history = await account.getTxHistory(initial_block);
console.log(tx_history);
/*
[
{
type: 'withdraw',
tx_hash: '0x3ee8a6a351b05b4684e3e329399f6df02c446ce986c1e0be925ca71b757c6e0',
block_number: 6,
nonce: 2n,
amount: 1n,
to: '0x075662cc8b986d55d709d58f698bbb47090e2474918343b010192f487e30c23f"'
},
{
type: 'transferOut',
tx_hash: '0x3dc4e84d5212c125bb92e43c0c097d4630ec7899d60bdca408f7bcdb563b0c1',
block_number: 4,
nonce: 1n,
amount: 23n,
to: 'tpBg43FFq7SQhmimTMxubT7cJ4dDpjsp5r2TtYYToKV9'
},
{
type: 'fund',
tx_hash: '0x4e134a86b86db0fe494e030d9b3baa664f5ca51750051cda759d06e27931e1',
block_number: 3,
nonce: 0n,
amount: 100n
}
]
*/
Operations
Operations are objects that represent Tongo transactions. Each operation encapsulates the cryptographic proofs, encrypted data, and calldata needed for a specific action.
Creation and Execution
Operations are created by an instance of a Tongo Account Class and executed by a starknet signer. The general lifecycle of an Operation is
// 1. Create the operation
const operation = await tongoAccount.someOperation({...params});
// 2. Convert to calldata
const calldata = operation.toCalldata();
// 3. Execute with Starknet signer
const tx = await signer.execute([calldata]);
// 4. Wait for confirmation
await provider.waitForTransaction(tx.transaction_hash);
All operations have a toCalldata() method that serialized all the needed data and contructs the starknet call. This call is to be executed by the transaction sender or signer.
Each operation has its own set of params that are needed to create it. Some generalities are shared between the parameters of Operations and requires further explanations
The sender parameter
This parameter is present in all operations. It is the starknet account address that will execute the transaction (the signer address in the example). This address is binded in the Zero-Knowledge proofs. ZK proofs will only be valid if the sender of the transaction is matches this parameter.
The amount parameter
This parameter, when present, represents a amount of Tongos to transact with. This is NOT the amount of ERC20 you pretend to transact with. When you wrap some amount of ERC20 you recive some other amount of wraped ERC20 (we call them Tongos). The rate of conversion is fixed and defined when that particuar instance of Tongo was deployed.
Note that 1 unit of Tongo is the minimum amount that a operation can handle. The Tongo Account class has the rate() method to query the rate of the Tongo contract and the tongoToERC20() get te equivalent amount of ERC20 to some amount of Tongos.
Operation Types
Tongo supports five core operations, we describe them in the following sections:
- Fund - Convert ERC20 tokens to encrypted balance
- Transfer - Send encrypted amounts to another account
- Rollover - Claim pending incoming transfers
- Withdraw - Convert encrypted balance back to ERC20
- Ragequit - Emergency withdrawal of entire balance
Fund Operation
This operation converts ERC20 tokens into encrypted Tongo balance. The parameters are:
amount: The amount of encrypted Tongo balance you want to fund to your Tongo account.sender: The sender of the transaction. In this case it is also the starknet account that will send the ERC20 to the Tongo contract to convert into encrypted Tongo balance.
On Fund operations, the Tongo contract calls the function transfer_from() form the ERC20 to pull assets and assign encrypted balance the to Tongo account. For this to work the sender has to sign an Approval in the ERC20 to allow the Tongo contract to move funds.
The Approval call is also created by the Fund operation, so the sender does not have to create it.
const fundOp = await account.fund({
amount: "AMOUNT_TO_FUND"
sender: "SENDER_ADDRESS"
});
// Execute both approval and fund
await signer.execute([
fundOp.approve!, // ERC20 approval
fundOp.toCalldata() // Fund operation
]);
Balance Handling
When receiving a Fund operation for some amount, the cairo contract creates an ElGamal encryption of that amount for the account public key and fixed randomness. This encryption is added to the balance of the account. The pending balance of the account is not manipulated in this operation.
Zero-Knowledge Proof
In this opretaion, the only thing that has to be proven is the ownership of the Tongo account. This is done by proving knowledge of the account's private key.
Transfer Operation
This operation sends encrypted amounts between two Tongo accounts of the same Tongo instance without revealing the transfer amount. The parameters are:
amount: The amount of encrypted Tongo you want to transfer.to: The public key of the Tongo account thaw will receive the transfer.sender: The sender of the transaction.
const transferOp = await senderAccount.transfer({
to: "RECEIVER_PUBLIC_KEY",
amount: "AMOUNT_TO_TRANSFER"
sender: "SENDER_ADDRESS"
});
const tx = await signer.execute([transferOp.toCalldata()]);
await provider.waitForTransaction(tx.transaction_hash);
Balance Handling
As part of the Transfer operation, the user gives two ElGamal encryption of the same amount, one is encrypted for the sender's public key and it is subtracted from the sender's balance. The other one is encrypted for the receiver's public key and it is added to the receiver's pending balance.
Zero-Knowledge Proof
In this operation, the ZK proof given by the sender shows:
- Ownership of the sender account.
- The two given encrpytion are valid encryptions for the same amount under the correct public keys.
- The amount encrypted in positive.
- After the subtraction, the sender's balance is positive.
Rollover Operation
This operation takes the pending balance of the account and converts it to actual usable balance for the account. The parameter is:
sender: The sender of the transaction.
const rolloverOp = await account.rollover({
sender: "SENDER_ADDRESS"
});
await signer.execute([rolloverOp.toCalldata()]);
Balance Handling
When receiving a Rollover operation, the cairo contract adds the user's pending balance to the user's balance. After that the user's pending balance is reseted to zero.
Zero-Knowledge Proof
In this opretaion, the only thing that has to be proven is the ownership of the Tongo account. This is done by proving knowledge of the account's private key.
Withdraw Operation
This operation converts encrypted Tongo balance to ERC20 tokens and sends them to the given starknet account address. The parameters are:
amount: The amount of Tongo balance to withdraw.to: The starknet account address to send the ERC20 to.sender: The sender of the transaction.
Convert encrypted Tongo balance back to ERC20 tokens.
const withdrawOp = await senderAccount.withdraw({
to: "RECEIVER_STARKNET_ACCOUNT_ADDRESS",
amount: "AMOUNT_TO_WITHDRAW"
sender: "SENDER_ADDRESS"
});
const tx = await signer.execute([withdrawOp.toCalldata()]);
await provider.waitForTransaction(tx.transaction_hash);
Balance Handling
When receiving a Withdraw operation for some amount, the cairo contract creates an ElGamal encryption of that amount for the account public key and fixed randomness. This encryption is subtracted from balance of the account. The pending balance of the account is not manipulated in this operation.
Zero-Knowledge Proof
In this operation, the ZK proof given by the sender shows:
- Ownership of the user account.
- After the subtraction, the user's balance is positive.
Ragequit Operation
This operation converts all the encrypted Tongo balance to ERC20 tokens and sends them to the given starknet account address. The parameters are:
to: The starknet account address to send the ERC20 to.sender: The sender of the transaction.
const ragequitOp = await senderAccount.ragequit({
to: "RECEIVER_STARKNET_ACCOUNT_ADDRESS",
sender: "SENDER_ADDRESS"
});
const tx = await signer.execute([ragequit.toCalldata()]);
await provider.waitForTransaction(tx.transaction_hash);
Balance Handling
When receiving a Ragequit operation, the user discloses the total encrypted Tongo balance, after sending the unwrapped ERC20 to the starknet account address, the cairo contract resets the user's balance to zero. The pending balance of the account is not manipulated in this operation.
Zero-Knowledge Proof
In this opretaion, the only thing that has to be proven is the ownership of the Tongo account. This is done by proving knowledge of the account's private key.
Zero-Knowledge Proof
In this operation, the ZK proof given by the sender shows:
- Ownership of the user account.
- The disclosed amount is the total balance of the user's account.