# Kohaku > Privacy-first tooling for the Ethereum ecosystem ## Get started Kohaku is a collection of privacy-first tooling for the Ethereum ecosystem. :::info TODO: Write intro ::: ### Packages Kohaku has a few packages that are worth checking out. * [@kohaku-eth/railgun](./railgun/intro.mdx) - A privacy preserving pool protocol that allows users to shield and unshield their ERC20 tokens. * [@kohaku-eth/privacy-pools](./privacy-pools/intro.mdx) (**WIP**) - A privacy preserving pool protocol that allows users to shield and unshield their ERC20 tokens. * [@kohaku-eth/tornado](./tornado/intro.mdx) (**WIP**) - A privacy preserving pool protocol that allows users to shield and unshield their ERC20 tokens. Other noteworthy packages to checkout include: * [@a16z/helios](https://github.com/a16z/helios) - A lightclient developed by [a16z](https://a16z.com/). ### Wallet Implementation Although the kohaku packages are wallet-agnostic by default, we have a wallet implementation that you can use to try it for yourself. [@ethereum/kohaku-extension](https://github.com/ethereum/kohaku-extension) - A wallet implementation for the kohaku packages. Forked from [@ambiretech/extension](https://github.com/ambiretech/extension) & [@ambiretech/ambire-common](https://github.com/ambiretech/ambire-common). ## Wallet Best Practices \[What does the ultimate wallet look like?] ### Transaction Privacy By default ethereum transactions are transparent. This means anyone can see the amount, sender, recipient, and data within the transaction. However, there are smart-contract based privacy protocols that allow you to have more control over the visibility of your transactions. A few examples include: * [Railgun](https://railgun.org/) see ([@kohaku-eth/railgun](./railgun/intro.mdx)) * [Privacy Pools](https://privacypools.com) see ([@kohaku-eth/privacy-pools](./privacy-pools/intro.mdx)) * ~~[Tornado Cash](https://tornado.cash) see ([@kohaku-eth/tornado](./tornado/intro.mdx))~~ ### Walletbeat [Walletbeat](https://beta.walletbeat.eth.limo/) aims to provide a comprehensive overview of the Ethereum wallet ecosystem. In addition to holding wallets to best practices, and ensuring the end-users have the best possible experience. We advise you to have a look yourself. ### Many accounts, many you Or maybe more realistic, different accounts for different things. The process to create a new account, top it up, and get going should be as seamless as possible. A good first step would be to have a "create new" button, when connecting to a dApp. In addition to a privacy-friendly way to top up the your newly created account. ### Private RPC The source of truth for data within a wallet is access to the Ethereum Network. This is done through Remote Procedure Calls (a.k.a. RPC). It hence of importance that access to RPC (essentially what data you lookup) is private. The traditional (and what should be the default) approach is to use a user-defined RPC endpoint. Ideally this is a locally run ethereum node, though this may not always be feasible. #### Lightclients & Yolo Nodes So what if, we could run a lightweight client on the users device, keep a checkpoint sync of the network, and verify results from an untrusted RPC on the fly? This is where lightclients come in. [Helios](https://github.com/a16z/helios) is a lightclient developed by [a16z](https://a16z.com/) that does just this. It lets you run a wrap an existing RPC and verify its results. #### Network Privacy The extremely privacy caution person, such as a journalist or privacy enthusiast, may want to add an extra layer of protection by routing their traffic through Tor. ## Privacy in Ethereum \[Where are we at?] ### One account to rule them all When you connect to a dApp today you are typically asked connect your wallet. This allows for anyone to see exactly what you have interacted with, when, and how. Spreading your identity across multiple accounts, by for example having one address per dApp could already be a good start. Unfortunately not many wallets operate this way, most have a user experience designed for "a single active account at a time". ### Default RPC A lot of wallets today use a default hardcoded RPC endpoint. Some even hitting the endpoint before the user has had a chance to setup their preferred configuration. In addition to introducing centralization, this also introduced the risk of associating you with your wallet activity. ### Token Discovery Due to the nature of how token discovery works the majority of wallets today use a centralized indexer to monitor "all transfer events" on-chain. This allows for the discovery of new tokens right as theyre sent to your wallet. Although this is a nice-to-have it does have privacy drawbacks. ## Introduction to Tornado \[A privacy preserving pool protocol] ### Installation Install [@kohaku-eth/tornado](https://www.npmjs.com/package/@kohaku-eth/tornado) via your package manager of choice. :::code-group ```bash [npm] npm install @kohaku-eth/tornado ``` ```bash [pnpm] pnpm install @kohaku-eth/tornado ``` ```bash [yarn] yarn install @kohaku-eth/tornado ``` ```bash [bun] bun install @kohaku-eth/tornado ``` ::: ### Next Steps Now that you have tornado installed you can start using it to create and spend notes. ## Notes Tornado uses the concept of notes. A note represents a fixed amount of a token. For example 1 ETH, 10 ETH, 100 ETH, etc. :::info TODO ::: ## Transacting :::info TODO ::: ## Accounts :::info TODO: Write intro ::: ### Setup #### Generate a BIP39 Mnemonic :::code-group ```ts filename="viem.ts" twoslash [Viem] // @noErrors import { english, generateMnemonic } from 'viem/accounts' const mnemonic = generateMnemonic(english) // [!code focus] const accountIndex = 0 ``` ```ts filename="raw.ts" twoslash [Raw] // @noErrors const mnemonic = 'test test test test test test test test test test test test test junk' const accountIndex = 0 ``` ::: #### Initialize Account and Indexer Putting it all together ```ts twoslash filename="result.ts" // @noErrors import { createRailgunAccount, createRailgunIndexer, ViemProviderAdapter, RAILGUN_CONFIG_BY_CHAIN_ID } from '@kohaku-eth/railgun' import { createPublicClient, http } from 'viem' const mnemonic = 'test test test test test test test test test test test junk' const accountIndex = 0 const chainId = '11155111' as const const network = RAILGUN_CONFIG_BY_CHAIN_ID[chainId] // Create a provider first const publicClient = createPublicClient({ transport: http('https://sepolia.rpc.example'), }) // Create the indexer const indexer = await createRailgunIndexer({ network, provider: new ViemProviderAdapter(publicClient), }) // Create the account with specified signer and indexer const account = await createRailgunAccount({ credential: { type: 'mnemonic', mnemonic, accountIndex }, indexer, }); // Sync the indexer to the latest block await indexer.sync() ``` #### Get 0zk Address Every railgun account has a 0zk address that can be used to receive and send tokens. :::code-group ```ts twoslash [address.ts] filename="address.ts" import { account } from './config.ts' const zkAddress = await account.getRailgunAddress(); // 0zk ``` ```ts twoslash [config.ts] filename="config.ts" import { createRailgunAccount, createRailgunIndexer, ViemProviderAdapter, RAILGUN_CONFIG_BY_CHAIN_ID } from '@kohaku-eth/railgun'; import { createPublicClient, http } from 'viem'; const mnemonic = 'test test test test test test test test test test test junk' const chainId = '11155111' as const const network = RAILGUN_CONFIG_BY_CHAIN_ID[chainId] const indexer = await createRailgunIndexer({ network, provider: new ViemProviderAdapter(createPublicClient({ transport: http('https://sepolia.rpc.example') })), }) export const account = await createRailgunAccount({ credential: { type: 'mnemonic', mnemonic, accountIndex: 0 }, indexer, }); ``` ::: You can now receive funds to this address, see [Transacting](/railgun/txs) for more details. ### Getting Balances ```ts twoslash // @noErrors import { createRailgunIndexer, createRailgunAccount, ViemProviderAdapter } from '@kohaku-eth/railgun'; import { createPublicClient, http } from 'viem'; const chainId = '11155111' as const const network = RAILGUN_CONFIG_BY_CHAIN_ID[chainId] const startBlock = 4495479 const mnemonic = 'test test test test test test test test test test test junk' // Get current block const publicClient = createPublicClient({ transport: http('https://sepolia.rpc.example'), }); const endBlock = await publicClient.getBlockNumber() // Create indexer and sync const indexer = await createRailgunIndexer({ network, provider: new ViemProviderAdapter(publicClient), }); // Create account export const account = await createRailgunAccount({ credential: { type: 'mnemonic', mnemonic, accountIndex: 0 }, indexer, }); await indexer.sync(); // Get balance - optional token parameter (undefined = native gas token balance) const balance = await account.getBalance(); ``` #### Syncing Merkle Roots ```ts twoslash // @noErrors import { ByteUtils, createRailgunAccount, createRailgunIndexer, ViemProviderAdapter } from '@kohaku-eth/railgun'; const mnemonic = 'test test test test test test test test test test test junk' const accountIndex = 0 export const publicClient = createPublicClient({ transport: http('https://sepolia.rpc.example'), }); export const indexer = await createRailgunIndexer({ network, provider: new ViemProviderAdapter(publicClient), }); export const account = await createRailgunAccount({ credential: { type: 'mnemonic', mnemonic, accountIndex }, indexer, }); await indexer.sync(); const root = await indexer.getLatestMerkleRoot(); console.log('latest merkle root:', ByteUtils.hexlify(root, true)); ``` #### Storage Considerations To sync faster you could retain the previously indexed receipts. And re-use them in future syncs to save having to re-index. Railgun syncing happens incrementally, as such you can store previous receipts to speed up future syncs. ## Introduction to Railgun \[A privacy preserving pool protocol] Railgun is a privacy preserving pool protocol that allows users to shield and unshield their ERC20 tokens. This means any ERC20 (any ETH by extension through Wrapped ETH) can be shielded, internally transfered, and unshielded. You can read more about the protocol [here](https://railgun.org). ### Installation Install [@kohaku-eth/railgun](https://www.npmjs.com/package/@kohaku-eth/railgun) via your package manager of choice. :::code-group ```bash [npm] npm install @kohaku-eth/railgun ``` ```bash [pnpm] pnpm install @kohaku-eth/railgun ``` ```bash [yarn] yarn add @kohaku-eth/railgun ``` ```bash [bun] bun install @kohaku-eth/railgun ``` ::: ### Next Steps Now that you have railgun installed you can start using it to create a railgun account. ## Private Proof of Innocence :::info TODO ::: For more information see [Railgun documentation](https://docs.railgun.org/wiki/assurance/private-proofs-of-innocence). ## Shielding :::info TODO: Write intro ::: ### Shielding ETH When shielding you are essentially depositing an ERC20 token into the railgun system. This transaction comes from the account you want to shield from. :::code-group ```ts [shield-eth.ts] filename="shield-eth.ts" // @noErrors import { account, wallet } from './config.ts'; const VALUE = 10000000000000n; // 0.00001 ETH // Shield native ETH (auto-wraps to WETH) const tx = await account.shieldNative(VALUE); // [!code focus] // Eoa that will send the tx await wallet.sendTransaction({ to: tx.to, data: tx.data, value: tx.value, gasLimit: 6000000 }); ``` ```ts [config.ts] filename="config.ts" // @errors: 2307 import { createRailgunAccount, createRailgunIndexer, ViemProviderAdapter, RAILGUN_CONFIG_BY_CHAIN_ID, } from '@kohaku-eth/railgun'; import { createPublicClient, createWalletClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; const rpcUrl = 'https://sepolia.rpc.example'; const publicClient = createPublicClient({ transport: http(rpcUrl) }); const mnemonic = 'test test test test test test test test test test test junk'; const accountIndex = 0; const chainId = '11155111' as const; const network = RAILGUN_CONFIG_BY_CHAIN_ID[chainId]; // Create wallet client for signing export const wallet = createWalletClient({ transport: http(rpcUrl), }); const indexer = await createRailgunIndexer({ network, provider: new ViemProviderAdapter(publicClient), }); // Create the account with provided credentials and indexer export const account = await createRailgunAccount({ credential: { type: 'mnemonic', mnemonic, accountIndex }, indexer, }); // Sync indexer to include account state await indexer.sync(); ``` ::: :::info The `shield()` method will wrap the ETH to WETH before shielding. ::: ### Shielding ERC20 Railgun allows for shielding any ERC20 token. ```ts // @noErrors import type { RailgunAccount } from '@kohaku-eth/railgun'; import type { WalletClient } from 'viem'; declare const account: RailgunAccount; declare const wallet: WalletClient; const VALUE = 10000000000000n; // 0.00001 WETH const WETH = '0xfFf9976782d46CC05630D92EE39e704800271F7D'; const tx = await account.shield(WETH, VALUE); // [!code focus] // Eoa that will send the tx await wallet.sendTransaction({ to: tx.to, data: tx.data, value: tx.value, gasLimit: 6000000 }); ``` ### Private Transfers Transfer funds privately between railgun accounts using the `transfer()` method: ```ts // @noErrors import type { RailgunAccount } from '@kohaku-eth/railgun'; import type { WalletClient } from 'viem'; declare const account: RailgunAccount; declare const wallet: WalletClient; const WETH = '0xfFf9976782d46CC05630D92EE39e704800271F7D' as const; const amount = 50000000000000n; // 0.05 WETH const recipientZkAddress = '0zk1qy...'; // recipient's 0zk address const tx = await account.transfer(WETH, recipientZkAddress, amount); // [!code focus] // Send the transaction await wallet.sendTransaction({ to: tx.to, data: tx.data, value: tx.value, gasLimit: 6000000 }); ``` ### Syncing account state When you manage more than one Railgun account, wire them through the same indexer so each address stays in sync from a single log stream. ```ts // @noErrors import { createRailgunIndexer, createRailgunAccount } from '@kohaku-eth/railgun'; import { createPublicClient, http } from 'viem'; const chainId = '11155111' as const; const rpcUrl = 'https://sepolia.rpc.example'; const publicClient = createPublicClient({ transport: http(rpcUrl) }); const provider = new (await import('@kohaku-eth/railgun')).ViemProviderAdapter(publicClient); const railgunSepolia = RAILGUN_CONFIG_BY_CHAIN_ID[chainId]; const indexer = await createRailgunIndexer({ network: railgunSepolia, provider, }); // Create multiple accounts const aliceAccount = await createRailgunAccount({ credentials: { mnemonic: 'alice mnemonic here', accountIndex: 0 }, indexer, }); const bobAccount = await createRailgunAccount({ credentials: { mnemonic: 'bob mnemonic here', accountIndex: 0 }, indexer, }); await indexer.sync(); const root = await indexer.getLatestMerkleRoot(); console.log('root: ', root); ``` ### Using Colibri-Stateless as RPC Provider Colibri implements the EIP-1193 provider interface, so it can be used directly as the RPC source for the indexer. See the Colibri JS/TS bindings docs: [`colibri-stateless JS/TS bindings`](https://corpus-core.gitbook.io/specification-colibri-stateless/developer-guide/bindings/javascript-typescript). ```ts import { createRailgunIndexer, RAILGUN_CONFIG_BY_CHAIN_ID } from '@kohaku-eth/railgun'; const chainId = '1' as const; const network = RAILGUN_CONFIG_BY_CHAIN_ID[chainId]; const indexer = await createRailgunIndexer({ network, rpc: { type: 'colibri', colibri: { prover: ['https://mainnet.colibri-proof.tech'] }, }, }); ``` ### Considerations When shielding ERC20 there are some things to consider. Please refer to the [Railgun documentation](https://docs.railgun.org/wiki/learn/shielding-tokens) for more information. ### Fees In addition to the expected network fees (gas fees), there is also a [Protocol Fee](https://docs.railgun.org/wiki/learn/railgun-deductions) (when shielding & unshielding) and [Broadcaster Premium](https://docs.railgun.org/wiki/learn/railgun-deductions). Broadcaster Premiums are up to the individual Broadcasters, according to the railgun documentation "generally they are \~10% of the total gas"[\*](https://docs.railgun.org/wiki/learn/railgun-deductions). ## Transacting After your tokens have been shielded you can use them within the railgun system. This includes privately sending them to another railgun account, interacting with smart contracts, defi, and the like. ### Internal Transfer ```ts twoslash // @noErrors import type { RailgunAccount } from '@kohaku-eth/railgun'; import type { WalletClient } from 'viem'; declare const account: RailgunAccount declare const wallet: WalletClient const chainId = '11155111' as const; const value = 10000000000000n; // 0.00001 WETH const receiver = '0zk1qy...'; // Railgun address const WETH = '0xfFf9976782d46CC05630D92EE39e704800271F7D' as const; const tx = await account.transfer(chainId, WETH, receiver, value); // Submit the tx await wallet.sendTransaction({ to: tx.to, data: tx.data, value: tx.value, gasLimit: 6000000 }); ``` ### Defi & Other Smart Contracts something something railgun and helper contract, unshielding, performing actions, shielding results. :::info TODO: Code snippet ::: See the [Railgun Adapter Documentation](https://docs.railgun.org/wiki/learn/integrating-railgun/adapt-modules) for more information. ## Unshielding :::info TODO: Write intro ::: ### From EOA This transaction can be sent from an EOA. This can be the recipient itself, or a different EOA. If the recipient is not an EOA, or does not have ETH yet, you can use a [relayer](#railgun-relayer) to submit the tx on your behalf. :::note Do note that this EOA will be the submitter of the tx, and thus linked to the recipient. ::: ```ts filename="vars.ts" twoslash // @noErrors const value = 10000000000000n; // 0.00001 ETH const recipient = '0x1234567890123456789012345678901234567890' as `0x${string}`; ``` :::code-group ```ts twoslash [Ethers] // @noErrors import type { RailgunAccount } from '@kohaku-eth/railgun'; import type { Wallet } from 'ethers'; declare const account: RailgunAccount declare const wallet: Wallet const chainId = '11155111' as const; const WETH = '0xfFf9976782d46CC05630D92EE39e704800271F7D' as const; const value = 10000000000000n; // 0.00001 ETH const recipient = '0x1234567890123456789012345678901234567890' as const; const tx = await account.unshield(chainId, WETH, recipient, value); // [!code focus] // Submit the tx using Ethers await wallet.sendTransaction({ to: tx.to, data: tx.data, value: tx.value, gasLimit: 6000000 }); ``` ```ts twoslash [Viem] // @noErrors import type { RailgunAccount } from '@kohaku-eth/railgun'; import { createWalletClient, http } from 'viem' declare const account: RailgunAccount declare const walletClient: ReturnType const chainId = '11155111' as const; const WETH = '0xfFf9976782d46CC05630D92EE39e704800271F7D' as const; const value = 10000000000000n; // 0.00001 ETH const recipient = '0x1234567890123456789012345678901234567890' as `0x${string}`; const tx = await account.unshield(chainId, WETH, recipient, value); // [!code focus] // Submit the tx using Viem await walletClient.sendTransaction({ to: tx.to as `0x${string}`, data: tx.data as `0x${string}`, value: tx.value, gas: 6000000n }); ``` ::: ### Railgun Relayer The railgun system does have a relayer that can be used to submit the tx on your behalf. :::info TODO: Code snippet ::: ## Accounts :::info TODO: Write intro ::: ## Introduction to Privacy Pools \[A privacy preserving pool protocol] Privacy Pools is a privacy preserving pool protocol that allows users to shield and unshield their ERC20 tokens. This means any ERC20 (any ETH by extension through Wrapped ETH) can be shielded, internally transfered, and unshielded. You can read more about the protocol [here](https://privacypools.com). ### Installation Install [@kohaku-eth/privacy-pools](https://www.npmjs.com/package/@kohaku-eth/privacy-pools) via your package manager of choice. :::code-group ```bash [npm] npm install @kohaku-eth/privacy-pools ``` ```bash [pnpm] pnpm install @kohaku-eth/privacy-pools ``` ```bash [yarn] yarn install @kohaku-eth/privacy-pools ``` ```bash [bun] bun install @kohaku-eth/privacy-pools ``` ::: ### Next Steps Now that you have privacy pools installed you can start using it to create a privacy pools account. ## Private Proof of Innocence :::info TODO ::: ## Shielding :::info TODO ::: ### Unshielding :::info TODO ::: ## Transacting :::info TODO :::