diff --git a/client-sdk/examples/end-to-end.mdx b/client-sdk/examples/end-to-end.mdx index 597866b..a8a3e68 100644 --- a/client-sdk/examples/end-to-end.mdx +++ b/client-sdk/examples/end-to-end.mdx @@ -1,9 +1,9 @@ --- title: End-to-End Example -description: "A complete encrypt → store → decrypt flow using @cofhe/sdk" +description: "A complete encrypt to store to decrypt flow using @cofhe/sdk" --- -This example demonstrates the full lifecycle of working with encrypted data: initialize the SDK, encrypt a value, send it to a contract, and decrypt the result — both for UI display and for on-chain verification. +This example demonstrates the full lifecycle of working with encrypted data: initialize the SDK, encrypt a value, send it to a contract, and decrypt the result, both for UI display and for onchain verification. ## The contract diff --git a/client-sdk/foundry-plugin/getting-started.mdx b/client-sdk/foundry-plugin/getting-started.mdx index 9186e1a..1c67a5b 100644 --- a/client-sdk/foundry-plugin/getting-started.mdx +++ b/client-sdk/foundry-plugin/getting-started.mdx @@ -3,7 +3,7 @@ title: Getting Started description: "Set up @cofhe/foundry-plugin for local FHE contract development and testing under Forge" --- -`@cofhe/foundry-plugin` is the Foundry counterpart to [`@cofhe/hardhat-plugin`](/client-sdk/hardhat-plugin/getting-started). It provides two abstract Solidity contracts — `CofheTest` (test base, deploys all CoFHE mocks) and `CofheClient` (per-account encrypt/decrypt/permit shim) — that let you exercise FHE contracts under `forge test` with **no JS SDK required**. +`@cofhe/foundry-plugin` is the Foundry counterpart to [`@cofhe/hardhat-plugin`](/client-sdk/hardhat-plugin/getting-started). It provides two abstract Solidity contracts, `CofheTest` (test base, deploys all CoFHE mocks) and `CofheClient` (per-account encrypt/decrypt/permit shim), that let you exercise FHE contracts under `forge test` with **no JS SDK required**. Want to skip the setup? Clone the [cofhe-foundry-starter](https://github.com/FhenixProtocol/cofhe-foundry-starter) template to get a pre-configured project ready to go. @@ -11,9 +11,9 @@ Want to skip the setup? Clone the [cofhe-foundry-starter](https://github.com/Fhe ## What the plugin provides -- **`CofheTest`** — abstract test base that inherits `forge-std/Test` and deploys the full CoFHE mock stack (`MockTaskManager`, `MockACL`, `MockZkVerifier`, `MockThresholdNetwork`). -- **`CofheClient`** — in-Solidity SDK shim. One client per "user" in your scenario; each client carries a private key and produces encrypted inputs and signed permits as if it were that user's frontend SDK. -- **Plaintext assertions** — `expectPlaintext(handle, value)` reads the on-chain plaintext from the mock task manager. Faster than `decryptForView` and needs no permit. +- **`CofheTest`**: abstract test base that inherits `forge-std/Test` and deploys the full CoFHE mock stack (`MockTaskManager`, `MockACL`, `MockZkVerifier`, `MockThresholdNetwork`). +- **`CofheClient`**: in-Solidity SDK shim. One client per "user" in your scenario; each client carries a private key and produces encrypted inputs and signed permits as if it were that user's frontend SDK. +- **Plaintext assertions**: `expectPlaintext(handle, value)` reads the onchain plaintext from the mock task manager. Faster than `decryptForView` and needs no permit. ## Prerequisites @@ -71,12 +71,12 @@ code_size_limit = 100000 # mocks exceed 24 KB ``` -- `code_size_limit = 100000` is required — the mock contracts exceed the EIP-170 24 KB ceiling. +- `code_size_limit = 100000` is required, the mock contracts exceed the EIP-170 24 KB ceiling. - `solc_version = "0.8.25"` matches the compiler used by `@fhenixprotocol/cofhe-contracts`. -`evm_version = "cancun"` is no longer required as of `@cofhe/mock-contracts@0.5.0` — `MockACL` was migrated off transient storage (`tstore`/`tload`) to block-number-based storage, and the pragma was lowered to `>=0.8.19`. Set it only if your own contracts need cancun-specific opcodes. +`evm_version = "cancun"` is no longer required as of `@cofhe/mock-contracts@0.5.0`. `MockACL` was migrated off transient storage (`tstore`/`tload`) to block-number-based storage, and the pragma was lowered to `>=0.8.19`. Set it only if your own contracts need cancun-specific opcodes. @@ -123,7 +123,7 @@ contract MyTest is CofheTest { ## Version pinning -The plugin and `@cofhe/mock-contracts` pin `@fhenixprotocol/cofhe-contracts` *exactly* (no caret). Keep the three CoFHE packages aligned — otherwise `npm install` may resolve `cofhe-contracts` to a newer version that the mocks don't implement, producing `MockTaskManager should be marked as abstract` at compile time. +The plugin and `@cofhe/mock-contracts` pin `@fhenixprotocol/cofhe-contracts` *exactly* (no caret). Keep the three CoFHE packages aligned, otherwise `npm install` may resolve `cofhe-contracts` to a newer version that the mocks don't implement, producing `MockTaskManager should be marked as abstract` at compile time. Known-aligned tuple as of writing: @@ -139,15 +139,15 @@ See the [Compatibility](/get-started/introduction/compatibility) page for the ca The mocks are the same `@cofhe/mock-contracts` package the [Hardhat plugin](/client-sdk/hardhat-plugin/mock-contracts) uses. Behaviorally: -- Plaintext lives on-chain in `MockTaskManager.mockStorage` (so `expectPlaintext` and `getPlaintext` work). +- Plaintext lives onchain in `MockTaskManager.mockStorage` (so `expectPlaintext` and `getPlaintext` work). - No real ZK proving; encrypted inputs are signed by `MockZkVerifierSigner`. -- Decryption is synchronous — `decryptForTx_withoutPermit` returns the result immediately. +- Decryption is synchronous. `decryptForTx_withoutPermit` returns the result immediately. - Mock signatures are accepted by the same `FHE.verifyDecryptResult` your contract uses on testnet. The same test code runs unchanged against real CoFHE on a deployed network. ## Next steps -- [CofheTest](/client-sdk/foundry-plugin/cofhe-test) — the test base contract: `deployMocks`, `expectPlaintext`, `getPlaintext`, log toggles. -- [CofheClient](/client-sdk/foundry-plugin/cofhe-client) — per-user shim: `createInEuintN`, `decryptForTx_withoutPermit`, `decryptForView`, permits. -- [Testing](/client-sdk/foundry-plugin/testing) — canonical test patterns and the migration mapping from the old `@cofhe/mock-contracts/foundry/CoFheTest.sol` API. +- [CofheTest](/client-sdk/foundry-plugin/cofhe-test): the test base contract: `deployMocks`, `expectPlaintext`, `getPlaintext`, log toggles. +- [CofheClient](/client-sdk/foundry-plugin/cofhe-client): per-user shim: `createInEuintN`, `decryptForTx_withoutPermit`, `decryptForView`, permits. +- [Testing](/client-sdk/foundry-plugin/testing): canonical test patterns and the migration mapping from the old `@cofhe/mock-contracts/foundry/CoFheTest.sol` API. diff --git a/client-sdk/guides/decrypt-to-tx.mdx b/client-sdk/guides/decrypt-to-tx.mdx index 75ba9f1..87390ee 100644 --- a/client-sdk/guides/decrypt-to-tx.mdx +++ b/client-sdk/guides/decrypt-to-tx.mdx @@ -1,9 +1,9 @@ --- title: Decrypt to Transact -description: "Decrypt with a verifiable Threshold Network signature for on-chain use" +description: "Decrypt with a verifiable Threshold Network signature for onchain use" --- -Use `decryptForTx` to reveal a confidential (encrypted) value on-chain: it returns the plaintext together with a Threshold Network signature, so a contract can verify the reveal when you publish it in a transaction. +Use `decryptForTx` to reveal a confidential (encrypted) value onchain: it returns the plaintext together with a Threshold Network signature, so a contract can verify the reveal when you publish it in a transaction. Common use cases: @@ -11,13 +11,13 @@ Common use cases: - **Finalize a private auction / game move**: bids or moves are submitted encrypted, and the winner is revealed later in a verifiable way. -If you only need to show plaintext in your UI (and you do **not** need an on-chain-verifiable signature), use [`decryptForView`](/client-sdk/guides/decrypt-to-view) instead. +If you only need to show plaintext in your UI (and you do **not** need an onchain-verifiable signature), use [`decryptForView`](/client-sdk/guides/decrypt-to-view) instead. ## Prerequisites 1. [Create and connect a client](/client-sdk/guides/client-setup). -2. Know the on-chain encrypted handle (`ctHash`) you want to decrypt. +2. Know the onchain encrypted handle (`ctHash`) you want to decrypt. 3. Determine whether the contract's ACL policy for this `ctHash` requires a [permit](/client-sdk/guides/permits). @@ -30,7 +30,7 @@ Often, `decryptForTx` is used to reveal a value that the protocol already consid Examples where a permit is **not** needed: - **Unshielding**: the amount being unshielded is no longer meant to stay secret. -- **Auction/game reveal**: it doesn't matter who submits the reveal — only that the result is verified. +- **Auction/game reveal**: it doesn't matter who submits the reveal, only that the result is verified. If the ACL policy restricts decryption, you must use `.withPermit(...)`. @@ -38,9 +38,9 @@ If the ACL policy restricts decryption, you must use `.withPermit(...)`. `.execute()` resolves to an object with: -- `ctHash: bigint | string` — the ciphertext handle you decrypted -- `decryptedValue: bigint` — the plaintext value (always a `bigint`) -- `signature: 0x${string}` — the Threshold Network signature as a hex string +- `ctHash: bigint | string`: the ciphertext handle you decrypted +- `decryptedValue: bigint`: the plaintext value (always a `bigint`) +- `signature: 0x${string}`: the Threshold Network signature as a hex string ## Decrypt (choose permit mode) @@ -74,33 +74,33 @@ const decryptResult = await client -After decrypting, see [Writing Decrypt Result to Contract](/client-sdk/guides/writing-decrypt-result) for how to publish or verify the result on-chain. +After decrypting, see [Writing Decrypt Result to Contract](/client-sdk/guides/writing-decrypt-result) for how to publish or verify the result onchain. ## Builder API -### `.execute()` — required, call last +### `.execute()` (required, call last) Runs the decryption and returns `{ ctHash, decryptedValue, signature }`. -### `.withPermit(...)` — required unless using `.withoutPermit()` +### `.withPermit(...)` (required unless using `.withoutPermit()`) -- `.withPermit()` — uses the active permit -- `.withPermit(permitHash)` — fetches a stored permit by hash -- `.withPermit(permit)` — uses the provided permit object +- `.withPermit()`: uses the active permit +- `.withPermit(permitHash)`: fetches a stored permit by hash +- `.withPermit(permit)`: uses the provided permit object -### `.withoutPermit()` — required unless using `.withPermit(...)` +### `.withoutPermit()` (required unless using `.withPermit(...)`) Decrypt via global allowance (no permit). Only works if the contract's ACL policy allows anyone to decrypt that `ctHash`. -### `.setAccount(address)` — optional +### `.setAccount(address)` (optional) Overrides the account used to resolve the active/stored permit. -### `.setChainId(chainId)` — optional +### `.setChainId(chainId)` (optional) Overrides the chain used to resolve the Threshold Network URL and permits. -### `.onPoll(callback)` — optional +### `.onPoll(callback)` (optional) Register a callback that fires once per poll attempt while `decryptForTx` waits for the Threshold Network to return the plaintext. Useful for surfacing progress in a UI. @@ -119,13 +119,13 @@ The callback receives: | Field | Type | Description | | --- | --- | --- | | `operation` | `'decrypt' \| 'sealoutput'` | Which Threshold Network flow is polling. For `decryptForTx` this is always `'decrypt'`. | -| `requestId` | `string` | The Threshold Network request id. **May be the empty string** during submit-time retries — see `.set404RetryTimeout(...)` below. | +| `requestId` | `string` | The Threshold Network request id. **May be the empty string** during submit-time retries, see `.set404RetryTimeout(...)` below. | | `attemptIndex` | `number` | Zero-based poll attempt counter. | | `elapsedMs` | `number` | Time since the first submit attempt. | | `intervalMs` | `number` | Delay until the next poll. | | `timeoutMs` | `number` | Overall budget shared by submit-retries and status-polling. | -### `.set404RetryTimeout(timeoutMs)` — optional +### `.set404RetryTimeout(timeoutMs)` (optional) Configures how long `decryptForTx` keeps retrying when the Threshold Network's submit endpoint responds with `404 Not Found` before a `requestId` is available. This typically happens on slower backends where the ciphertext isn't visible yet at submit time. Defaults to `10_000` ms. diff --git a/client-sdk/guides/decrypt-to-view.mdx b/client-sdk/guides/decrypt-to-view.mdx index 98cb4f7..97097cf 100644 --- a/client-sdk/guides/decrypt-to-view.mdx +++ b/client-sdk/guides/decrypt-to-view.mdx @@ -5,7 +5,7 @@ description: "Reveal encrypted values locally for UI display using permits" Use `decryptForView` to reveal a confidential (encrypted) value locally in your app so you can display it in the UI. -Unlike [`decryptForTx`](/client-sdk/guides/decrypt-to-tx), this flow does **not** return an on-chain-verifiable signature, and it is **not** meant to be published on-chain. +Unlike [`decryptForTx`](/client-sdk/guides/decrypt-to-tx), this flow does **not** return an onchain-verifiable signature, and it is **not** meant to be published onchain. ## Flow @@ -14,7 +14,7 @@ Unlike [`decryptForTx`](/client-sdk/guides/decrypt-to-tx), this flow does **not* 3. Call `decryptForView(ctHash, utype).execute()` to get the plaintext. -`decryptForView` always decrypts using a permit (there is no `.withoutPermit()` mode). If your protocol intends for the plaintext to become publicly visible on-chain, use [`decryptForTx`](/client-sdk/guides/decrypt-to-tx) instead. +`decryptForView` always decrypts using a permit (there is no `.withoutPermit()` mode). If your protocol intends for the plaintext to become publicly visible onchain, use [`decryptForTx`](/client-sdk/guides/decrypt-to-tx) instead. ## Prerequisites @@ -31,9 +31,9 @@ Unlike [`decryptForTx`](/client-sdk/guides/decrypt-to-tx), this flow does **not* **Providing `utype`**: `utype` must match the ciphertext's underlying FHE type. The SDK uses it to convert the decrypted `bigint` into a convenient JS type. Supported `utype`s: -- `FheTypes.Bool` → returns a `boolean` -- `FheTypes.Uint160` (address) → returns a checksummed `0x...` string -- `FheTypes.Uint8 | Uint16 | Uint32 | Uint64 | Uint128` → returns a `bigint` +- `FheTypes.Bool` to returns a `boolean` +- `FheTypes.Uint160` (address) to returns a checksummed `0x...` string +- `FheTypes.Uint8 | Uint16 | Uint32 | Uint64 | Uint128` to returns a `bigint` ## Permit setup @@ -90,29 +90,29 @@ Running `.execute()` resolves to a scalar JS value: ## Builder API -### `.execute()` — required, call last +### `.execute()` (required, call last) Runs the decryption and returns a UI-friendly scalar value. -### `.withPermit(...)` — optional +### `.withPermit(...)` (optional) Select which permit to use: -- `.withPermit()` — uses the active permit -- `.withPermit(permitHash)` — fetches a stored permit by hash -- `.withPermit(permit)` — uses the provided permit object +- `.withPermit()`: uses the active permit +- `.withPermit(permitHash)`: fetches a stored permit by hash +- `.withPermit(permit)`: uses the provided permit object If you don't call `.withPermit(...)`, the active permit is used by default. -### `.setAccount(address)` — optional +### `.setAccount(address)` (optional) Overrides the account used to resolve the active/stored permit. -### `.setChainId(chainId)` — optional +### `.setChainId(chainId)` (optional) Overrides the chain used to resolve the Threshold Network URL and permits. -### `.onPoll(callback)` — optional +### `.onPoll(callback)` (optional) Register a callback that fires once per poll attempt while `decryptForView` waits for the Threshold Network to return the sealed plaintext. Useful for surfacing decrypt progress in a UI. @@ -130,13 +130,13 @@ The callback receives: | Field | Type | Description | | --- | --- | --- | | `operation` | `'decrypt' \| 'sealoutput'` | Which Threshold Network flow is polling. For `decryptForView` this is `'sealoutput'`. | -| `requestId` | `string` | The Threshold Network request id. **May be the empty string** during submit-time retries — see `.set404RetryTimeout(...)` below. | +| `requestId` | `string` | The Threshold Network request id. **May be the empty string** during submit-time retries, see `.set404RetryTimeout(...)` below. | | `attemptIndex` | `number` | Zero-based poll attempt counter. | | `elapsedMs` | `number` | Time since the first submit attempt. | | `intervalMs` | `number` | Delay until the next poll. | | `timeoutMs` | `number` | Overall budget shared by submit-retries and status-polling. | -### `.set404RetryTimeout(timeoutMs)` — optional +### `.set404RetryTimeout(timeoutMs)` (optional) Configures how long `decryptForView` keeps retrying when the Threshold Network's submit endpoint responds with `404 Not Found` before a `requestId` is available. This typically happens on slower backends where the ciphertext isn't visible yet at submit time. Defaults to `10_000` ms. diff --git a/client-sdk/guides/encrypting-inputs.mdx b/client-sdk/guides/encrypting-inputs.mdx index d34c3c6..a69271c 100644 --- a/client-sdk/guides/encrypting-inputs.mdx +++ b/client-sdk/guides/encrypting-inputs.mdx @@ -3,12 +3,12 @@ title: Encrypting Inputs description: "Encrypt plaintext values with ZK proofs for use in FHE-enabled smart contracts" --- -`encryptInputs` encrypts plaintext values into FHE ciphertexts that can be passed as inputs to a confidential smart contract transaction. Values must be encrypted before being passed on-chain to preserve confidentiality. +`encryptInputs` encrypts plaintext values into FHE ciphertexts that can be passed as inputs to a confidential smart contract transaction. Values must be encrypted before being passed onchain to preserve confidentiality. ## Prerequisites 1. [Create and connect a client](/client-sdk/guides/client-setup). -2. Know which encrypted type you want to encode each value as — the type must match the Solidity parameter type your contract expects (e.g. `InEuint32` vs `InEuint64`). +2. Know which encrypted type you want to encode each value as, the type must match the Solidity parameter type your contract expects (e.g. `InEuint32` vs `InEuint64`). ## Basic usage @@ -28,11 +28,11 @@ const encrypted = await cofheClient const [eUint32, eBool, eAddress] = encrypted; ``` -The return type is a typed tuple that mirrors the array you pass in — each element is the corresponding `EncryptedItemInput` type. +The return type is a typed tuple that mirrors the array you pass in, each element is the corresponding `EncryptedItemInput` type. ## Using encrypted inputs in a transaction -Pass the returned `EncryptedItemInput` objects directly into your contract call. The on-chain CoFHE library verifies the signature before using the ciphertext. +Pass the returned `EncryptedItemInput` objects directly into your contract call. The onchain CoFHE library verifies the signature before using the ciphertext. @@ -54,7 +54,7 @@ await contract.confidentialTransfer(recipient, encryptedAmount); ## Builder API -### `.execute()` — required, call last +### `.execute()` (required, call last) Runs the encryption pipeline and returns the `EncryptedItemInput[]` tuple. @@ -64,9 +64,9 @@ const [encryptedAge, encryptedFlag] = await cofheClient .execute(); ``` -### `.setAccount(address)` — optional +### `.setAccount(address)` (optional) -Override the address that "owns" the encrypted input. Only that address will be allowed to use the encrypted inputs on-chain. Defaults to the connected wallet account. +Override the address that "owns" the encrypted input. Only that address will be allowed to use the encrypted inputs onchain. Defaults to the connected wallet account. ```typescript const encrypted = await cofheClient @@ -75,7 +75,7 @@ const encrypted = await cofheClient .execute(); ``` -### `.setChainId(chainId)` — optional +### `.setChainId(chainId)` (optional) Override the chain the encrypted input will be used on. Defaults to the connected chain. @@ -86,7 +86,7 @@ const encrypted = await cofheClient .execute(); ``` -### `.setUseWorker(boolean)` — optional +### `.setUseWorker(boolean)` (optional) Override the `useWorkers` flag for this specific call. When `true` (the default), ZK proof generation runs in a Web Worker to avoid blocking the main thread. No-op in Node.js. @@ -97,7 +97,7 @@ const encrypted = await cofheClient .execute(); ``` -### `.onStep(callback)` — optional +### `.onStep(callback)` (optional) Register a callback that fires at the start and end of each encryption step. Useful for building progress indicators. @@ -125,7 +125,7 @@ Calling `.execute()` runs five sequential steps: | `Prove` | Generates the ZK proof of knowledge (ZKPoK). Uses a Web Worker when available. | | `Verify` | Sends the proof to the CoFHE verifier. Returns signed `EncryptedItemInput` objects. | -## Encryptable — creating inputs +## Encryptable: creating inputs Use the `Encryptable` factory to create the items you want to encrypt. Each factory function accepts the plaintext value and an optional `securityZone`. @@ -151,7 +151,7 @@ Encryptable.create('address', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'); **Bit limit:** A single `encryptInputs` call may encrypt at most **2048 bits** of plaintext in total. Exceeding this limit throws a `ZkPackFailed` error. -## EncryptedItemInput — the result type +## EncryptedItemInput: the result type Each element of the returned array is an `EncryptedItemInput`: @@ -164,7 +164,7 @@ type EncryptedItemInput = { }; ``` -Pass these directly into a contract function that accepts `InEuint*` structs. The contract's CoFHE library validates the signature on-chain before operating on the ciphertext. +Pass these directly into a contract function that accepts `InEuint*` structs. The contract's CoFHE library validates the signature onchain before operating on the ciphertext. ## Common pitfalls diff --git a/client-sdk/guides/permits.mdx b/client-sdk/guides/permits.mdx index c926f7c..e13e627 100644 --- a/client-sdk/guides/permits.mdx +++ b/client-sdk/guides/permits.mdx @@ -3,7 +3,7 @@ title: Permits description: "Create and manage EIP-712 permits for decryption authorization" --- -Permits are EIP-712 signatures that authorize decryption of confidential data. The `issuer` field identifies who is accessing the data — the issuer must have been granted access on-chain via `FHE.allow(handle, address)`. When a permit is used, CoFHE validates it against the ACL contract to confirm that the issuer has access to the requested encrypted handle. +Permits are EIP-712 signatures that authorize decryption of confidential data. The `issuer` field identifies who is accessing the data, the issuer must have been granted access onchain via `FHE.allow(handle, address)`. When a permit is used, CoFHE validates it against the ACL contract to confirm that the issuer has access to the requested encrypted handle. Each permit includes a sealing keypair. The public key is sent to CoFHE so it can re-encrypt the data for the permit holder. The private key stays client-side and is used to unseal the returned data. @@ -21,7 +21,7 @@ Each permit includes a sealing keypair. The public key is sent to CoFHE so it ca ## Quick start -The examples below show two approaches. The `client.permits` API is the recommended approach — it automatically signs permits with the connected wallet and manages the permit store. The `PermitUtils` API is a lower-level alternative that gives you direct control over signing and storage. +The examples below show two approaches. The `client.permits` API is the recommended approach, it automatically signs permits with the connected wallet and manages the permit store. The `PermitUtils` API is a lower-level alternative that gives you direct control over signing and storage. @@ -177,7 +177,7 @@ The exported JSON does not contain any sensitive data and can be shared via any -Do not share `serialize(permit)` output — serialization is meant for local persistence and includes the sealing private key. +Do not share `serialize(permit)` output. Serialization is meant for local persistence and includes the sealing private key. @@ -251,9 +251,9 @@ client.permits.removeActivePermit(); ## Validating permits -Since `@cofhe/sdk@0.5.0`, `PermitUtils.validate` enforces the **full** check: schema + signed + not-expired. The decrypt flows (`decryptForView`, `decryptForTx` with `.withPermit(...)`) call this for you and surface failures as typed errors — you only need to validate manually when you want to inspect or filter permits before using them. +Since `@cofhe/sdk@0.5.0`, `PermitUtils.validate` enforces the **full** check: schema + signed + not-expired. The decrypt flows (`decryptForView`, `decryptForTx` with `.withPermit(...)`) call this for you and surface failures as typed errors, you only need to validate manually when you want to inspect or filter permits before using them. -### Throwing helpers — `PermitUtils.*` +### Throwing helpers: `PermitUtils.*` | Function | What it checks | Behavior on failure | | --- | --- | --- | @@ -273,7 +273,7 @@ try { } ``` -### Non-throwing helpers — `ValidationUtils.*` +### Non-throwing helpers: `ValidationUtils.*` For inspection without exception handling, use the `ValidationUtils` helpers. They return a typed `ValidationResult`: diff --git a/client-sdk/guides/writing-decrypt-result.mdx b/client-sdk/guides/writing-decrypt-result.mdx index da63864..a956ef1 100644 --- a/client-sdk/guides/writing-decrypt-result.mdx +++ b/client-sdk/guides/writing-decrypt-result.mdx @@ -1,9 +1,9 @@ --- title: Writing Decrypt Result to Contract -description: "Submit a decryptForTx result on-chain for verification or publishing" +description: "Submit a decryptForTx result onchain for verification or publishing" --- -This page covers the "decrypt → write tx" flow after you run [`decryptForTx`](/client-sdk/guides/decrypt-to-tx): you take `{ ctHash, decryptedValue, signature }` and submit a transaction that your contract can verify. +This page covers the "decrypt to write tx" flow after you run [`decryptForTx`](/client-sdk/guides/decrypt-to-tx): you take `{ ctHash, decryptedValue, signature }` and submit a transaction that your contract can verify. There are two common patterns: @@ -19,9 +19,9 @@ There are two common patterns: `decryptedValue` is a `bigint`. If your Solidity function expects a smaller integer type (e.g. `uint32`), make sure the value is within range. -## Publish the decrypt result on-chain +## Publish the decrypt result onchain -The intended consumer of `decryptForTx` is an on-chain verifier such as `FHE.publishDecryptResult(...)`. In practice, you publish the result by calling a function on **your contract** that invokes `FHE.publishDecryptResult` internally. +The intended consumer of `decryptForTx` is an onchain verifier such as `FHE.publishDecryptResult(...)`. In practice, you publish the result by calling a function on **your contract** that invokes `FHE.publishDecryptResult` internally. @@ -53,7 +53,7 @@ await tx.wait(); ## Verify a decrypt result signature (without publishing) -Some protocols don't need (or don't want) to publish the decrypt result globally — they only need to verify that the provided plaintext and signature match a specific handle (`ctHash`). +Some protocols don't need (or don't want) to publish the decrypt result globally, they only need to verify that the provided plaintext and signature match a specific handle (`ctHash`). For example, an "unshield" flow can accept `(ctHash, plaintext, signature)` and only proceed if the signature is valid: diff --git a/client-sdk/guides/writing-encrypted-data.mdx b/client-sdk/guides/writing-encrypted-data.mdx index dfd5803..2c496f1 100644 --- a/client-sdk/guides/writing-encrypted-data.mdx +++ b/client-sdk/guides/writing-encrypted-data.mdx @@ -3,9 +3,9 @@ title: Writing Encrypted Data to Contract description: "Encrypt plaintext values and pass them directly into a contract call" --- -This page covers the "encrypt → write tx" flow: encrypt plaintext values into `InE*` structs and pass them directly into a contract call. +This page covers the "encrypt to write tx" flow: encrypt plaintext values into `InE*` structs and pass them directly into a contract call. -`encryptInputs` returns `EncryptedItemInput` objects that match the Solidity `InE*` input structs. The on-chain CoFHE library validates the verifier signature before the contract can use the ciphertext. +`encryptInputs` returns `EncryptedItemInput` objects that match the Solidity `InE*` input structs. The onchain CoFHE library validates the verifier signature before the contract can use the ciphertext. ## Flow @@ -20,9 +20,9 @@ This page covers the "encrypt → write tx" flow: encrypt plaintext values into The encrypted type you choose in TypeScript must match the Solidity parameter type: -- `Encryptable.uint32(...)` → `InEuint32` -- `Encryptable.bool(...)` → `InEbool` -- `Encryptable.address(...)` → `InEaddress` +- `Encryptable.uint32(...)` to `InEuint32` +- `Encryptable.bool(...)` to `InEbool` +- `Encryptable.address(...)` to `InEaddress` ## Example: encrypt and call a contract diff --git a/client-sdk/hardhat-plugin/mock-contracts.mdx b/client-sdk/hardhat-plugin/mock-contracts.mdx index ad067a1..db64735 100644 --- a/client-sdk/hardhat-plugin/mock-contracts.mdx +++ b/client-sdk/hardhat-plugin/mock-contracts.mdx @@ -3,19 +3,19 @@ title: Mock Contracts description: "Interact with mock CoFHE contracts and read plaintext values in tests" --- -The plugin deploys a suite of mock contracts that simulate the full CoFHE coprocessor stack on the Hardhat network. This lets you develop and test FHE contracts without running the off-chain FHE engine. +The plugin deploys a suite of mock contracts that simulate the full CoFHE coprocessor stack on the Hardhat network. This lets you develop and test FHE contracts without running the offchain FHE engine. ## What the mocks simulate | Contract | Role | | --- | --- | -| `MockTaskManager` | Manages FHE operations; stores plaintext values on-chain for testing | +| `MockTaskManager` | Manages FHE operations; stores plaintext values onchain for testing | | `MockACL` | Access control for encrypted handles | | `MockZkVerifier` | Simulates ZK proof verification for encrypted inputs | | `MockThresholdNetwork` | Handles decryption requests | -| `TestBed` | Helper contract for testing — exposes trivial value setters and a `numberHash` getter | +| `TestBed` | Helper contract for testing. Exposes trivial value setters and a `numberHash` getter | -The SDK automatically detects when it's running against the mock environment (by checking bytecode at the `MockZkVerifier` fixed address) and adapts its behavior accordingly — ZK proof generation is skipped and verification is handled by the mock contracts. +The SDK automatically detects when it's running against the mock environment (by checking bytecode at the `MockZkVerifier` fixed address) and adapts its behavior accordingly. ZK proof generation is skipped and verification is handled by the mock contracts. ## Auto-deployment @@ -63,7 +63,7 @@ const testBed = await hre.cofhe.mocks.getTestBed(); ## Reading plaintext values -Because `MockTaskManager` stores plaintext values on-chain, you can read the underlying plaintext of any encrypted handle directly in tests — no permit needed. +Because `MockTaskManager` stores plaintext values onchain, you can read the underlying plaintext of any encrypted handle directly in tests, no permit needed. ### `getPlaintext(ctHash)` @@ -84,7 +84,7 @@ expect(plaintext).to.equal(7n); ### `expectPlaintext(ctHash, expectedValue)` -Assertion shorthand — wraps `getPlaintext` with a Chai `expect`: +Assertion shorthand. Wraps `getPlaintext` with a Chai `expect`: ```typescript import hre from 'hardhat'; diff --git a/client-sdk/hardhat-plugin/testing.mdx b/client-sdk/hardhat-plugin/testing.mdx index 55c13eb..9ffd6e6 100644 --- a/client-sdk/hardhat-plugin/testing.mdx +++ b/client-sdk/hardhat-plugin/testing.mdx @@ -7,7 +7,7 @@ This page shows the common patterns for writing Hardhat tests with the CoFHE plu ## Test setup -Use `hre.cofhe.createClientWithBatteries` in a `before` hook. It creates and connects a fully configured `CofheClient` — including a self-permit — so the client is ready for every test in the suite: +Use `hre.cofhe.createClientWithBatteries` in a `before` hook. It creates and connects a fully configured `CofheClient`, including a self-permit, so the client is ready for every test in the suite: ```typescript import hre from 'hardhat'; @@ -25,7 +25,7 @@ before(async () => { See [Client](/client-sdk/hardhat-plugin/client) for manual setup options. -## Encrypt → store → decrypt +## Encrypt to store to decrypt The core test loop: encrypt a value, pass it to a contract, then decrypt the stored handle. @@ -98,7 +98,7 @@ const aliceClient = await hre.cofhe.createClientWithBatteries(alice); ## `decryptForTx` patterns -[`decryptForTx`](/client-sdk/guides/decrypt-to-tx) returns a `{ ctHash, decryptedValue, signature }` tuple for on-chain submission. The permit mode must be selected explicitly. +[`decryptForTx`](/client-sdk/guides/decrypt-to-tx) returns a `{ ctHash, decryptedValue, signature }` tuple for onchain submission. The permit mode must be selected explicitly. ### Globally allowed values (`.withoutPermit()`) @@ -142,7 +142,7 @@ expect(result.decryptedValue).to.equal(99n); -### Submitting the result on-chain +### Submitting the result onchain Pass the result directly to your contract: diff --git a/client-sdk/introduction/mental-model.mdx b/client-sdk/introduction/mental-model.mdx index 64410cb..1623165 100644 --- a/client-sdk/introduction/mental-model.mdx +++ b/client-sdk/introduction/mental-model.mdx @@ -3,17 +3,17 @@ title: Mental Model description: "Understand how data flows through FHE-enabled dApps using the @cofhe/sdk" --- -To understand how `@cofhe/sdk` fits into the Fhenix framework, you'll explore a simple mental model using a Counter smart contract example. This will show you how data flows through FHE-enabled dApps—from encryption to computation to decryption. +To understand how `@cofhe/sdk` fits into the Fhenix framework, you'll explore a simple mental model using a Counter smart contract example. This will show you how data flows through FHE-enabled dApps, from encryption to computation to decryption. ## The Counter Example -Imagine a smart contract called **Counter** where each user has their own private counter. Users can increment their counter and read its value with complete privacy—no one, including the smart contract itself, can see the actual counter values. +Imagine a smart contract called **Counter** where each user has their own private counter. Users can increment their counter and read its value with complete privacy, no one, including the smart contract itself, can see the actual counter values. ### Key Concepts - **Public Key** = A lock that anyone can use to seal data - **Private Key** = The unique key to unlock sealed data -- **CoFHE Co-Processor** = Fhenix's off-chain service that handles FHE operations +- **CoFHE Co-Processor** = Fhenix's offchain service that handles FHE operations - **Ciphertext** = Encrypted data that can be computed on without decryption @@ -26,7 +26,7 @@ When a user wants to add `5` to their counter, the data must first be encrypted 1. The user's plaintext value `5` is encrypted using `client.encryptInputs([Encryptable.uint32(5n)]).execute()` 2. The SDK generates a ZK proof and submits the encrypted value to the CoFHE verifier -3. The returned `EncryptedItemInput` is sent to the smart contract on-chain +3. The returned `EncryptedItemInput` is sent to the smart contract onchain 4. The blockchain sees only encrypted data, never the actual value `5` ### The "Locked Box" Analogy @@ -64,16 +64,16 @@ When a user wants to read their counter value, they use one of the SDK's two dec **For UI display (`decryptForView`):** 1. The user reads the encrypted handle (`ctHash`) from the contract -2. A permit authorizes decryption — created via `client.permits.getOrCreateSelfPermit()` +2. A permit authorizes decryption. Created via `client.permits.getOrCreateSelfPermit()` 3. The SDK requests re-encryption from the Threshold Network using the permit's sealing key 4. The plaintext is returned locally for display -**For on-chain use (`decryptForTx`):** +**For onchain use (`decryptForTx`):** 1. The user reads the encrypted handle (`ctHash`) from the contract 2. The SDK requests decryption from the Threshold Network 3. The plaintext and a verifiable signature are returned -4. The signature can be verified on-chain via `FHE.verifyDecryptResult(...)` +4. The signature can be verified onchain via `FHE.verifyDecryptResult(...)` ### The "Lock Exchange" Analogy @@ -129,9 +129,9 @@ sequenceDiagram ## Key Takeaways 1. **Encryption happens client-side**: The SDK encrypts data with ZK proofs before it reaches the blockchain -2. **Computation happens on-chain**: Smart contracts perform operations on encrypted data via `FHE.sol` +2. **Computation happens onchain**: Smart contracts perform operations on encrypted data via `FHE.sol` 3. **FHE enables privacy-preserving computation**: The blockchain never sees plaintext values 4. **Permits enable access control**: EIP-712 signed permits authorize who can decrypt specific data -5. **Two decryption paths**: `decryptForView` for UI display, `decryptForTx` for on-chain verification +5. **Two decryption paths**: `decryptForView` for UI display, `decryptForTx` for onchain verification -This architecture ensures that sensitive data remains private throughout its entire lifecycle—from input to computation to output—while still enabling powerful decentralized applications. +This architecture ensures that sensitive data remains private throughout its entire lifecycle, from input to computation to output, while still enabling decentralized applications. diff --git a/client-sdk/introduction/migrating-from-cofhejs.mdx b/client-sdk/introduction/migrating-from-cofhejs.mdx index 679fdf2..f79905f 100644 --- a/client-sdk/introduction/migrating-from-cofhejs.mdx +++ b/client-sdk/introduction/migrating-from-cofhejs.mdx @@ -5,22 +5,22 @@ description: "Side-by-side migration guide from cofhejs to @cofhe/sdk" `@cofhe/sdk` is the successor to `cofhejs`, redesigned around an explicit, builder-pattern API that gives you full control over encryption, decryption, and permit management. -### Why migrate? +## Why migrate? -- **Explicit API** — no more implicit initialization or auto-generated permits. Every action is opt-in. -- **Builder pattern** — `encryptInputs`, `decryptForView`, and `decryptForTx` use a chainable builder so you can set overrides (account, chain, callbacks) before calling `.execute()`. -- **`decryptForTx` feature** — `cofhejs` does not provide an API for generating decryption signatures for on-chain usage. -- **Deferred key loading** — FHE keys and TFHE WASM are fetched lazily on the first `encryptInputs` call, not during initialization. -- **Better multichain support** — configure multiple chains up front and override per-call. -- **Structured errors** — typed `CofheError` objects with error codes replace the `Result` wrapper. +- **Explicit API**: no more implicit initialization or auto-generated permits. Every action is opt-in. +- **Builder pattern**: `encryptInputs`, `decryptForView`, and `decryptForTx` use a chainable builder so you can set overrides (account, chain, callbacks) before calling `.execute()`. +- **`decryptForTx` feature**: `cofhejs` does not provide an API for generating decryption signatures for onchain usage. +- **Deferred key loading**: FHE keys and TFHE WASM are fetched lazily on the first `encryptInputs` call, not during initialization. +- **Better multichain support**: configure multiple chains up front and override per-call. +- **Structured errors**: typed `CofheError` objects with error codes replace the `Result` wrapper. -### Requirements +## Requirements - Node.js 18+ - TypeScript 5+ - Viem 2+ -### Installation +## Installation Remove `cofhejs` and install `@cofhe/sdk`: @@ -30,13 +30,13 @@ npm uninstall cofhejs && npm install @cofhe/sdk ## 1. Initialization -The single `cofhejs.initializeWithEthers(...)` / `cofhejs.initializeWithViem(...)` call is replaced by a three-step flow: create a config, create a client, then connect. FHE keys and WASM are no longer fetched eagerly during init — they are deferred until the first `encryptInputs` call. +The single `cofhejs.initializeWithEthers(...)` / `cofhejs.initializeWithViem(...)` call is replaced by a three-step flow: create a config, create a client, then connect. FHE keys and WASM are no longer fetched eagerly during init, they are deferred until the first `encryptInputs` call. | | `cofhejs` | `@cofhe/sdk` | | --- | --- | --- | -| **Entry** | `cofhejs.initializeWithEthers(...)` / `cofhejs.initializeWithViem(...)` | `createCofheConfig(...)` → `createCofheClient(config)` → `client.connect(...)` | +| **Entry** | `cofhejs.initializeWithEthers(...)` / `cofhejs.initializeWithViem(...)` | `createCofheConfig(...)` to `createCofheClient(config)` to `client.connect(...)` | | **Key fetching** | Immediate (during init) | Deferred (first `encryptInputs` call) | | **WASM init** | Immediate (during init) | Deferred (first `encryptInputs` call) | | **Environment** | `"LOCAL"` / `"TESTNET"` / `"MAINNET"` string | Chain objects via `supportedChains: [chains.sepolia]` | @@ -171,17 +171,17 @@ The `Encryptable` factory functions (`Encryptable.uint32(...)`, `Encryptable.boo `cofhejs` has a single `unseal` function. `@cofhe/sdk` splits decryption into two purpose-built methods: -- **`decryptForView`** — returns the plaintext for UI display (no on-chain signature). -- **`decryptForTx`** — returns the plaintext **and** a Threshold Network signature for on-chain verification. +- **`decryptForView`**: returns the plaintext for UI display (no onchain signature). +- **`decryptForTx`**: returns the plaintext **and** a Threshold Network signature for onchain verification. | | `cofhejs` | `@cofhe/sdk` | | --- | --- | --- | | **Function** | `cofhejs.unseal(sealed, type)` | `client.decryptForView(ctHash, type)` or `client.decryptForTx(ctHash)` | -| **Permit handling** | Automatic (uses most recent permit) | Explicit — `.withPermit()` / `.withoutPermit()` | +| **Permit handling** | Automatic (uses most recent permit) | Explicit. `.withPermit()` / `.withoutPermit()` | | **Return value** | `Result` | Direct value for view; `{ ctHash, decryptedValue, signature }` for tx | -| **On-chain verification** | Not built in | `decryptForTx` returns a signature for `FHE.publishDecryptResult(...)` | +| **Onchain verification** | Not built in | `decryptForTx` returns a signature for `FHE.publishDecryptResult(...)` | @@ -201,7 +201,7 @@ if (!result.success) { console.log(result.data); // bigint ``` -### After (@cofhe/sdk) — viewing in UI +### After (@cofhe/sdk): viewing in UI ```typescript import { FheTypes } from '@cofhe/sdk'; @@ -213,7 +213,7 @@ const balance = await client .execute(); ``` -### After (@cofhe/sdk) — publishing on-chain +### After (@cofhe/sdk): publishing onchain @@ -254,7 +254,7 @@ Permits are no longer auto-generated during initialization. All permit operation | | `cofhejs` | `@cofhe/sdk` | | --- | --- | --- | -| **Auto-generation** | `generatePermit: true` (default) | Never — always explicit | +| **Auto-generation** | `generatePermit: true` (default) | Never, always explicit | | **Creation** | `cofhejs.createPermit({ type, issuer })` | `client.permits.createSelf(...)`, `client.permits.createSharing(...)` | | **Return type** | `Result` | Direct `Permit` object | | **Active permit** | Implicitly used by `unseal` | `getOrCreateSelfPermit()` sets active; used automatically by decrypt methods | diff --git a/client-sdk/introduction/overview.mdx b/client-sdk/introduction/overview.mdx index dbcef35..e943f45 100644 --- a/client-sdk/introduction/overview.mdx +++ b/client-sdk/introduction/overview.mdx @@ -5,7 +5,7 @@ description: "Introduction to @cofhe/sdk - the TypeScript client SDK for buildin `@cofhe/sdk` is the TypeScript client SDK for [CoFHE](/deep-dive/cofhe-components/overview). It handles the client-side operations required to interact with FHE-enabled smart contracts: encrypting inputs with ZK proofs, decrypting ciphertext handles via the Threshold Network, and managing EIP-712 permits for access control. -On-chain, contracts use [`FHE.sol`](/fhe-library/introduction/overview) to operate on encrypted data. Off-chain, this SDK prepares the inputs and reads the outputs. +Onchain, contracts use [`FHE.sol`](/fhe-library/introduction/overview) to operate on encrypted data. Offchain, this SDK prepares the inputs and reads the outputs. ## What the SDK does @@ -16,11 +16,11 @@ On-chain, contracts use [`FHE.sol`](/fhe-library/introduction/overview) to opera - Requests decryption of a ciphertext handle via the Threshold Network using a permit. Returns the plaintext locally, not published on-chain. + Requests decryption of a ciphertext handle via the Threshold Network using a permit. Returns the plaintext locally, not published onchain. - Requests decryption and returns the plaintext with a Threshold Network signature for on-chain verification. + Requests decryption and returns the plaintext with a Threshold Network signature for onchain verification. @@ -58,9 +58,9 @@ import { Ethers6Adapter } from '@cofhe/sdk/adapters'; ## Client lifecycle -The SDK follows a three-step lifecycle: **config → client → connect**. +The SDK follows a three-step lifecycle: **config to client to connect**. -``` +```text createCofheConfig({ supportedChains }) → createCofheClient(config) → client.connect(publicClient, walletClient) ``` @@ -94,7 +94,7 @@ const balance = await client - Install the SDK, write a contract, and run your first encrypt → store → decrypt test in minutes. + Install the SDK, write a contract, and run your first encrypt to store to decrypt test in minutes. @@ -110,7 +110,7 @@ const balance = await client - Decrypt with a verifiable Threshold Network signature for on-chain use. + Decrypt with a verifiable Threshold Network signature for onchain use. diff --git a/client-sdk/quick-start/foundry.mdx b/client-sdk/quick-start/foundry.mdx index d5307a9..da31615 100644 --- a/client-sdk/quick-start/foundry.mdx +++ b/client-sdk/quick-start/foundry.mdx @@ -1,9 +1,9 @@ --- title: "Quick Start: Foundry" -description: "Set up a Foundry project with @cofhe/foundry-plugin, write a contract, and test the encrypt → store → decrypt flow" +description: "Set up a Foundry project with @cofhe/foundry-plugin, write a contract, and test the encrypt to store to decrypt flow" --- -Set up a Foundry project with `@cofhe/foundry-plugin`, write a contract that stores an encrypted `uint32`, and test the encrypt → store → decrypt flow under `forge test`. +Set up a Foundry project with `@cofhe/foundry-plugin`, write a contract that stores an encrypted `uint32`, and test the encrypt to store to decrypt flow under `forge test`. Want to skip the setup? Clone the [cofhe-foundry-starter](https://github.com/FhenixProtocol/cofhe-foundry-starter) template to get a pre-configured project with everything ready to go. @@ -52,11 +52,11 @@ code_size_limit = 100000 ``` -`code_size_limit = 100000` is required — the mock contracts exceed the EIP-170 24 KB limit. +`code_size_limit = 100000` is required, the mock contracts exceed the EIP-170 24 KB limit. -`evm_version = "cancun"` is no longer required as of `@cofhe/mock-contracts@0.5.0` — `MockACL` was migrated off transient storage to block-number-based storage. Set it only if your own contracts need cancun-specific opcodes. +`evm_version = "cancun"` is no longer required as of `@cofhe/mock-contracts@0.5.0`. `MockACL` was migrated off transient storage to block-number-based storage. Set it only if your own contracts need cancun-specific opcodes. ## 3. Configure `remappings.txt` @@ -70,7 +70,7 @@ hardhat/=node_modules/forge-std/src/ @cofhe/foundry-plugin/=node_modules/@cofhe/foundry-plugin/ ``` -The `hardhat/=node_modules/forge-std/src/` line is **load-bearing** — `MockCoFHE.sol` imports `hardhat/console.sol`, and this alias resolves it to forge-std's compatible `console.sol`. +The `hardhat/=node_modules/forge-std/src/` line is **load-bearing**. `MockCoFHE.sol` imports `hardhat/console.sol`, and this alias resolves it to forge-std's compatible `console.sol`. ## 4. Write a contract @@ -91,9 +91,9 @@ contract MyContract { } ``` -- `euint32` — an encrypted `uint32` stored on-chain as a ciphertext handle. -- `InEuint32` — the encrypted input struct produced by `CofheClient`. -- `FHE.allowThis` / `FHE.allowSender` — grant the contract and caller permission to read the encrypted value (required by the ACL). +- `euint32`: an encrypted `uint32` stored onchain as a ciphertext handle. +- `InEuint32`: the encrypted input struct produced by `CofheClient`. +- `FHE.allowThis` / `FHE.allowSender`: grant the contract and caller permission to read the encrypted value (required by the ACL). ## 5. Write a test @@ -142,7 +142,7 @@ contract MyContractTest is CofheTest { forge test -vvv ``` -``` +```text [PASS] test_StoresAndDecryptsAnEncryptedValue() (gas: …) Test result: ok. 1 passed; 0 failed; 0 skipped; finished in … @@ -152,13 +152,13 @@ Test result: ok. 1 passed; 0 failed; 0 skipped; finished in … 1. **`deployMocks()`** deployed the full CoFHE coprocessor mock stack (TaskManager, ACL, ZK verifier, threshold network) to the in-process EVM. 2. **`createCofheClient()` + `bob.connect(BOB_PKEY)`** spun up an in-Solidity SDK shim bound to `vm.addr(BOB_PKEY)`. -3. **`bob.createInEuint32(42)`** produced a signed `InEuint32` — the same shape your contract receives on testnet, signed by `MockZkVerifierSigner`. +3. **`bob.createInEuint32(42)`** produced a signed `InEuint32`, the same shape your contract receives on testnet, signed by `MockZkVerifierSigner`. 4. **`vm.prank(bob.account())` + `setValue(...)`** called the contract as Bob. The contract stored the ciphertext handle and granted ACL access to itself and Bob. -5. **`expectPlaintext(myContract.storedValue(), 42)`** read the on-chain plaintext directly from `MockTaskManager.mockStorage` — no permit, no SDK round-trip. +5. **`expectPlaintext(myContract.storedValue(), 42)`** read the onchain plaintext directly from `MockTaskManager.mockStorage`, no permit, no SDK round-trip. ## Next steps -- [Foundry Plugin → Getting Started](/client-sdk/foundry-plugin/getting-started) — full plugin configuration and features. -- [CofheTest](/client-sdk/foundry-plugin/cofhe-test) — `deployMocks`, `expectPlaintext`, log toggles. -- [CofheClient](/client-sdk/foundry-plugin/cofhe-client) — encrypt inputs, decrypt for view / tx, permits. -- [Testing](/client-sdk/foundry-plugin/testing) — canonical test patterns for ACL, public-decrypt, and fuzzing. +- [Foundry Plugin to Getting Started](/client-sdk/foundry-plugin/getting-started): full plugin configuration and features. +- [CofheTest](/client-sdk/foundry-plugin/cofhe-test): `deployMocks`, `expectPlaintext`, log toggles. +- [CofheClient](/client-sdk/foundry-plugin/cofhe-client): encrypt inputs, decrypt for view / tx, permits. +- [Testing](/client-sdk/foundry-plugin/testing): canonical test patterns for ACL, public-decrypt, and fuzzing. diff --git a/client-sdk/quick-start/hardhat.mdx b/client-sdk/quick-start/hardhat.mdx index ffb9fcf..9b6a070 100644 --- a/client-sdk/quick-start/hardhat.mdx +++ b/client-sdk/quick-start/hardhat.mdx @@ -1,9 +1,9 @@ --- title: "Quick Start: Hardhat" -description: "Set up a Hardhat project with @cofhe/sdk, write a contract, and test the encrypt → store → decrypt flow" +description: "Set up a Hardhat project with @cofhe/sdk, write a contract, and test the encrypt to store to decrypt flow" --- -Set up a Hardhat project with `@cofhe/hardhat-plugin`, write a contract that stores an encrypted `uint32`, and test the encrypt → store → decrypt flow. +Set up a Hardhat project with `@cofhe/hardhat-plugin`, write a contract that stores an encrypted `uint32`, and test the encrypt to store to decrypt flow. Want to skip the setup? Clone the [cofhe-hardhat-starter](https://github.com/FhenixProtocol/cofhe-hardhat-starter) template to get a pre-configured project with everything ready to go. @@ -60,7 +60,7 @@ Without `evmVersion: 'cancun'`, compilation will fail with errors from `@fhenixp ## 3. Write a contract -Create a minimal contract that accepts an encrypted input and stores it on-chain. +Create a minimal contract that accepts an encrypted input and stores it onchain. ```solidity contracts/MyContract.sol // SPDX-License-Identifier: UNLICENSED @@ -79,13 +79,13 @@ contract MyContract { } ``` -- `euint32` — an encrypted `uint32` stored on-chain as a ciphertext handle. -- `InEuint32` — the encrypted input struct produced by the SDK. -- `FHE.allowThis` / `FHE.allowSender` — grant the contract and caller permission to read the encrypted value (required by the ACL). +- `euint32`: an encrypted `uint32` stored onchain as a ciphertext handle. +- `InEuint32`: the encrypted input struct produced by the SDK. +- `FHE.allowThis` / `FHE.allowSender`: grant the contract and caller permission to read the encrypted value (required by the ACL). ## 4. Write a test -Use `hre.cofhe.createClientWithBatteries` to get a fully configured SDK client with a self-permit, then encrypt → send → decrypt. +Use `hre.cofhe.createClientWithBatteries` to get a fully configured SDK client with a self-permit, then encrypt to send to decrypt. ```typescript test/MyContract.test.ts import hre from 'hardhat'; @@ -131,9 +131,9 @@ describe('MyContract', () => { npx hardhat test ``` -The plugin deploys mock contracts automatically — no extra setup needed. +The plugin deploys mock contracts automatically, no extra setup needed. -``` +```text MyContract ✓ stores and decrypts an encrypted value @@ -145,11 +145,11 @@ The plugin deploys mock contracts automatically — no extra setup needed. 1. The **Hardhat plugin** deployed mock versions of the CoFHE coprocessor contracts (TaskManager, ACL, ZK verifier, threshold network) before the test ran. 2. `createClientWithBatteries` created an SDK client connected to the Hardhat network, with a self-permit ready to go. 3. `encryptInputs` encrypted the plaintext `42` into an FHE ciphertext with a ZK proof (simulated by the mock verifier). -4. The contract stored the ciphertext handle on-chain and set ACL permissions. +4. The contract stored the ciphertext handle onchain and set ACL permissions. 5. `decryptForView` used the permit to decrypt the handle back to `42n` locally. ## Next steps -- [Hardhat Plugin](/client-sdk/hardhat-plugin/getting-started) — full plugin configuration and features. -- [Mock Contracts](/client-sdk/hardhat-plugin/mock-contracts) — read plaintext directly and assert encrypted state in tests. -- [Logging](/client-sdk/hardhat-plugin/logging) — inspect every FHE operation your contracts perform. +- [Hardhat Plugin](/client-sdk/hardhat-plugin/getting-started): full plugin configuration and features. +- [Mock Contracts](/client-sdk/hardhat-plugin/mock-contracts): read plaintext directly and assert encrypted state in tests. +- [Logging](/client-sdk/hardhat-plugin/logging): inspect every FHE operation your contracts perform. diff --git a/client-sdk/quick-start/javascript.mdx b/client-sdk/quick-start/javascript.mdx index 1a31b22..ec5b828 100644 --- a/client-sdk/quick-start/javascript.mdx +++ b/client-sdk/quick-start/javascript.mdx @@ -3,7 +3,7 @@ title: "Quick Start: JavaScript" description: "Get started with @cofhe/sdk in a browser or Node.js app" --- -Connect to an FHE-enabled contract, encrypt a value, send it on-chain, and decrypt the result — all from JavaScript. +Connect to an FHE-enabled contract, encrypt a value, send it onchain, and decrypt the result, all from JavaScript. ## Prerequisites @@ -131,8 +131,8 @@ console.log(plaintext); // 42n ## Next steps -- [Client Setup](/client-sdk/guides/client-setup) — adapters, connection management, and config options. -- [Encrypting Inputs](/client-sdk/guides/encrypting-inputs) — supported types, builder API, and progress callbacks. -- [Permits](/client-sdk/guides/permits) — create, share, and manage decryption authorization. -- [Decrypt to View](/client-sdk/guides/decrypt-to-view) — reveal encrypted state in your UI. -- [Decrypt to Transact](/client-sdk/guides/decrypt-to-tx) — decrypt with a verifiable signature for on-chain use. +- [Client Setup](/client-sdk/guides/client-setup): adapters, connection management, and config options. +- [Encrypting Inputs](/client-sdk/guides/encrypting-inputs): supported types, builder API, and progress callbacks. +- [Permits](/client-sdk/guides/permits): create, share, and manage decryption authorization. +- [Decrypt to View](/client-sdk/guides/decrypt-to-view): reveal encrypted state in your UI. +- [Decrypt to Transact](/client-sdk/guides/decrypt-to-tx): decrypt with a verifiable signature for onchain use. diff --git a/client-sdk/reference/foundry-reference.mdx b/client-sdk/reference/foundry-reference.mdx index fea280d..315942e 100644 --- a/client-sdk/reference/foundry-reference.mdx +++ b/client-sdk/reference/foundry-reference.mdx @@ -28,7 +28,7 @@ import { CofheTest } from "@cofhe/foundry-plugin/contracts/CofheTest.sol"; | --- | --- | | `getPlaintext(bytes32 handle)` | Returns the raw `bytes32` plaintext from `MockTaskManager.mockStorage`. | | `getPlaintext(ebool)` / `(euint8)` / `(euint16)` / `(euint32)` / `(euint64)` / `(euint128)` / `(eaddress)` | Typed overloads returning `bool` / `uint8`–`uint128` / `address`. | -| `expectPlaintext(handle, value)` | Assertion variant — typed overloads for the same set. | +| `expectPlaintext(handle, value)` | Assertion variant. Typed overloads for the same set. | | `expectPlaintext(handle, value, "msg")` | With assertion message. | Reverts if the handle isn't in mock storage. @@ -86,7 +86,7 @@ All produce signed `EncryptedInput` shapes signed for `account()`. | --- | --- | --- | | `decryptForTx_withoutPermit(bytes32 ctHash)` | `(bytes32, uint256, bytes)` | `(ctHash, plaintext, signature)`. Signature consumable by `FHE.publishDecryptResult`. Requires `FHE.allowPublic(handle)` to have been called. | | `decryptForTx_withPermit(bytes32 ctHash, Permission permit)` | `(bytes32, uint256, bytes)` | ACL-gated `decryptForTx`. | -| `decryptForView(bytes32 ctHash, Permission permit)` | `uint256` | Off-chain seal/unseal. **Reverts on deny** — to assert deny use `mockThresholdNetwork.querySealOutput(...)`. | +| `decryptForView(bytes32 ctHash, Permission permit)` | `uint256` | Offchain seal/unseal. **Reverts on deny**, to assert deny use `mockThresholdNetwork.querySealOutput(...)`. | ### Permits @@ -94,13 +94,13 @@ All produce signed `EncryptedInput` shapes signed for `account()`. | --- | --- | | `permit_createSelf()` | Self-permit for the connected account; sealing key auto-derived. | | `permit_createShared(address recipient)` | Issuer-side shared permit. | -| `permit_exportShared(Permission perm)` | Strip sensitive fields → `SharedPermitExport`. | +| `permit_exportShared(Permission perm)` | Strip sensitive fields to `SharedPermitExport`. | | `permit_importShared(SharedPermitExport export)` | Recipient-side completion. Reverts unless `export.recipient == account()`. | | `createSealingKey(bytes32 seed)` | Custom sealing key (rarely needed). | ## Mock storage layout -`MockTaskManager.mockStorage` is the on-chain plaintext storage that backs `getPlaintext` / `expectPlaintext`. It only exists in the mock environment — `getPlaintext` reverts on real CoFHE networks. +`MockTaskManager.mockStorage` is the onchain plaintext storage that backs `getPlaintext` / `expectPlaintext`. It only exists in the mock environment. `getPlaintext` reverts on real CoFHE networks. ## `foundry.toml` requirements @@ -113,7 +113,7 @@ libs = ["node_modules"] ``` -`evm_version = "cancun"` is no longer required as of `@cofhe/mock-contracts@0.5.0` — `MockACL` was migrated off transient storage to block-number-based storage. Set it only if your own contracts need cancun-specific opcodes. +`evm_version = "cancun"` is no longer required as of `@cofhe/mock-contracts@0.5.0`. `MockACL` was migrated off transient storage to block-number-based storage. Set it only if your own contracts need cancun-specific opcodes. ## `remappings.txt` (canonical shape) @@ -132,7 +132,7 @@ hardhat/=node_modules/forge-std/src/ ## Version pinning -`@cofhe/foundry-plugin` and `@cofhe/mock-contracts` pin `@fhenixprotocol/cofhe-contracts` *exactly* — keep all three CoFHE packages aligned. Known-good tuple as of the latest release: +`@cofhe/foundry-plugin` and `@cofhe/mock-contracts` pin `@fhenixprotocol/cofhe-contracts` *exactly*, keep all three CoFHE packages aligned. Known-good tuple as of the latest release: | Package | Version | | --- | --- | diff --git a/fhe-library/confidential-contracts/dual-mode/confidential-operations.mdx b/fhe-library/confidential-contracts/dual-mode/confidential-operations.mdx index f3589c2..c8f01cd 100644 --- a/fhe-library/confidential-contracts/dual-mode/confidential-operations.mdx +++ b/fhe-library/confidential-contracts/dual-mode/confidential-operations.mdx @@ -5,17 +5,17 @@ description: "Confidential transfers, the operator system, and transfer callback ## Overview -Once value is [shielded](/fhe-library/confidential-contracts/dual-mode/shield-unshield) into the confidential layer, `ERC20Confidential` exposes the full [ERC-7984](https://eips.ethereum.org/EIPS/eip-7984) surface for moving it privately. This API is **identical to FHERC20's** — confidential transfers, a time-based operator system, and safe transfers with receiver callbacks. This page covers each, with the specifics of the `ERC20Confidential` implementation. +Once value is [shielded](/fhe-library/confidential-contracts/dual-mode/shield-unshield) into the confidential layer, `ERC20Confidential` exposes the full [ERC-7984](https://eips.ethereum.org/EIPS/eip-7984) surface for moving it privately. This API is **identical to FHERC20's**. Confidential transfers, a time-based operator system, and safe transfers with receiver callbacks. This page covers each, with the specifics of the `ERC20Confidential` implementation. -The confidential-transfer API here mirrors FHERC20. If you already know FHERC20's [operators](/fhe-library/confidential-contracts/fherc20/operators) and [transfer callbacks](/fhe-library/confidential-contracts/fherc20/transfer-callbacks), the same mental model applies — only the contract name and errors differ. +The confidential-transfer API here mirrors FHERC20. If you already know FHERC20's [operators](/fhe-library/confidential-contracts/fherc20/operators) and [transfer callbacks](/fhe-library/confidential-contracts/fherc20/transfer-callbacks), the same mental model applies, only the contract name and errors differ. --- ## Confidential Transfers -Every transfer function comes in two overloads: an `InEuint64` overload for encrypted input coming from off-chain users, and an `euint64` overload for already-encrypted, contract-to-contract values. +Every transfer function comes in two overloads: an `InEuint64` overload for encrypted input coming from offchain users, and an `euint64` overload for already-encrypted, contract-to-contract values. ```solidity // From caller @@ -54,7 +54,7 @@ function _confidentialTransfer(address from, address to, euint64 value) ``` -**Zero-replacement:** if the sender's confidential balance is insufficient, the transfer moves **encrypted zero** rather than reverting (to avoid leaking balance information). Always use the returned `transferred` handle — not the requested amount — for any downstream logic. +**Zero-replacement:** if the sender's confidential balance is insufficient, the transfer moves **encrypted zero** rather than reverting (to avoid leaking balance information). Always use the returned `transferred` handle, not the requested amount, for any downstream logic. ### Usage @@ -72,7 +72,7 @@ await token.confidentialTransfer(recipient.address, enc); ``` -The caller is granted **transient** access (`FHE.allowTransient`) to the `transferred` handle — access valid only for the current transaction. Both the sender and recipient receive persistent access to it inside `_confidentialUpdate`. +The caller is granted **transient** access (`FHE.allowTransient`) to the `transferred` handle. Access valid only for the current transaction. Both the sender and recipient receive persistent access to it inside `_confidentialUpdate`. --- @@ -135,7 +135,7 @@ function confidentialTransferFrom(address from, address to, euint64 value) ``` -An operator has access to a holder's **entire** confidential balance until expiry, not a specific amount. Grant only to trusted addresses and prefer short windows. Expiry is silent — a `confidentialTransferFrom` after `until` reverts with `ERC20ConfidentialUnauthorizedSpender`. +An operator has access to a holder's **entire** confidential balance until expiry, not a specific amount. Grant only to trusted addresses and prefer short windows. Expiry is silent, a `confidentialTransferFrom` after `until` reverts with `ERC20ConfidentialUnauthorizedSpender`. `OperatorSet(holder, operator, until)` is emitted on every grant or revocation. @@ -155,7 +155,7 @@ function confidentialTransferFromAndCall(address from, address to, euint64 amoun ### The accept-or-refund mechanism -`ERC20Confidential` moves the tokens first, asks the recipient, and **refunds** whatever the recipient rejects — all under FHE, without revealing amounts: +`ERC20Confidential` moves the tokens first, asks the recipient, and **refunds** whatever the recipient rejects, all under FHE, without revealing amounts: ```solidity function _confidentialTransferAndCall(address from, address to, euint64 amount, bytes calldata data) @@ -183,7 +183,7 @@ function _confidentialTransferAndCall(address from, address to, euint64 amount, -`transferred = sent - refund` — the actual net amount that stuck with the recipient. +`transferred = sent - refund`, the actual net amount that stuck with the recipient. @@ -203,7 +203,7 @@ interface IERC7984Receiver { ``` - Return `FHE.asEbool(true)` to accept the transfer. -- Return `FHE.asEbool(false)` to reject it — the tokens are refunded to the sender under FHE. +- Return `FHE.asEbool(false)` to reject it, the tokens are refunded to the sender under FHE. ```solidity contract ConfidentialVault is IERC7984Receiver { @@ -230,7 +230,7 @@ contract ConfidentialVault is IERC7984Receiver { ``` -Unlike an all-or-nothing revert, the accept/reject decision is evaluated under encryption and settled by refunding the rejected portion. See [FHERC20 Transfer Callbacks](/fhe-library/confidential-contracts/fherc20/transfer-callbacks) for receiver patterns, reentrancy guidance, and data-passing examples — the receiver contract is written the same way. +Unlike an all-or-nothing revert, the accept/reject decision is evaluated under encryption and settled by refunding the rejected portion. See [FHERC20 Transfer Callbacks](/fhe-library/confidential-contracts/fherc20/transfer-callbacks) for receiver patterns, reentrancy guidance, and data-passing examples, the receiver contract is written the same way. --- @@ -262,7 +262,7 @@ event OperatorSet(address indexed holder, address indexed operator, uint48 until ``` -`ConfidentialTransfer` carries an encrypted `euint64` handle, never a plaintext amount — you cannot index transfer amounts off-chain from events. The sidecar `ERC20ConfidentialIndicator` also emits a standard `Transfer(from, to, 10110000001)` so explorers register activity without exposing amounts. +`ConfidentialTransfer` carries an encrypted `euint64` handle, never a plaintext amount, you cannot index transfer amounts offchain from events. The sidecar `ERC20ConfidentialIndicator` also emits a standard `Transfer(from, to, 10110000001)` so explorers register activity without exposing amounts. --- diff --git a/fhe-library/confidential-contracts/dual-mode/dual-balance-model.mdx b/fhe-library/confidential-contracts/dual-mode/dual-balance-model.mdx index 1f0cfef..a3e8ed3 100644 --- a/fhe-library/confidential-contracts/dual-mode/dual-balance-model.mdx +++ b/fhe-library/confidential-contracts/dual-mode/dual-balance-model.mdx @@ -30,7 +30,7 @@ abstract contract ERC20Confidential is ERC20, ERC165, IERC20Confidential, FHERC2 **Inherited from OpenZeppelin `ERC20`** -Transparent balances tracked in the standard `_balances` mapping. `balanceOf`, `transfer`, `approve`, `transferFrom`, and `totalSupply` behave like any ERC-20. Fully visible on-chain. +Transparent balances tracked in the standard `_balances` mapping. `balanceOf`, `transfer`, `approve`, `transferFrom`, and `totalSupply` behave like any ERC-20. Fully visible onchain. @@ -40,7 +40,7 @@ Encrypted balances stored as `euint64` handles. Readable only by the holder (via -Because `ERC20Confidential` inherits the **real** OpenZeppelin `ERC20`, the public side is genuinely functional — this is the inversion of FHERC20, where those same functions revert. +Because `ERC20Confidential` inherits the **real** OpenZeppelin `ERC20`, the public side is genuinely functional. This is the inversion of FHERC20, where those same functions revert. ```solidity /// @dev `false` because {balanceOf} returns the real public ERC-20 balance, not an indicator. @@ -90,7 +90,7 @@ function confidentialTotalSupply() public view virtual returns (euint64) { ``` -The `euint64` returned by `confidentialTotalSupply()` is **synthetic** — it wraps a plaintext number, not a registered ciphertext. It is for display/inspection only; you cannot decrypt it or feed it into FHE operations. +The `euint64` returned by `confidentialTotalSupply()` is **synthetic**, it wraps a plaintext number, not a registered ciphertext. It is for display/inspection only; you cannot decrypt it or feed it into FHE operations. --- @@ -124,7 +124,7 @@ Dust below one confidential unit cannot be shielded. `shield()` reverts with `Am ## The Confidential Update Engine -`_confidentialUpdate` is the encrypted analog of ERC-20's `_update`. Every confidential mutation — shield mint, confidential transfer, unshield burn — flows through it. Passing `address(0)` as `from` means *mint*; passing `address(0)` as `to` means *burn*. +`_confidentialUpdate` is the encrypted analog of ERC-20's `_update`. Every confidential mutation (shield mint, confidential transfer, unshield burn) flows through it. Passing `address(0)` as `from` means *mint*; passing `address(0)` as `to` means *burn*. ```solidity function _confidentialUpdate( @@ -170,7 +170,7 @@ Key behaviors baked into this function: `FHESafeMath.tryDecrease` returns an encrypted `success` flag. If the sender's balance is too low, `FHE.select(success, amount, 0)` sets the transferred amount to **encrypted zero** instead of reverting. -This is deliberate: reverting would leak whether the sender had enough balance. The trade-off is that transfers can silently move zero tokens — always work with the returned `transferred` handle, never the requested amount. +This is deliberate: reverting would leak whether the sender had enough balance. The trade-off is that transfers can silently move zero tokens, always work with the returned `transferred` handle, never the requested amount. @@ -179,9 +179,9 @@ Each balance change stores a **new** `euint64` handle (`ptr`). Any `FHE.allow` y After each mutation the function grants: -- `FHE.allowThis(...)` on the new balance and the transferred amount — so the contract can keep operating on them. -- `FHE.allow(balance, holder)` — so the holder can decrypt their own balance. -- `FHE.allow(transferred, from/to)` — so both parties can see what moved. +- `FHE.allowThis(...)` on the new balance and the transferred amount, so the contract can keep operating on them. +- `FHE.allow(balance, holder)`: so the holder can decrypt their own balance. +- `FHE.allow(transferred, from/to)`: so both parties can see what moved. Learn more in [Access Control](/fhe-library/core-concepts/access-control). @@ -191,7 +191,7 @@ Learn more in [Access Control](/fhe-library/core-concepts/access-control). ## The Indicator Sidecar Token -On FHERC20 the token's own `balanceOf` doubles as a wallet "activity indicator." `ERC20Confidential` can't do that — its `balanceOf` is a real balance. Instead, the constructor deploys a **separate companion token**, `ERC20ConfidentialIndicator`, dedicated to showing confidential activity in wallets and explorers **without revealing real amounts**. +On FHERC20 the token's own `balanceOf` doubles as a wallet "activity indicator." `ERC20Confidential` can't do that, its `balanceOf` is a real balance. Instead, the constructor deploys a **separate companion token**, `ERC20ConfidentialIndicator`, dedicated to showing confidential activity in wallets and explorers **without revealing real amounts**. ```solidity constructor(address parentAddress, string memory parentName, string memory parentSymbol) @@ -212,9 +212,9 @@ function balanceOf(address account) public view override returns (uint256) { Characteristics of the sidecar: - **Name/symbol:** `"1011000 "` / `"c"` (e.g. `cMDT`), with **4 decimals**. -- **Fake balances:** `balanceOf` returns `10110000000 + _indicatedBalances[account]` — a deliberately non-revealing number that changes with activity but encodes no real amount. +- **Fake balances:** `balanceOf` returns `10110000000 + _indicatedBalances[account]`: a deliberately non-revealing number that changes with activity but encodes no real amount. - **Driven only by the parent:** `emitConfidentialTransfer(from, to)` is `onlyParent`. The parent calls it inside `_confidentialUpdate`, nudging a bounded internal counter and emitting a `Transfer(from, to, 10110000001)` so explorers register that *something* happened. -- **Inert on its own:** `transfer`, `transferFrom`, `approve`, and `allowance` all revert with `ERC20ConfidentialIndicatorNoOp()`. You cannot move or approve the indicator token — it exists purely for display. +- **Inert on its own:** `transfer`, `transferFrom`, `approve`, and `allowance` all revert with `ERC20ConfidentialIndicatorNoOp()`. You cannot move or approve the indicator token, it exists purely for display. ```solidity function emitConfidentialTransfer(address from, address to) public onlyParent { @@ -225,7 +225,7 @@ function emitConfidentialTransfer(address from, address to) public onlyParent { ``` -The indicator's counter jitters within bounds (increment seeds at `5001`, capped at `9999`; decrement seeds at `4999`, floored at `1`). These numbers are intentionally meaningless — they signal activity, not balance. Access the deployed sidecar via the parent's public `indicatorToken` variable. +The indicator's counter jitters within bounds (increment seeds at `5001`, capped at `9999`; decrement seeds at `4999`, floored at `1`). These numbers are intentionally meaningless, they signal activity, not balance. Access the deployed sidecar via the parent's public `indicatorToken` variable. --- @@ -259,7 +259,7 @@ if (handle && handle !== 0n) { -A confidential balance handle of `0` means the account has **never** held a confidential balance — not that the balance is zero. Always check before attempting to decrypt. +A confidential balance handle of `0` means the account has **never** held a confidential balance, not that the balance is zero. Always check before attempting to decrypt. --- diff --git a/fhe-library/confidential-contracts/dual-mode/overview.mdx b/fhe-library/confidential-contracts/dual-mode/overview.mdx index 9d49abc..3573847 100644 --- a/fhe-library/confidential-contracts/dual-mode/overview.mdx +++ b/fhe-library/confidential-contracts/dual-mode/overview.mdx @@ -1,20 +1,20 @@ --- title: "ERC20Confidential Overview" -description: "The dual-mode confidential token — a real public ERC-20 and an encrypted balance in one contract" +description: "The dual-mode confidential token, a real public ERC-20 and an encrypted balance in one contract" --- ## What is ERC20Confidential? -`ERC20Confidential` is a **dual-mode** confidential token. Unlike [FHERC20](/fhe-library/confidential-contracts/fherc20/overview)—where every balance is encrypted and the public ERC-20 surface is only a decorative shim—`ERC20Confidential` gives each holder **two real balances in the same contract**: +`ERC20Confidential` is a **dual-mode** confidential token. Unlike [FHERC20](/fhe-library/confidential-contracts/fherc20/overview), where every balance is encrypted and the public ERC-20 surface is only a decorative shim, `ERC20Confidential` gives each holder **two real balances in the same contract**: 1. A **public ERC-20 balance**, inherited from the standard OpenZeppelin `ERC20`. `balanceOf`, `transfer`, `approve`, `transferFrom`, and `totalSupply` all work exactly as they do on any normal token. -2. A **confidential, FHE-encrypted balance**, stored as an `euint64` handle. Nobody—not even the contract—can read it without an ACL grant and a decryption permit. +2. A **confidential, FHE-encrypted balance**, stored as an `euint64` handle. Nobody, not even the contract, can read it without an ACL grant and a decryption permit. -Tokens move between the two layers with **shield** (public → confidential) and **unshield → claim** (confidential → public). Think of it as a normal ERC-20 with a built-in privacy pool baked directly into the token. +Tokens move between the two layers with **shield** (public to confidential) and **unshield to claim** (confidential to public). Think of it as a normal ERC-20 with a built-in privacy pool baked directly into the token. -A working public ERC-20 balance **and** a parallel encrypted balance for the same holder — no separate wrapper contract required. +A working public ERC-20 balance **and** a parallel encrypted balance for the same holder, no separate wrapper contract required. @@ -89,11 +89,11 @@ Move public tokens into the confidential pool and mint yourself an equal encrypt -Transfer privately with `confidentialTransfer`, delegate with operators, and receive with callbacks — all on encrypted amounts. +Transfer privately with `confidentialTransfer`, delegate with operators, and receive with callbacks, all on encrypted amounts. - -Burn confidential tokens (async), decrypt the burned amount off-chain, then claim the equivalent public tokens back out of the pool. + +Burn confidential tokens (async), decrypt the burned amount offchain, then claim the equivalent public tokens back out of the pool. @@ -146,7 +146,7 @@ Auto-deployed by the constructor. Shows non-revealing "activity" balances in wal **Interface** -`IERC20Confidential is IFHERC20` — adds the shield/unshield bridge to the shared confidential-token surface. +`IERC20Confidential is IFHERC20`. Adds the shield/unshield bridge to the shared confidential-token surface. diff --git a/fhe-library/confidential-contracts/dual-mode/shield-unshield.mdx b/fhe-library/confidential-contracts/dual-mode/shield-unshield.mdx index e44e83b..252337b 100644 --- a/fhe-library/confidential-contracts/dual-mode/shield-unshield.mdx +++ b/fhe-library/confidential-contracts/dual-mode/shield-unshield.mdx @@ -5,15 +5,15 @@ description: "The built-in bridge that moves value between the public and confid ## Overview -`ERC20Confidential` bridges its two balances with three operations. **Shielding** (public → confidential) is synchronous. **Unshielding** (confidential → public) is a two-step, asynchronous flow because FHE decryption happens off-chain. +`ERC20Confidential` bridges its two balances with three operations. **Shielding** (public to confidential) is synchronous. **Unshielding** (confidential to public) is a two-step, asynchronous flow because FHE decryption happens offchain. -Move public tokens into the confidential pool and mint yourself an equal encrypted balance — in a single transaction. +Move public tokens into the confidential pool and mint yourself an equal encrypted balance, in a single transaction. - -Burn confidential tokens, decrypt the burned amount off-chain, then claim the equivalent public tokens back out of the pool. + +Burn confidential tokens, decrypt the burned amount offchain, then claim the equivalent public tokens back out of the pool. @@ -21,7 +21,7 @@ Both directions are governed by the [conversion rate](/fhe-library/confidential- --- -## Shielding (Public → Confidential) +## Shielding (Public to Confidential) ```solidity function shield(uint256 amount) public virtual { @@ -61,7 +61,7 @@ An equal (rate-scaled) encrypted amount is credited to the caller's confidential -Shielding requires no approval — it moves the caller's **own** public balance. The caller must already hold at least `amountToShield` public tokens. +Shielding requires no approval, it moves the caller's **own** public balance. The caller must already hold at least `amountToShield` public tokens. ### Example @@ -84,11 +84,11 @@ await token.shield(999n); // rate = 1e12 → rounds to 0 → AmountTooSmallForCo --- -## Unshielding (Confidential → Public) +## Unshielding (Confidential to Public) -Unshielding is asynchronous. `unshield` burns the confidential tokens and marks the burned ciphertext publicly decryptable; the network decrypts it off-chain; `claimUnshielded` then verifies the decryption proof and releases the public tokens. +Unshielding is asynchronous. `unshield` burns the confidential tokens and marks the burned ciphertext publicly decryptable; the network decrypts it offchain; `claimUnshielded` then verifies the decryption proof and releases the public tokens. -### Step 1 — Burn and create a claim +### Step 1: Burn and create a claim There are two overloads: one takes a plaintext `uint64`, the other an already-encrypted `euint64` handle. @@ -113,12 +113,12 @@ function _unshield(euint64 amount, uint64 requestedAmount) internal virtual retu ``` -Because of [zero-replacement](/fhe-library/confidential-contracts/dual-mode/dual-balance-model#the-confidential-update-engine), unshielding more than your confidential balance burns **zero** — it does not revert. You'll end up with a claim for zero tokens. Ensure sufficient balance before unshielding. +Because of [zero-replacement](/fhe-library/confidential-contracts/dual-mode/dual-balance-model#the-confidential-update-engine), unshielding more than your confidential balance burns **zero**, it does not revert. You'll end up with a claim for zero tokens. Ensure sufficient balance before unshielding. -### Step 2 — Decrypt off-chain +### Step 2: Decrypt offchain -`unshield` returns the burned `euint64` handle and calls `FHE.allowPublic(burned)`, so **anyone** can decrypt it — no permit needed. Retrieve the claim's `ctHash` and request decryption: +`unshield` returns the burned `euint64` handle and calls `FHE.allowPublic(burned)`, so **anyone** can decrypt it, no permit needed. Retrieve the claim's `ctHash` and request decryption: ```typescript // Find the pending claim's ctHash @@ -135,7 +135,7 @@ const decryptResult = await cofheClient // decryptResult.signature → the decryption proof ``` -### Step 3 — Claim the public tokens +### Step 3: Claim the public tokens Submit the plaintext and proof. The contract verifies the proof (via `FHE.verifyDecryptResult`), then transfers the rate-scaled public amount out of the pool. @@ -163,7 +163,7 @@ const publicBal = await token.balanceOf(user.address); ``` -The public payout goes to the claim's stored `to` (the address that called `unshield`), regardless of who submits the `claimUnshielded` transaction. The proof — not the sender — authorizes the release. +The public payout goes to the claim's stored `to` (the address that called `unshield`), regardless of who submits the `claimUnshielded` transaction. The proof, not the sender, authorizes the release. --- @@ -228,7 +228,7 @@ function getUserClaims(address user) public view returns (Claim[] memory); -`getUserClaims` returns only **pending** claims — a claim is removed from the user's set once it is successfully claimed. Use it to drive a "claimable balance" view in your UI. +`getUserClaims` returns only **pending** claims, a claim is removed from the user's set once it is successfully claimed. Use it to drive a "claimable balance" view in your UI. ### Claim errors @@ -296,4 +296,4 @@ event UnshieldedTokensClaimed( - Understand the backing pool and rate in [The Dual-Balance Model](/fhe-library/confidential-contracts/dual-mode/dual-balance-model) - Move tokens privately with [Confidential Operations](/fhe-library/confidential-contracts/dual-mode/confidential-operations) - Compare with the [FHERC20 wrapper unshield flow](/fhe-library/confidential-contracts/fherc20/fherc20-wrapper#unshielding-tokens) -- Decrypt handles off-chain with the [Client SDK — Decrypt to Transaction](/client-sdk/guides/decrypt-to-tx) +- Decrypt handles offchain with the [Client SDK. Decrypt to Transaction](/client-sdk/guides/decrypt-to-tx) diff --git a/fhe-library/confidential-contracts/fherc20/best-practices.mdx b/fhe-library/confidential-contracts/fherc20/best-practices.mdx index 6f6bbab..e563e57 100644 --- a/fhe-library/confidential-contracts/fherc20/best-practices.mdx +++ b/fhe-library/confidential-contracts/fherc20/best-practices.mdx @@ -255,7 +255,7 @@ function dangerousReceiver( **Use operators when:** - User directly approves via wallet transaction -- Simple on-chain permission grants +- Simple onchain permission grants ```solidity // User directly approves DEX @@ -297,7 +297,7 @@ await token.setOperator( | **Complexity** | Simple | | **User Experience** | Standard wallet flow | | **Use Case** | General permissions | -| **Implementation** | Direct on-chain | +| **Implementation** | Direct onchain | --- diff --git a/fhe-library/confidential-contracts/fherc20/core-features.mdx b/fhe-library/confidential-contracts/fherc20/core-features.mdx index a959eca..8aebb20 100644 --- a/fhe-library/confidential-contracts/fherc20/core-features.mdx +++ b/fhe-library/confidential-contracts/fherc20/core-features.mdx @@ -131,7 +131,7 @@ function confidentialTransfer( -Use the `InEuint64` overload when accepting user input from off-chain. Use the `euint64` overload for contract-to-contract transfers where the value is already encrypted. Note: the `euint64` overload requires the caller to be authorized via the FHE ACL for the given amount. +Use the `InEuint64` overload when accepting user input from offchain. Use the `euint64` overload for contract-to-contract transfers where the value is already encrypted. Note: the `euint64` overload requires the caller to be authorized via the FHE ACL for the given amount. ### Transfer Implementation @@ -314,10 +314,10 @@ FHE.allowThis(_confidentialTotalSupply); ``` This ensures: -- ✅ Users can access their own balances -- ✅ The contract can perform operations on balances -- ✅ Transfer participants (sender, receiver, and caller) can see the transferred amount -- ✅ Total supply is only accessible by the contract +- Users can access their own balances +- The contract can perform operations on balances +- Transfer participants (sender, receiver, and caller) can see the transferred amount +- Total supply is only accessible by the contract Learn more about FHE access control in the [Access Control](/fhe-library/core-concepts/access-control) guide. @@ -395,7 +395,7 @@ Like transfers, burning uses the zero-replacement pattern. If you attempt to bur ## Amount Disclosure -FHERC20 includes optional disclosure functions for transparency when needed. Accounts with access to an encrypted amount can voluntarily reveal it on-chain. +FHERC20 includes optional disclosure functions for transparency when needed. Accounts with access to an encrypted amount can voluntarily reveal it onchain. ### Requesting Disclosure @@ -557,7 +557,7 @@ event OperatorSet(address indexed holder, address indexed operator, uint48 until ``` -The `Transfer` event doesn't reveal the actual transfer amount—only that a transfer occurred. The `value` field always contains `indicatorTick` to maintain ERC20 compatibility while preserving privacy. +The `Transfer` event doesn't reveal the actual transfer amount, only that a transfer occurred. The `value` field always contains `indicatorTick` to maintain ERC20 compatibility while preserving privacy. --- diff --git a/fhe-library/confidential-contracts/fherc20/fherc20-wrapper.mdx b/fhe-library/confidential-contracts/fherc20/fherc20-wrapper.mdx index 2c516fd..2ecd6bb 100644 --- a/fhe-library/confidential-contracts/fherc20/fherc20-wrapper.mdx +++ b/fhe-library/confidential-contracts/fherc20/fherc20-wrapper.mdx @@ -77,12 +77,12 @@ User can now transfer the shielded tokens confidentially using all FHERC20 featu When ready to exit, user burns confidential tokens. The burned amount is marked as publicly decryptable via `FHE.allowPublic`, and a claim is created. - -The user (or anyone) requests decryption of the burned amount off-chain via `decryptForTx`, receiving the plaintext and a decryption proof. + +The user (or anyone) requests decryption of the burned amount offchain via `decryptForTx`, receiving the plaintext and a decryption proof. -The user submits the plaintext and proof on-chain via `claimUnshielded`. The contract verifies the proof and transfers the underlying ERC20 tokens (multiplied by the rate). +The user submits the plaintext and proof onchain via `claimUnshielded`. The contract verifies the proof and transfers the underlying ERC20 tokens (multiplied by the rate). @@ -160,7 +160,7 @@ await nativeWrapper.shieldWrappedNative(recipientAddress, amount); ## Unshielding Tokens -Unshielding is a three-step process shared by both wrapper types: burn on-chain, decrypt off-chain, then claim with proof. +Unshielding is a three-step process shared by both wrapper types: burn onchain, decrypt offchain, then claim with proof. ### Step 1: Unshield (Burn and Create Claim) @@ -190,7 +190,7 @@ await wrapper.unshield(myAddress, myAddress, 100); Due to the [zero-replacement behavior](/fhe-library/confidential-contracts/fherc20/core-features#the-update-function), if you attempt to unshield more than your balance, zero tokens will be burned and you'll have a claim for zero tokens. -### Step 2: Decrypt Off-Chain +### Step 2: Decrypt Offchain Retrieve the claim's `ctHash` using `getUserClaims`, then request decryption via `decryptForTx`. Since `FHE.allowPublic` was called, no permit is needed: @@ -396,7 +396,7 @@ Always test with the specific token before deploying to production. -Claiming requires completing the off-chain decryption step first: +Claiming requires completing the offchain decryption step first: ```typescript // 1. Unshield burns tokens and allows public decryption diff --git a/fhe-library/confidential-contracts/fherc20/overview.mdx b/fhe-library/confidential-contracts/fherc20/overview.mdx index ee57471..1a72b98 100644 --- a/fhe-library/confidential-contracts/fherc20/overview.mdx +++ b/fhe-library/confidential-contracts/fherc20/overview.mdx @@ -5,7 +5,7 @@ description: "Introduction to the FHERC20 confidential token standard (ERC-7984) ## What is FHERC20? -FHERC20 is a Fully Homomorphic Encryption (FHE) enabled token standard that provides **complete confidentiality** for token balances while maintaining compatibility with existing ERC20 infrastructure. Built on the Fhenix CoFHE protocol and implementing the [ERC-7984](https://eips.ethereum.org/EIPS/eip-7984) standard, FHERC20 allows users to transfer and manage tokens without revealing their balances or transaction amounts to anyone—not even other participants in the same smart contract. +FHERC20 is a Fully Homomorphic Encryption (FHE) enabled token standard that provides **complete confidentiality** for token balances while maintaining compatibility with existing ERC20 infrastructure. Built on the Fhenix CoFHE protocol and implementing the [ERC-7984](https://eips.ethereum.org/EIPS/eip-7984) standard, FHERC20 allows users to transfer and manage tokens without revealing their balances or transaction amounts to anyone, not even other participants in the same smart contract. @@ -100,7 +100,7 @@ function requestDiscloseEncryptedAmount(euint64 amount) external; function discloseEncryptedAmount(euint64 amount, uint64 cleartext, bytes memory proof) external; ``` -This emits an `AmountDisclosed` event, allowing accounts with access to an encrypted amount to voluntarily reveal it on-chain. +This emits an `AmountDisclosed` event, allowing accounts with access to an encrypted amount to voluntarily reveal it onchain. --- @@ -108,10 +108,10 @@ This emits an `AmountDisclosed` event, allowing accounts with access to an encry -Users encrypt their transaction data (amounts, recipients) off-chain using the Client SDK (`@cofhe/sdk`) before submitting to the blockchain. +Users encrypt their transaction data (amounts, recipients) offchain using the Client SDK (`@cofhe/sdk`) before submitting to the blockchain. - + The FHERC20 contract performs all operations (additions, subtractions, comparisons) on encrypted data without ever decrypting it. @@ -153,10 +153,11 @@ Standard ERC20 functions like `balanceOf()` must return a `uint256`. For confide The `balanceOfIsIndicator()` function returns `true`, signalling to wallets and block explorers that `balanceOf` returns an indicator, not a real balance. This allows: -- ✅ Wallets to display "activity" for the token -- ✅ Block explorers to show transactions occurred -- ✅ Basic compatibility with existing infrastructure -- ❌ But doesn't reveal actual token amounts +- Wallets to display "activity" for the token +- Block explorers to show that transactions occurred +- Basic compatibility with existing infrastructure + +It does not reveal actual token amounts. Users can opt out by calling `resetIndicatedBalance()` to set their indicator back to zero. diff --git a/fhe-library/core-concepts/access-control.mdx b/fhe-library/core-concepts/access-control.mdx index 6c58500..b96f6b1 100644 --- a/fhe-library/core-concepts/access-control.mdx +++ b/fhe-library/core-concepts/access-control.mdx @@ -45,19 +45,19 @@ By default, newly created ciphertext handles are accessible to the contract that ### What the ACL Does *Not* Gate -The ACL governs **operation creation** — computing on a handle, and requesting its decryption. It does not gate the on-chain publication of a decrypt result: +The ACL governs **operation creation**. Computing on a handle, and requesting its decryption. It does not gate the onchain publication of a decrypt result: ```solidity // Anyone can call this — it is signature-gated, not ACL-gated FHE.publishDecryptResult(seenHandle, plaintext, signature); ``` -`publishDecryptResult` (and `verifyDecryptResult`) only check that `signature` is a valid ECDSA signature from the Threshold Network over that exact `(ctHash, result, chainId, encType)` tuple. Publication is deliberately permissionless so clients and relayers can settle results on-chain. +`publishDecryptResult` (and `verifyDecryptResult`) only check that `signature` is a valid ECDSA signature from the Threshold Network over that exact `(ctHash, result, chainId, encType)` tuple. Publication is deliberately permissionless so clients and relayers can settle results onchain. -What protects the value is that an attacker cannot *obtain* that signature. The signature is only issued by the Threshold Network in response to a decryption request, and that request is ACL-gated: the requester must either hold permission on the handle (with a valid permit) or the handle must have been marked with `FHE.allowPublic()`. Without permission, the request is denied and no signature is ever produced — so there is nothing valid to publish. +What protects the value is that an attacker cannot *obtain* that signature. The signature is only issued by the Threshold Network in response to a decryption request, and that request is ACL-gated: the requester must either hold permission on the handle (with a valid permit) or the handle must have been marked with `FHE.allowPublic()`. Without permission, the request is denied and no signature is ever produced, so there is nothing valid to publish. -Treat `FHE.allowPublic()` as the real decision point. Once a handle is marked public, anyone can request its decryption and publish the plaintext on-chain, permanently. +Treat `FHE.allowPublic()` as the real decision point. Once a handle is marked public, anyone can request its decryption and publish the plaintext onchain, permanently. --- @@ -114,9 +114,9 @@ FHE.allowTransient(secretValue, otherContract); // Temporary access for this tx **`FHE.allowPublic(CIPHERTEXT_HANDLE)`** -Marks a ciphertext handle as eligible for public decryption. Anyone can then request decryption of this value off-chain via `decryptForTx` and publish or verify the result on-chain. +Marks a ciphertext handle as eligible for public decryption. Anyone can then request decryption of this value offchain via `decryptForTx` and publish or verify the result onchain. -Use this when a value is intended to become public — for example, the amount being unshielded in an FHERC20 unwrap flow. +Use this when a value is intended to become public, for example, the amount being unshielded in an FHERC20 unwrap flow. ```solidity euint32 burnedAmount = FHE.asEuint32(input); @@ -124,7 +124,7 @@ FHE.allowPublic(burnedAmount); // Anyone can decrypt and publish this value ``` -`allowPublic` does not reveal the value immediately. It only grants permission for anyone to request decryption. The value is revealed only when someone submits the plaintext and signature on-chain via `FHE.publishDecryptResult` or `FHE.verifyDecryptResult`. +`allowPublic` does not reveal the value immediately. It only grants permission for anyone to request decryption. The value is revealed only when someone submits the plaintext and signature onchain via `FHE.publishDecryptResult` or `FHE.verifyDecryptResult`. @@ -133,7 +133,7 @@ FHE.allowPublic(burnedAmount); // Anyone can decrypt and publish this value ## Decryption and Access Control -Decryption is a multi-step process: a client requests the plaintext and a threshold signature off-chain via `decryptForTx`, then publishes or verifies the result on-chain. +Decryption is a multi-step process: a client requests the plaintext and a threshold signature offchain via `decryptForTx`, then publishes or verifies the result onchain. Access control governs who can request decryption: @@ -141,7 +141,7 @@ Access control governs who can request decryption: - Otherwise, only addresses with explicit permission on the handle can request decryption, and must provide a valid permit (`.withPermit()`). -If the requester does not have permission on the ciphertext handle, the decryption request will be denied by the access control system. Grant appropriate permissions before attempting to decrypt — use `FHE.allowPublic()` for values intended to become public, or `FHE.allow()` / `FHE.allowSender()` for restricted access. +If the requester does not have permission on the ciphertext handle, the decryption request will be denied by the access control system. Grant appropriate permissions before attempting to decrypt, use `FHE.allowPublic()` for values intended to become public, or `FHE.allow()` / `FHE.allowSender()` for restricted access. --- diff --git a/fhe-library/core-concepts/conditions.mdx b/fhe-library/core-concepts/conditions.mdx index 1cb003a..5530506 100644 --- a/fhe-library/core-concepts/conditions.mdx +++ b/fhe-library/core-concepts/conditions.mdx @@ -234,7 +234,7 @@ contract EncryptedAuction { ``` -In the example above, the actual bid amounts remain encrypted throughout the auction. To reveal the winner, a client calls `decryptForTx` off-chain to obtain the plaintext and a threshold signature, then submits them on-chain via `revealWinner` for verification. +In the example above, the actual bid amounts remain encrypted throughout the auction. To reveal the winner, a client calls `decryptForTx` offchain to obtain the plaintext and a threshold signature, then submits them onchain via `revealWinner` for verification. --- diff --git a/fhe-library/core-concepts/decryption-operations.mdx b/fhe-library/core-concepts/decryption-operations.mdx index 15b80f1..25407d0 100644 --- a/fhe-library/core-concepts/decryption-operations.mdx +++ b/fhe-library/core-concepts/decryption-operations.mdx @@ -7,10 +7,10 @@ description: "Understanding how to decrypt encrypted data in FHE smart contracts Decryption is the process of converting encrypted data back into its original form. In the context of Fully Homomorphic Encryption (FHE), decryption allows for the retrieval of results after performing computations on encrypted data. -Decryption in CoFHE is a multi-step process that involves both off-chain and on-chain components: +Decryption in CoFHE is a multi-step process that involves both offchain and onchain components: -1. A client requests decryption off-chain and receives the plaintext along with a Threshold Network signature. -2. The plaintext and signature are submitted on-chain, where the contract publishes or verifies the result. +1. A client requests decryption offchain and receives the plaintext along with a Threshold Network signature. +2. The plaintext and signature are submitted onchain, where the contract publishes or verifies the result. Learn more about our unique MPC decryption threshold network in the [Threshold Network](/deep-dive/cofhe-components/threshold-network) guide. @@ -24,7 +24,7 @@ CoFHE provides two primary ways to perform decryption, each suited for different ### 1. Decrypt for Transaction (`decryptForTx`) -The client calls `decryptForTx(ctHash)` off-chain to obtain the plaintext and a Threshold Network signature. These are then submitted on-chain via `FHE.publishDecryptResult` or `FHE.verifyDecryptResult`, making the result verifiable by the contract. +The client calls `decryptForTx(ctHash)` offchain to obtain the plaintext and a Threshold Network signature. These are then submitted onchain via `FHE.publishDecryptResult` or `FHE.verifyDecryptResult`, making the result verifiable by the contract. **Common examples:** - **Unshield a confidential token**: reveal the encrypted amount so the contract can finalize the public transfer. @@ -32,11 +32,11 @@ The client calls `decryptForTx(ctHash)` off-chain to obtain the plaintext and a ### 2. Decrypt for View (`decryptForView`) -The client calls `decryptForView` off-chain to obtain the plaintext for display in a UI. No on-chain transaction or signature is needed. +The client calls `decryptForView` offchain to obtain the plaintext for display in a UI. No onchain transaction or signature is needed. **Common examples:** - Displaying a user's confidential balance in a wallet UI. -- Showing the current state of an encrypted value without revealing it on-chain. +- Showing the current state of an encrypted value without revealing it onchain. Use `decryptForTx` when you need to act on the decrypted value in a smart contract. Use `decryptForView` when you only need to display the value in a UI. @@ -44,33 +44,33 @@ Use `decryptForTx` when you need to act on the decrypted value in a smart contra ### 3. Client-Published Decryption (Signature-Verified) -The client decrypts off-chain via `decryptForTx`, receives the plaintext result along with an **ECDSA signature** from the Threshold Network's Dispatcher, and then publishes the result on-chain by calling `FHE.publishDecryptResult()`. The TaskManager verifies the signature on-chain before storing the result. +The client decrypts offchain via `decryptForTx`, receives the plaintext result along with an **ECDSA signature** from the Threshold Network's Dispatcher, and then publishes the result onchain by calling `FHE.publishDecryptResult()`. The TaskManager verifies the signature onchain before storing the result. -This combines the best of both worlds: the client controls when the result lands on-chain, while the contract can still use the decrypted value. +This combines the best of both worlds: the client controls when the result lands onchain, while the contract can still use the decrypted value. -The signature cryptographically proves the result came from the authorized Threshold Network. No trust in the publisher is required — anyone holding a valid signature can submit it. +The signature cryptographically proves the result came from the authorized Threshold Network. No trust in the publisher is required. Anyone holding a valid signature can submit it. ### Comparison Table | Method | Visibility | Gas Cost | Smart Contract Usable | Best For | |--------|-----------|----------|----------------------|----------| -| **`decryptForTx`** | Public (once published on-chain) | Gas for the publish/verify tx | Yes | Public results, contract logic | -| **`decryptForView`** | Private (off-chain only) | None | No | UI display, confidential data | -| **Client-Published (signature)** | Public (on-chain) | Medium | Yes | Client-driven settlement, permissionless delivery | +| **`decryptForTx`** | Public (once published onchain) | Gas for the publish/verify tx | Yes | Public results, contract logic | +| **`decryptForView`** | Private (offchain only) | None | No | UI display, confidential data | +| **Client-Published (signature)** | Public (onchain) | Medium | Yes | Client-driven settlement, permissionless delivery | --- ## The Decryption Flow -### Step 1: Grant decryption permissions (on-chain) +### Step 1: Grant decryption permissions (onchain) Before anyone can request decryption, the ciphertext handle must have the appropriate ACL permissions. Use one of the following in your contract: -- `FHE.allowPublic(ctHash)` — anyone can request decryption (common for unshield flows) -- `FHE.allow(ctHash, address)` — only a specific address can request decryption -- `FHE.allowSender(ctHash)` — only `msg.sender` can request decryption +- `FHE.allowPublic(ctHash)`: anyone can request decryption (common for unshield flows) +- `FHE.allow(ctHash, address)`: only a specific address can request decryption +- `FHE.allowSender(ctHash)`: only `msg.sender` can request decryption ```solidity // Example: allow anyone to decrypt when the auction closes @@ -84,7 +84,7 @@ function closeBidding() external onlyAuctioneer { See [Access Control](/fhe-library/core-concepts/access-control) for the full list of permission methods, including `FHE.allowPublic()`. -### Step 2: Request decryption off-chain (client-side) +### Step 2: Request decryption offchain (client-side) The client calls `decryptForTx(ctHash)` to obtain the plaintext and a Threshold Network signature. Choose the permit mode that matches the contract's ACL policy: @@ -112,13 +112,13 @@ const decryptResult = await client `decryptForTx` always returns the plaintext as a `bigint`. Your contract determines whether that value is interpreted as `uint32`, `uint64`, etc. -### Step 3: Publish or verify on-chain +### Step 3: Publish or verify onchain Submit the plaintext and signature to your contract. You have two options: #### Option A: `FHE.publishDecryptResult` -Publishes the decrypted value on-chain, making it available for any contract to read. +Publishes the decrypted value onchain, making it available for any contract to read. ```solidity Solidity @@ -286,12 +286,12 @@ When a value is intended to become public (e.g. unshielding, auction reveals), u Ensure only authorized parties can request decryption. Use `FHE.allow()` or `FHE.allowSender()` for restricted access. See [Access Control](/fhe-library/core-concepts/access-control). - -Use `FHE.publishDecryptResult` when you want the result stored publicly on-chain. Use `FHE.verifyDecryptResult` when you only need to confirm the plaintext is authentic without publishing it. + +Use `FHE.publishDecryptResult` when you want the result stored publicly onchain. Use `FHE.verifyDecryptResult` when you only need to confirm the plaintext is authentic without publishing it. -If you only need to display a value in your UI and don't need an on-chain-verifiable signature, use `decryptForView` instead of `decryptForTx` to avoid unnecessary on-chain transactions. +If you only need to display a value in your UI and don't need an onchain-verifiable signature, use `decryptForView` instead of `decryptForTx` to avoid unnecessary onchain transactions. diff --git a/fhe-library/core-concepts/trivial-encryption.mdx b/fhe-library/core-concepts/trivial-encryption.mdx index dac3536..912c389 100644 --- a/fhe-library/core-concepts/trivial-encryption.mdx +++ b/fhe-library/core-concepts/trivial-encryption.mdx @@ -21,7 +21,7 @@ euint16 encrypted_number = FHE.asEuint16(number); This is a crucial concept to understand when developing confidential contracts. Any value that is trivially encrypted (e.g. `encrypted_number`) should be treated as public information, even though it uses the same data type as truly encrypted values. -`euints` are only confidential when they are formed from encrypted `inEuint` inputs, which are encrypted off-chain. Learn more in the [Data Evaluation](/fhe-library/core-concepts/data-evaluation) guide. +`euints` are only confidential when they are formed from encrypted `inEuint` inputs, which are encrypted offchain. Learn more in the [Data Evaluation](/fhe-library/core-concepts/data-evaluation) guide. When two trivially-encrypted numbers are combined in an FHE operation, the result is still not confidential, because an observer can keep track of the calculations. diff --git a/fhe-library/examples/auction-example.mdx b/fhe-library/examples/auction-example.mdx index 182acd7..81539c7 100644 --- a/fhe-library/examples/auction-example.mdx +++ b/fhe-library/examples/auction-example.mdx @@ -15,7 +15,7 @@ In this example, you'll see practical implementations of: - **Encrypted comparisons** - Finding the highest bid without revealing values - **Conditional logic with `select`** - Updating the highest bidder based on encrypted conditions - **Access control management** - Properly managing permissions for encrypted data -- **Decrypt-with-proof pattern** - Using `decryptForTx` off-chain and `publishDecryptResult` on-chain to reveal the winner +- **Decrypt-with-proof pattern** - Using `decryptForTx` offchain and `publishDecryptResult` onchain to reveal the winner --- @@ -36,12 +36,12 @@ Participants submit bids by sending plaintext amounts that are immediately encry The auctioneer closes the auction and calls `FHE.allowPublic` on the highest bid and bidder, making them eligible for public decryption. - -Anyone can call `decryptForTx` off-chain to obtain the plaintext values and Threshold Network signatures for the winning bid and bidder. + +Anyone can call `decryptForTx` offchain to obtain the plaintext values and Threshold Network signatures for the winning bid and bidder. -The decrypted values and signatures are submitted on-chain via `revealWinner`, which calls `FHE.publishDecryptResult` to verify the proofs and store the results. +The decrypted values and signatures are submitted onchain via `revealWinner`, which calls `FHE.publishDecryptResult` to verify the proofs and store the results. @@ -75,19 +75,19 @@ highestBidder = FHE.select(isHigher, newBidder, currentBidder); // Update bidde The contract demonstrates the new decryption flow: -**Step 1:** Close auction and allow public decryption (on-chain) +**Step 1:** Close auction and allow public decryption (onchain) ```solidity FHE.allowPublic(highestBid); FHE.allowPublic(highestBidder); ``` -**Step 2:** Request decryption off-chain (client-side) +**Step 2:** Request decryption offchain (client-side) ```typescript const bidResult = await client.decryptForTx(bidCtHash).withoutPermit().execute(); const bidderResult = await client.decryptForTx(bidderCtHash).withoutPermit().execute(); ``` -**Step 3:** Publish results on-chain with proof +**Step 3:** Publish results onchain with proof ```solidity FHE.publishDecryptResult(highestBid, plaintext, signature); FHE.publishDecryptResult(highestBidder, plaintextAddress, bidderSignature); @@ -265,11 +265,11 @@ function closeBidding() external onlyAuctioneer { } ``` -Since `FHE.allowPublic` is used, anyone can request decryption off-chain without needing a permit. The values are not revealed until someone submits the proof on-chain. +Since `FHE.allowPublic` is used, anyone can request decryption offchain without needing a permit. The values are not revealed until someone submits the proof onchain. ### Revealing the Winner -The `revealWinner` function accepts the decrypted values and their Threshold Network signatures, then publishes them on-chain: +The `revealWinner` function accepts the decrypted values and their Threshold Network signatures, then publishes them onchain: ```solidity function revealWinner( @@ -321,7 +321,7 @@ await auction.connect(bidder3).bid(1200); await auction.connect(auctioneer).closeBidding(); ``` -### 4. Decrypt Off-Chain and Reveal the Winner +### 4. Decrypt Offchain and Reveal the Winner ```typescript // Read the encrypted handles from the contract @@ -373,7 +373,7 @@ Every encrypted value created must have permissions granted via `FHE.allowThis() -Decryption happens off-chain via `decryptForTx`, and results are verified on-chain via `FHE.publishDecryptResult` with a Threshold Network signature. +Decryption happens offchain via `decryptForTx`, and results are verified onchain via `FHE.publishDecryptResult` with a Threshold Network signature. diff --git a/fhe-library/introduction/best-practices.mdx b/fhe-library/introduction/best-practices.mdx index c7cbf8a..2cb6bd1 100644 --- a/fhe-library/introduction/best-practices.mdx +++ b/fhe-library/introduction/best-practices.mdx @@ -19,16 +19,16 @@ Before reading this guide, you should: ### Publish Decrypted Data Carefully -Decryption is a multi-step process: the client requests a plaintext + signature off-chain via `decryptForTx`, then publishes or verifies the result on-chain using `FHE.publishDecryptResult` or `FHE.verifyDecryptResult`. Once published, the plaintext is visible to everyone on the blockchain. +Decryption is a multi-step process: the client requests a plaintext + signature offchain via `decryptForTx`, then publishes or verifies the result onchain using `FHE.publishDecryptResult` or `FHE.verifyDecryptResult`. Once published, the plaintext is visible to everyone on the blockchain. **Key principles:** -- **Evaluate information leakage**: Before publishing a decrypted value on-chain, consider what information you're exposing and what an observer might learn from it -- **Minimize published values**: Only publish decrypted results when your protocol truly requires the plaintext on-chain. Use `decryptForView` if you only need to display the value in a UI +- **Evaluate information leakage**: Before publishing a decrypted value onchain, consider what information you're exposing and what an observer might learn from it +- **Minimize published values**: Only publish decrypted results when your protocol truly requires the plaintext onchain. Use `decryptForView` if you only need to display the value in a UI - **Use `verifyDecryptResult` when possible**: If your contract only needs to confirm a value without storing it publicly, prefer `FHE.verifyDecryptResult` over `FHE.publishDecryptResult` -Publishing a decrypted value on-chain makes it permanently visible to all observers. Always consider whether you need `decryptForTx` (on-chain proof) or `decryptForView` (UI-only) for your use case. +Publishing a decrypted value onchain makes it permanently visible to all observers. Always consider whether you need `decryptForTx` (onchain proof) or `decryptForView` (UI-only) for your use case. ### Always update permissions @@ -88,11 +88,11 @@ FHE operations are computationally expensive. Optimize your contracts to minimiz - **Use the minimum bit-width necessary**: Choose the smallest integer type that can safely represent your data -```solidity ❌ Less Efficient +```solidity Less efficient euint64 counter; // Using 64 bits when 32 would suffice ``` -```solidity ✅ More Efficient +```solidity More efficient euint32 counter; // Using 32 bits when that's sufficient ``` @@ -155,7 +155,7 @@ const handleEncrypt = async () => { }; ``` -The `EncryptStep` enum values fire in order: `InitTfhe` → `FetchKeys` → `Pack` → `Prove` → `Verify`. +The `EncryptStep` enum values fire in order: `InitTfhe` to `FetchKeys` to `Pack` to `Prove` to `Verify`. CoFHE operations may take time to complete, especially on testnets. Always provide user feedback during these operations to improve user experience. diff --git a/fhe-library/introduction/quick-start.mdx b/fhe-library/introduction/quick-start.mdx index b25f229..0ea4dc6 100644 --- a/fhe-library/introduction/quick-start.mdx +++ b/fhe-library/introduction/quick-start.mdx @@ -170,7 +170,7 @@ it('Full Client SDK flow', async function () { ``` -### Deploying to Testnet +### Deploying to testnet When ready for more realistic testing, deploy to a Sepolia testnet: @@ -243,7 +243,7 @@ These contracts provide mock implementations for FHE functionality: - Allows testing without actual FHE operations - Simulates the behavior of the real FHE environment -- Stores plaintext values on-chain for testing purposes +- Stores plaintext values onchain for testing purposes In the mock environment, gas costs are higher than in production due to the additional operations needed to simulate FHE behavior. This is especially noticeable when logging is enabled. @@ -266,7 +266,7 @@ CoFHE supports multiple development environments: - No external dependencies - Uses mock contracts to simulate FHE operations -### Sepolia Testnet +### Sepolia testnet - Public testnet for real FHE operations - Requires ETH from the Sepolia faucet @@ -311,7 +311,7 @@ Always set proper permissions with `FHE.allowThis()` and `FHE.allowSender()` to ### Error Handling -Implement robust error handling for FHE operations. Be prepared for potential decryption delays and use appropriate retry mechanisms. +Handle errors from FHE operations explicitly. Be prepared for potential decryption delays and use appropriate retry mechanisms. ### Gas Optimization @@ -328,7 +328,7 @@ Now that you have your development environment set up, you can: ## Resources -- [Fhenix Documentation](https://docs.fhenix.zone) +- [Fhenix documentation](/) - [Client SDK GitHub](https://github.com/FhenixProtocol/cofhesdk) - [CoFHE Contracts GitHub](https://github.com/FhenixProtocol/cofhe-contracts) - [cofhe-hardhat-starter GitHub](https://github.com/fhenixprotocol/cofhe-hardhat-starter) diff --git a/fhe-library/reference/fhe-sol.mdx b/fhe-library/reference/fhe-sol.mdx index d0c123f..44828b8 100644 --- a/fhe-library/reference/fhe-sol.mdx +++ b/fhe-library/reference/fhe-sol.mdx @@ -1041,7 +1041,7 @@ if (valid) { ### publishDecryptResultBatch -Publishes multiple decrypted results on-chain in a single call. Each element is verified independently. +Publishes multiple decrypted results onchain in a single call. Each element is verified independently. Array of ciphertext handles @@ -1112,7 +1112,7 @@ Array of Threshold Network signatures -Array of booleans — `true` for each valid signature, `false` for invalid +Array of booleans. `true` for each valid signature, `false` for invalid ```solidity @@ -1161,7 +1161,7 @@ FHE.allowThis(counter); // Required for future access ### allowPublic -Grants public permission to access the encrypted value. Once called, anyone can request decryption of this value off-chain via `decryptForTx` without needing a permit. +Grants public permission to access the encrypted value. Once called, anyone can request decryption of this value offchain via `decryptForTx` without needing a permit. Encrypted value to grant public access to @@ -1383,7 +1383,7 @@ Always consider security implications when working with encrypted data. 1. **Initialization**: All FHE functions check if their inputs are initialized and set them to 0 if not. -2. **Decryption**: Decryption is a two-phase process — mark values with `allowPublic` on-chain, decrypt off-chain via the Client SDK, then publish/verify the result with `publishDecryptResult` or `verifyDecryptResult`. Only reveal values when absolutely necessary. +2. **Decryption**: Decryption is a two-phase process. Mark values with `allowPublic` onchain, decrypt offchain via the Client SDK, then publish/verify the result with `publishDecryptResult` or `verifyDecryptResult`. Only reveal values when absolutely necessary. 3. **Security Zones**: Some functions accept a `securityZone` parameter to isolate different encrypted computations. FHE operations can only be performed between ciphertexts that share the same security zone. diff --git a/fhe-library/reference/fhe-sol/access-control.mdx b/fhe-library/reference/fhe-sol/access-control.mdx index dcc5b44..0d13fe0 100644 --- a/fhe-library/reference/fhe-sol/access-control.mdx +++ b/fhe-library/reference/fhe-sol/access-control.mdx @@ -7,7 +7,7 @@ All access control functions work on `ebool | euint8 | euint16 | euint32 | euint --- -### allow +## allow Grants permission to a specific address. @@ -15,7 +15,7 @@ Grants permission to a specific address. FHE.allow(encryptedValue, userAddress); ``` -### allowThis +## allowThis Grants permission to the current contract (`address(this)`). @@ -28,9 +28,9 @@ euint32 counter = FHE.add(counter, FHE.asEuint32(1)); FHE.allowThis(counter); ``` -### allowPublic +## allowPublic -Grants public permission — anyone can request decryption off-chain via `decryptForTx` without a permit. +Grants public permission. Anyone can request decryption offchain via `decryptForTx` without a permit. Once called, the value can be decrypted by anyone. Only use this when you intend to reveal the value publicly (e.g., after an auction closes or when unwrapping tokens). @@ -40,7 +40,7 @@ Once called, the value can be decrypted by anyone. Only use this when you intend FHE.allowPublic(highestBid); ``` -### allowSender +## allowSender Grants permission to `msg.sender`. @@ -48,7 +48,7 @@ Grants permission to `msg.sender`. FHE.allowSender(encryptedValue); ``` -### allowTransient +## allowTransient Grants temporary permission to a specific address for the current transaction only. @@ -58,7 +58,7 @@ FHE.allowTransient(encryptedValue, otherContract); --- -### isAllowed +## isAllowed Checks if an address has permission. @@ -66,7 +66,7 @@ Checks if an address has permission. bool hasAccess = FHE.isAllowed(encryptedValue, userAddress); ``` -### isPubliclyAllowed +## isPubliclyAllowed Checks if the value has been granted public access via `allowPublic`. diff --git a/fhe-library/reference/fhe-sol/decryption.mdx b/fhe-library/reference/fhe-sol/decryption.mdx index 950b977..94a8460 100644 --- a/fhe-library/reference/fhe-sol/decryption.mdx +++ b/fhe-library/reference/fhe-sol/decryption.mdx @@ -1,13 +1,13 @@ --- title: "Decryption" -description: "publishDecryptResult, verifyDecryptResult, getDecryptResult — on-chain decryption result handling" +description: "publishDecryptResult, verifyDecryptResult, getDecryptResult. Onchain decryption result handling" --- Decryption in CoFHE is a two-phase process: -1. **On-chain**: Mark a value as decryptable with `FHE.allowPublic(ctHash)` -2. **Off-chain**: Client calls `decryptForTx(ctHash)` via the SDK to get `{ plaintext, signature }` -3. **On-chain**: Submit the result via `publishDecryptResult` or `verifyDecryptResult` +1. **Onchain**: Mark a value as decryptable with `FHE.allowPublic(ctHash)` +2. **Offchain**: Client calls `decryptForTx(ctHash)` via the SDK to get `{ plaintext, signature }` +3. **Onchain**: Submit the result via `publishDecryptResult` or `verifyDecryptResult` --- @@ -15,7 +15,7 @@ Decryption in CoFHE is a two-phase process: ### publishDecryptResult -Publishes a decrypted result on-chain by verifying the Threshold Network signature. The plaintext is stored and can be read via `getDecryptResultSafe`. +Publishes a decrypted result onchain by verifying the Threshold Network signature. The plaintext is stored and can be read via `getDecryptResultSafe`. Ciphertext handle @@ -30,7 +30,7 @@ Threshold Network signature -Reverts if the signature is invalid. The value must have been granted public access via `allowPublic` before decryption was requested off-chain. +Reverts if the signature is invalid. The value must have been granted public access via `allowPublic` before decryption was requested offchain. ```solidity @@ -77,7 +77,7 @@ The compiler resolves the overload from the handle array's element type. There i ### verifyDecryptResult -Verifies a Threshold Network signature **without** storing the plaintext on-chain. Returns `bool`. +Verifies a Threshold Network signature **without** storing the plaintext onchain. Returns `bool`. Use this when you only need to act on the decrypted value within the transaction without making it permanently public. @@ -103,7 +103,7 @@ if (valid) { ### verifyDecryptResultBatch -Verifies multiple signatures in a single call. Returns `true` only if every entry is valid; reverts if any signature fails to recover (consistent with the single-entry `verifyDecryptResult`). Typed overloads exist for every encrypted type — same handle/result type table as [`publishDecryptResultBatch`](#publishdecryptresultbatch). +Verifies multiple signatures in a single call. Returns `true` only if every entry is valid; reverts if any signature fails to recover (consistent with the single-entry `verifyDecryptResult`). Typed overloads exist for every encrypted type, same handle/result type table as [`publishDecryptResultBatch`](#publishdecryptresultbatch). ```solidity bool allValid = FHE.verifyDecryptResultBatch(handles, values, sigs); @@ -111,7 +111,7 @@ bool allValid = FHE.verifyDecryptResultBatch(handles, values, sigs); ### verifyDecryptResultBatchSafe -Returns a `bool[]` indicating which entries are valid instead of reverting. Typed overloads cover every encrypted type — same handle/result type table as [`publishDecryptResultBatch`](#publishdecryptresultbatch). +Returns a `bool[]` indicating which entries are valid instead of reverting. Typed overloads cover every encrypted type, same handle/result type table as [`publishDecryptResultBatch`](#publishdecryptresultbatch). ```solidity bool[] memory results = FHE.verifyDecryptResultBatchSafe(handles, values, sigs); diff --git a/fhe-library/reference/fhe-sol/overview.mdx b/fhe-library/reference/fhe-sol/overview.mdx index 22c78fe..47dfdf7 100644 --- a/fhe-library/reference/fhe-sol/overview.mdx +++ b/fhe-library/reference/fhe-sol/overview.mdx @@ -45,7 +45,7 @@ Each encrypted type has a corresponding input struct used when receiving encrypt 1. **Initialization**: All FHE functions check if their inputs are initialized and set them to 0 if not. -2. **Decryption**: Decryption is a two-phase process — mark values with `allowPublic` on-chain, decrypt off-chain via the Client SDK, then publish/verify the result with `publishDecryptResult` or `verifyDecryptResult`. Only reveal values when absolutely necessary. +2. **Decryption**: Decryption is a two-phase process. Mark values with `allowPublic` onchain, decrypt offchain via the Client SDK, then publish/verify the result with `publishDecryptResult` or `verifyDecryptResult`. Only reveal values when absolutely necessary. 3. **Security Zones**: Some functions accept a `securityZone` parameter to isolate different encrypted computations. FHE operations can only be performed between ciphertexts that share the same security zone. diff --git a/get-started/introduction/fhenix.mdx b/get-started/introduction/fhenix.mdx index 38044ef..7483690 100644 --- a/get-started/introduction/fhenix.mdx +++ b/get-started/introduction/fhenix.mdx @@ -7,11 +7,11 @@ description: "The missing infrastructure for Confidential DeFi" ## **Introduction** -Blockchains are great for transparency, security and trust, but that transparency comes at a cost—**everything is public**. Every transaction, smart contract interaction, and account balance is out in the open, which isn't ideal for things like finance, healthcare, or any use case that deal with sensitive data. +Blockchains are great for transparency, security and trust, but that transparency comes at a cost: **everything is public**. Every transaction, smart contract interaction, and account balance is out in the open, which isn't ideal for things like finance, healthcare, or any use case that deal with sensitive data. -**Fully Homomorphic Encryption (FHE) fixes this.** Instead of exposing raw data on-chain, FHE allows computations to happen **directly on encrypted data**. The blockchain never sees the actual inputs or outputs—only encrypted values—yet the results are still valid when decrypted by an authorized recipient. +**Fully Homomorphic Encryption (FHE) fixes this.** Instead of exposing raw data onchain, FHE allows computations to happen **directly on encrypted data**. The blockchain never sees the actual inputs or outputs, only encrypted values, yet the results are still valid when decrypted by an authorized recipient. -This means smart contracts can run just like they do now, but with **built-in confidentiality**—without compromising decentralization or security. +This means smart contracts can run just like they do now, but with **built-in confidentiality**, without compromising decentralization or security. @@ -20,7 +20,7 @@ This means smart contracts can run just like they do now, but with **built-in co ## **The Blockchain Transparency Problem** -Blockchain is often praised for its **decentralization, immutability, and transparency**—but transparency is a double-edged sword. +Blockchain is often praised for its **decentralization, immutability, and transparency**, but transparency is a double-edged sword. ### **Why Transparency is a Problem** @@ -28,9 +28,9 @@ Blockchain is often praised for its **decentralization, immutability, and transp In public blockchains like Ethereum, every transaction, smart contract interaction, and account balance is **completely visible** to anyone. This radical transparency, while crucial for establishing trust and enabling verification, creates significant privacy challenges. FHE solves this fundamental tradeoff by allowing data to remain **fully encrypted** while still maintaining the network's ability to verify its accuracy and authenticity. This means sensitive information can be processed and validated without ever being exposed, combining the best of both worlds - **bulletproof privacy with trustless verification**. **Real-world consequences of blockchain transparency:** \ - ✅ **Front-running & MEV** – Traders can analyze mempools and exploit pending transactions before they are executed. \ - ✅ **Confidentiality leaks** – Sensitive financial transactions, payroll information, or business logic are exposed. \ - ✅ **Enterprise adoption hurdles** – Companies are reluctant to use public blockchains if competitors can access proprietary data. +- **Front-running and MEV**: traders can analyze mempools and exploit pending transactions before they are executed. \ +- **Confidentiality leaks**: sensitive financial transactions, payroll information, or business logic are exposed. \ +- **Enterprise adoption hurdles**: companies are reluctant to use public blockchains if competitors can access proprietary data. These challenges can all be mitigated by using FHE in your smart contracts. @@ -40,7 +40,7 @@ These challenges can all be mitigated by using FHE in your smart contracts. ## **What is FHE?** -**FHE** is a cryptographic technique that allows computations to be performed on encrypted data **without decrypting it**. Most cryptographic techniques secure data only until it needs to be used—FHE keeps it hidden even while processing, preventing leaks at every step. +**FHE** is a cryptographic technique that allows computations to be performed on encrypted data **without decrypting it**. Most cryptographic techniques secure data only until it needs to be used. FHE keeps it hidden even while processing, preventing leaks at every step. ### **How FHE Works** diff --git a/get-started/introduction/what-is-cofhe.mdx b/get-started/introduction/what-is-cofhe.mdx index d4e487b..7e71b65 100644 --- a/get-started/introduction/what-is-cofhe.mdx +++ b/get-started/introduction/what-is-cofhe.mdx @@ -1,48 +1,48 @@ --- title: What is CoFHE? -description: "A high-level introduction to CoFHE — Fhenix's Fully Homomorphic Encryption coprocessor" +description: "A high-level introduction to CoFHE. Fhenix's Fully Homomorphic Encryption coprocessor" --- [FHE](/get-started/introduction/fhenix) explains *why* confidential smart contracts matter. **CoFHE is how Fhenix makes them practical.** **CoFHE is an FHE coprocessor that lets any blockchain run computations on encrypted data.** -This makes confidentiality just another Solidity feature. There's no migration to a specialized FHE chain, no new toolchain, and no cryptography to implement yourself. CoFHE handles the heavy FHE math offchain; your contract only ever touches lightweight *handles* to encrypted values, so code stays familiar. Values are encrypted — everything else feels like ordinary development. +This makes confidentiality just another Solidity feature. There's no migration to a specialized FHE chain, no new toolchain, and no cryptography to implement yourself. CoFHE handles the heavy FHE math offchain; your contract only ever touches lightweight *handles* to encrypted values, so code stays familiar. Values are encrypted. Everything else feels like ordinary development. ## Why a coprocessor? -The coprocessor model adds confidentiality without changing how applications are built — same Solidity, same chains, same tooling, with encrypted values as just another type to work with. +The coprocessor model adds confidentiality without changing how applications are built, same Solidity, same chains, same tooling, with encrypted values as just another type to work with. -- **No migration** — CoFHE attaches to existing blockchains. Confidential contracts deploy to the networks already in use, not a dedicated FHE L1. -- **Familiar code** — contracts pass around lightweight *handles* (references to ciphertexts) rather than the ciphertexts themselves, so they read like normal Solidity. FHE operations add some gas overhead, but the onchain footprint stays small and predictable. -- **No cryptography to implement** — the heavy FHE math runs offchain on the CoFHE server, which is built to do it efficiently. A contract calls `FHE.add`; CoFHE does the rest. -- **Trust-minimized by default** — decryption is never in one party's hands. A Threshold Network performs it through multi-party computation. +- **No migration**: CoFHE attaches to existing blockchains. Confidential contracts deploy to the networks already in use, not a dedicated FHE L1. +- **Familiar code**: contracts pass around lightweight *handles* (references to ciphertexts) rather than the ciphertexts themselves, so they read like normal Solidity. FHE operations add some gas overhead, but the onchain footprint stays small and predictable. +- **No cryptography to implement**: the heavy FHE math runs offchain on the CoFHE server, which is built to do it efficiently. A contract calls `FHE.add`; CoFHE does the rest. +- **Trust-minimized by default**: decryption is never in one party's hands. A Threshold Network performs it through multi-party computation. ## What CoFHE lets you build Because computation happens on encrypted values, you can build applications where sensitive data never appears in plaintext onchain: -- **Confidential balances and transfers** — token amounts and balances stay hidden while transfers still settle correctly. -- **Private state in contracts** — per-user values (counters, scores, bids, positions) that no one, not even the contract or CoFHE, can read in the clear. -- **Sealed inputs** — users submit encrypted inputs (votes, bids, orders) that are computed on without ever being revealed. -- **Selective disclosure** — results are decrypted only for authorized parties, gated by signed permits. +- **Confidential balances and transfers**: token amounts and balances stay hidden while transfers still settle correctly. +- **Private state in contracts**: per-user values (counters, scores, bids, positions) that no one, not even the contract or CoFHE, can read in the clear. +- **Sealed inputs**: users submit encrypted inputs (votes, bids, orders) that are computed on without ever being revealed. +- **Selective disclosure**: results are decrypted only for authorized parties, gated by signed permits. ## How it works -Every CoFHE application follows the same three-phase lifecycle: **encrypt → compute → decrypt.** +Every CoFHE application follows the same three-phase lifecycle: **encrypt to compute to decrypt.** -The user's plaintext is encrypted in the client using the [`@cofhe/sdk`](/client-sdk/introduction/overview), bundled with a zero-knowledge proof that the input is well-formed, and submitted to CoFHE. The blockchain only ever receives an encrypted handle — never the raw value. +The user's plaintext is encrypted in the client using the [`@cofhe/sdk`](/client-sdk/introduction/overview), bundled with a zero-knowledge proof that the input is well-formed, and submitted to CoFHE. The blockchain only ever receives an encrypted handle, never the raw value. -The smart contract uses [`FHE.sol`](/fhe-library/introduction/overview) to operate on encrypted handles — adding, comparing, selecting — as if they were ordinary numbers. Each operation deterministically derives a new result handle and is recorded onchain; the CoFHE server independently computes the matching ciphertext offchain. Nothing returns to the contract, and plaintext is never exposed at any point. +The smart contract uses [`FHE.sol`](/fhe-library/introduction/overview) to operate on encrypted handles, adding, comparing, selecting, as if they were ordinary numbers. Each operation deterministically derives a new result handle and is recorded onchain; the CoFHE server independently computes the matching ciphertext offchain. Nothing returns to the contract, and plaintext is never exposed at any point. -When an authorized user wants a result, they present a signed [permit](/client-sdk/guides/permits). The Threshold Network decrypts via multi-party computation — either re-encrypting the value so only that user can read it (for display), or returning a verifiable plaintext with a signature (for onchain use). +When an authorized user wants a result, they present a signed [permit](/client-sdk/guides/permits). The Threshold Network decrypts via multi-party computation, either re-encrypting the value so only that user can read it (for display), or returning a verifiable plaintext with a signature (for onchain use). @@ -90,26 +90,26 @@ Developers only interact directly with **two** parts of CoFHE; the rest runs beh | --- | --- | | **Task Manager** | Onchain gateway that validates FHE requests and enforces access control | | **Slim Listener** | Watches onchain events and forwards operations to the offchain layer | -| **FHEOS Server** | Executes the actual FHE computations and holds encrypted state | +| **FheOS Server** | Executes the actual FHE computations and holds encrypted state | | **Result Processor** | Publishes verified results back onchain | -| **Threshold Network** | Decrypts via multi-party computation — no single party holds the key | +| **Threshold Network** | Decrypts via multi-party computation, no single party holds the key | | **Registries** | Track ciphertexts and record result commitments so integrity can be verified before any decryption | For a component-by-component breakdown, see the [CoFHE Architecture deep dive](/deep-dive/cofhe-components/overview). ## How CoFHE keeps data safe -- **Encrypted end-to-end** — values are encrypted client-side and stay encrypted through computation; only handles touch the chain. -- **Verified inputs** — zero-knowledge proofs ensure every encrypted input is well-formed before it enters the system. -- **Verified results** — the coprocessor commits to each result onchain, and the Threshold Network checks integrity before it will decrypt anything. -- **No single point of trust for decryption** — decryption requires the Threshold Network's multi-party computation, gated by signed permits. +- **Encrypted end-to-end**: values are encrypted client-side and stay encrypted through computation; only handles touch the chain. +- **Verified inputs**: zero-knowledge proofs ensure every encrypted input is well-formed before it enters the system. +- **Verified results**: the coprocessor commits to each result onchain, and the Threshold Network checks integrity before it will decrypt anything. +- **No single point of trust for decryption**: decryption requires the Threshold Network's multi-party computation, gated by signed permits. ## Next steps - Walk through encrypt → compute → decrypt with a concrete Counter example. + Walk through encrypt to compute to decrypt with a concrete Counter example. diff --git a/tutorials/acl-usage-examples.mdx b/tutorials/acl-usage-examples.mdx index 1c726be..a4124ec 100644 --- a/tutorials/acl-usage-examples.mdx +++ b/tutorials/acl-usage-examples.mdx @@ -65,7 +65,7 @@ If you don't call `FHE.allowThis()` after modifying encrypted values, you won't ## Allowance for Decryptions -To decrypt a ciphertext off-chain via the decryption network, the issuer must be allowed on the ciphertext handle via `FHE.allow(userAddress)`. +To decrypt a ciphertext offchain via the decryption network, the issuer must be allowed on the ciphertext handle via `FHE.allow(userAddress)`. ```solidity contract A { @@ -88,7 +88,7 @@ contract A { ``` -When allowing users to decrypt their own encrypted values, use `FHE.allow()` to grant persistent access. This enables users to decrypt values off-chain using `decryptForView` without requiring additional transactions. +When allowing users to decrypt their own encrypted values, use `FHE.allow()` to grant persistent access. This enables users to decrypt values offchain using `decryptForView` without requiring additional transactions. ## Allow Other Contracts @@ -165,7 +165,7 @@ After modifying any encrypted state variable, call `FHE.allowThis()` to ensure t -If users need to decrypt their own values off-chain, use `FHE.allow()` or `FHE.allowSender()` to grant them access. +If users need to decrypt their own values offchain, use `FHE.allow()` or `FHE.allowSender()` to grant them access. diff --git a/tutorials/adding-fhe-to-existing-contract.mdx b/tutorials/adding-fhe-to-existing-contract.mdx index be23f39..43aa887 100644 --- a/tutorials/adding-fhe-to-existing-contract.mdx +++ b/tutorials/adding-fhe-to-existing-contract.mdx @@ -248,7 +248,7 @@ function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external { ### Step 5: Constant time computation -In order to preserve the confidentiality of the user's vote, we must make sure that we aren't leaking any information about the user's choice. If we only updated the voting option that the user has selected, then a user's vote could be deduced by simply watching which vote counter changes. Therefore, we must update _all_ the vote counters to hide the user's true vote: +To preserve the confidentiality of the user's vote, we must make sure that we aren't leaking any information about the user's choice. If we only updated the voting option that the user has selected, then a user's vote could be deduced by simply watching which vote counter changes. Therefore, we must update _all_ the vote counters to hide the user's true vote: ```solidity function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external { @@ -369,7 +369,7 @@ function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external { ``` -It is critical to ensure that `FHE.allowThis` is used on encrypted variables that need to be used later in the contract's lifecycle. Contracts must have access to variables in order to perform FHE operations on those variables. +It is critical to ensure that `FHE.allowThis` is used on encrypted variables that need to be used later in the contract's lifecycle. Contracts must have access to variables to perform FHE operations on them. ### Step 9: Finalize the voting with `FHE.allowPublic` @@ -390,11 +390,11 @@ function finalizeVote(uint256 _proposalId) external { } ``` -`FHE.allowPublic` marks each vote count as eligible for public decryption. Anyone can now request the plaintext values off-chain. +`FHE.allowPublic` marks each vote count as eligible for public decryption. Anyone can now request the plaintext values offchain. ### Step 10: Reveal the results with `FHE.publishDecryptResult` -After `finalizeVote` is called, the vote counts need to be decrypted off-chain and published on-chain with proof. We add a `revealResults` function that accepts the decrypted values and their Threshold Network signatures: +After `finalizeVote` is called, the vote counts need to be decrypted offchain and published onchain with proof. We add a `revealResults` function that accepts the decrypted values and their Threshold Network signatures: ```solidity function revealResults( @@ -515,10 +515,10 @@ In this tutorial, we walked through migrating a simple voting contract to use Co 1. Changing the vote counts from plain `uint64` to encrypted values using `euint64` 2. Modifying the voting function to use encrypted addition instead of plain addition 3. Using `FHE.allowPublic` to mark values as decryptable after the voting deadline -4. Adding a `revealResults` function that publishes decrypted values on-chain with `FHE.publishDecryptResult` +4. Adding a `revealResults` function that publishes decrypted values onchain with `FHE.publishDecryptResult` 5. Updating the getter function to handle decryption of results safely -The resulting contract provides the same functionality as the original, but with the added privacy benefit that individual votes are not visible on-chain until the final tally is decrypted. This demonstrates how CoFHE can be used to add privacy to existing contracts with minimal changes to the core logic. +The resulting contract provides the same functionality as the original, but with the added privacy benefit that individual votes are not visible onchain until the final tally is decrypted. This demonstrates how CoFHE can be used to add privacy to existing contracts with minimal changes to the core logic. ## Final `FHEVotingExample.sol` diff --git a/tutorials/migrating-from-fhe-decrypt.mdx b/tutorials/migrating-from-fhe-decrypt.mdx index d83090b..55908de 100644 --- a/tutorials/migrating-from-fhe-decrypt.mdx +++ b/tutorials/migrating-from-fhe-decrypt.mdx @@ -5,7 +5,7 @@ description: "Step-by-step guide for migrating Solidity contracts from FHE.decry ## Overview -The old decryption pattern used `FHE.decrypt(ctHash)` to trigger an asynchronous decryption, followed by `FHE.getDecryptResultSafe(ctHash)` to read the result once available. The new pattern replaces `FHE.decrypt` with an off-chain decryption step using the Client SDK, and uses `FHE.publishDecryptResult` to submit the result on-chain with a cryptographic proof. +The old decryption pattern used `FHE.decrypt(ctHash)` to trigger an asynchronous decryption, followed by `FHE.getDecryptResultSafe(ctHash)` to read the result once available. The new pattern replaces `FHE.decrypt` with an offchain decryption step using the Client SDK, and uses `FHE.publishDecryptResult` to submit the result onchain with a cryptographic proof. This guide walks through concrete before/after Solidity examples. @@ -13,13 +13,13 @@ This guide walks through concrete before/after Solidity examples. | | Old pattern | New pattern | |---|---|---| -| **Trigger decrypt** | `FHE.decrypt(ctHash)` (on-chain) | `FHE.allowPublic(ctHash)` (on-chain) + `client.decryptForTx(ctHash)` (off-chain) | +| **Trigger decrypt** | `FHE.decrypt(ctHash)` (onchain) | `FHE.allowPublic(ctHash)` (onchain) + `client.decryptForTx(ctHash)` (offchain) | | **Submit result** | Automatic (async, no proof) | `FHE.publishDecryptResult(ctHash, plaintext, signature)` | | **Verify only** | N/A | `FHE.verifyDecryptResult(ctHash, plaintext, signature)` | | **Read result** | `FHE.getDecryptResultSafe(ctHash)` | `FHE.getDecryptResultSafe(ctHash)` (same) | -The key difference: `FHE.decrypt` triggered decryption without any proof. The new flow requires a Threshold Network signature, ensuring the plaintext is cryptographically verified before being used on-chain. +The key difference: `FHE.decrypt` triggered decryption without any proof. The new flow requires a Threshold Network signature, ensuring the plaintext is cryptographically verified before being used onchain. --- @@ -27,7 +27,7 @@ The key difference: `FHE.decrypt` triggered decryption without any proof. The ne ## The New Decryption Flow - + Instead of calling `FHE.decrypt()`, mark the value as decryptable: ```solidity @@ -39,7 +39,7 @@ FHE.allow(encryptedValue, authorizedAddress); ``` - + The client requests decryption from the Threshold Network, which returns the plaintext and a signature: ```typescript @@ -53,7 +53,7 @@ const result = await client ``` - + The decrypted value and signature are submitted to your contract: ```solidity @@ -215,9 +215,9 @@ function claimUnshielded( **Key differences:** -- `FHE.decrypt(burned)` → `FHE.allowPublic(burned)` — no on-chain decryption is triggered, just a permission grant -- `claimUnshielded(bytes32 ctHash)` → `claimUnshielded(bytes32 ctHash, uint64 decryptedAmount, bytes signature)` — the caller now provides the decrypted value + proof -- `FHE.getDecryptResultSafe` → `FHE.publishDecryptResult` — the contract verifies the Threshold Network signature instead of polling for a result +- `FHE.decrypt(burned)` to `FHE.allowPublic(burned)`: no onchain decryption is triggered, just a permission grant +- `claimUnshielded(bytes32 ctHash)` to `claimUnshielded(bytes32 ctHash, uint64 decryptedAmount, bytes signature)`: the caller now provides the decrypted value + proof +- `FHE.getDecryptResultSafe` to `FHE.publishDecryptResult`: the contract verifies the Threshold Network signature instead of polling for a result **Client-side flow (new):** @@ -295,7 +295,7 @@ function getResult() external view returns (uint256) { ## `publishDecryptResult` vs `verifyDecryptResult` -| Method | Stores result on-chain | Others can read it | Use case | +| Method | Stores result onchain | Others can read it | Use case | |---|---|---|---| | `publishDecryptResult` | Yes | Yes, via `getDecryptResultSafe` | Revealing results publicly (auctions, votes, counters) | | `verifyDecryptResult` | No | No | One-time verification (transfers, burns) | @@ -336,7 +336,7 @@ Create a new function that accepts `(plaintext, signature)` parameters and calls -Add the off-chain decryption step using `client.decryptForTx()` between the two on-chain calls. +Add the offchain decryption step using `client.decryptForTx()` between the two onchain calls. diff --git a/tutorials/your-first-fhe-contract.mdx b/tutorials/your-first-fhe-contract.mdx index 10a6d21..3de6564 100644 --- a/tutorials/your-first-fhe-contract.mdx +++ b/tutorials/your-first-fhe-contract.mdx @@ -157,9 +157,9 @@ This value is an encrypted value that we created client-side using the SDK (read ### Decryption: Allow Public and Reveal -Decryption follows a two-step on-chain pattern, with an off-chain step in between. +Decryption follows a two-step onchain pattern, with an offchain step in between. -**Step 1: Allow public decryption (on-chain)** +**Step 1: Allow public decryption (onchain)** The owner calls `allow_counter_publicly` to mark the counter as eligible for public decryption: @@ -169,9 +169,9 @@ function allow_counter_publicly() external onlyOwner { } ``` -**Step 2: Decrypt off-chain** +**Step 2: Decrypt offchain** -Anyone can now request decryption off-chain using `decryptForTx`, which returns the plaintext value and a Threshold Network signature: +Anyone can now request decryption offchain using `decryptForTx`, which returns the plaintext value and a Threshold Network signature: ```typescript const countCtHash = await counter.counter(); @@ -182,9 +182,9 @@ const result = await client .execute(); ``` -**Step 3: Publish on-chain with proof** +**Step 3: Publish onchain with proof** -The decrypted value and signature are submitted on-chain. `FHE.publishDecryptResult` verifies the signature and stores the plaintext — if the signature is invalid, the transaction reverts: +The decrypted value and signature are submitted onchain. `FHE.publishDecryptResult` verifies the signature and stores the plaintext. If the signature is invalid, the transaction reverts: ```solidity function reveal_counter(uint64 _decrypted, bytes memory _signature) external { @@ -210,13 +210,13 @@ If the result has not been published yet, the function reverts. Otherwise, it re ## Privacy Considerations -In this contract, only the owner can allow public decryption. Once `reveal_counter` is called, the plaintext value is published on-chain and visible to everyone. +In this contract, only the owner can allow public decryption. Once `reveal_counter` is called, the plaintext value is published onchain and visible to everyone. What if we want to allow the owner to privately read the value without revealing it publicly? For that, we need to add a call for `FHE.allow(counter, owner)` or `FHE.allowSender(counter)` every time that we change the counter's value. -This will allow the owner to read the encrypted counter's value using the `get_encrypted_counter_value` function and decrypt it privately off-chain using `decryptForView`: +This will allow the owner to read the encrypted counter's value using the `get_encrypted_counter_value` function and decrypt it privately offchain using `decryptForView`: ```solidity function increment_counter() external onlyOwner {