Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
c7df096
Adding cookbook
naiemk-coti May 4, 2026
8a15d96
Adding toc to the pod-cookbook
naiemk-coti May 5, 2026
667e31d
Merge branch 'main' into main
naiemk-coti May 5, 2026
344f038
GITBOOK-104: No subject
gmesika-coti May 6, 2026
04d0529
Update COTI Node Ecosystem documentation
gmesika-coti May 13, 2026
38e6b61
Refactor COTI Node Ecosystem documentation
gmesika-coti May 13, 2026
16741ad
Enhance COTI Node Ecosystem documentation
gmesika-coti May 13, 2026
5f2c9ef
Update COTI Node Ecosystem documentation to reflect testnet URLs
gmesika-coti May 13, 2026
7111575
GITBOOK-106: No subject
gmesika-coti May 13, 2026
46b7258
GITBOOK-107: No subject
gmesika-coti May 13, 2026
71009e7
WIP on main
gmesika-coti May 13, 2026
f86c06c
Enhance COTI Node Ecosystem documentation
gmesika-coti May 13, 2026
c45f2f6
Refine COTI Node Ecosystem documentation
gmesika-coti May 13, 2026
7efb617
Add new images and enhance FQDN setup documentation
gmesika-coti May 13, 2026
8966e93
Update eligibility criteria in documentation and image
gmesika-coti May 13, 2026
43aaa01
Update dashboard images and enhance README content
gmesika-coti May 13, 2026
055c6b7
Add new image for Edit Node flow and update documentation
gmesika-coti May 13, 2026
6712d58
Update COTI Node Ecosystem documentation for certified OS requirements
gmesika-coti May 13, 2026
70e947a
Update installation documentation to standardize command options
gmesika-coti May 13, 2026
1e9dccc
docs: add Private Messaging Quickstart for sending and receiving mess…
vladi-coti May 12, 2026
1115d97
docs: add Private Messaging Dogfood Report and update related documen…
vladi-coti May 12, 2026
75ab101
docs: update quickstart guide for private messaging to simplify walle…
vladi-coti May 12, 2026
d631a45
docs: enhance quickstart guide for private messaging with additional …
vladi-coti May 12, 2026
fd584a5
docs: add installable private messaging skills
vladi-coti May 13, 2026
bcfd2c2
docs(private-messaging): one-line bootstrap path
vladi-coti May 14, 2026
95bbbc3
docs: add Smart Contract Audit Report for Coti
gmesika-coti May 18, 2026
a10072d
GITBOOK-108: No subject
gmesika-coti May 18, 2026
db3c156
GITBOOK-109: No subject
gmesika-coti May 18, 2026
f3209c0
GITBOOK-110: No subject
gmesika-coti May 18, 2026
4091544
GITBOOK-111: No subject
gmesika-coti May 18, 2026
351e842
docs: reorganize Support and Community section in SUMMARY.md
gmesika-coti May 20, 2026
01921b3
docs: update COTI Snap setup guide with new last updated date and cla…
gmesika-coti May 20, 2026
8546a90
docs: update installation guides to reflect changes in command flags …
gmesika-coti Jun 1, 2026
7fe4086
docs: clarify FRPC and Nginx configurations in installation guides, e…
gmesika-coti Jun 1, 2026
e548702
docs: update server requirements and installation guides to reflect U…
gmesika-coti Jun 2, 2026
3fa97ac
docs: enhance installation guides with detailed commands for Linux, m…
gmesika-coti Jun 2, 2026
278c73e
GITBOOK-112: No subject
gmesika-coti Jun 2, 2026
a92beb4
docs: update installation guides to standardize `<network>` placehold…
gmesika-coti Jun 2, 2026
726721e
docs: expand manual-full-node.md with detailed instructions for setti…
gmesika-coti Jun 2, 2026
44b9548
docs: add comprehensive documentation for private messaging, includin…
vladi-coti Jun 3, 2026
d4e3f27
docs: enhance private messaging documentation with new guidelines for…
vladi-coti Jun 7, 2026
1394446
Merge branch 'coti-io:main' into main
naiemk-coti Jun 15, 2026
56cda9c
Update PoD with latest MpcCore
naiemk-coti Jun 15, 2026
527df35
Merge branch 'coti-io:main' into main
naiemk-coti Jun 30, 2026
275bc78
docs: add COTI Wallet Plugin documentation, including integration gui…
gmesika-coti Jul 10, 2026
d3b68d3
docs: update COTI Wallet Plugin API reference and README to clarify u…
gmesika-coti Jul 12, 2026
c2743a7
docs: enhance COTI Wallet Plugin onboarding documentation with detail…
gmesika-coti Jul 13, 2026
18876ea
docs: add COTI Testnet and Avalanche Fuji network documentation, deta…
gmesika-coti Jul 14, 2026
2a969e5
docs: add comprehensive documentation for Privacy on Avalanche, inclu…
gmesika-coti Jul 14, 2026
47de584
docs: add end-user onboarding explainer
vladi-coti Jul 19, 2026
2c64997
docs: document Inbox system errors and Privacy Portal recovery
naiemk-coti Jul 19, 2026
ee550ca
Update docs after audit fixes
naiemk-coti Jul 23, 2026
ffc5e7e
Merge remote-tracking branch 'coti-io/main' into naiem/updates
naiemk-coti Jul 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions coti-privacy-portal/developer-guide/frontend-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,15 @@ async function encryptedApprove(
return txHash;
}
```

## Polling failed PoD / Privacy Portal requests

When a pToken request leaves `Pending`, read `requests(requestId).status` and decode `failedRequests(requestId)`:

1. If status is `SystemFailed` (or `failedRequests` ABI-decodes to Inbox `{ErrorData}` with `errorCode == 2`), show a system-error message. Do not retry the same request on COTI.
2. For portal deposits, call `refundFailedDeposit(mintRequestId)` **only** after `SystemFailed`. App `raise` / `Failed` is not refundable.
3. For portal withdrawals, call `cancelFailedWithdrawal(withdrawalId)` after the transfer request is `Failed` or `SystemFailed`.
4. If the mint is still `Pending` but COTI Inbox `errors[id].errorCode == 1`, show an **execution failure** and offer / await **`retryFailedRequest`** (permissionless). Use **`getOutboxError(id)`** for the capped returndata and decode in the client. Do **not** refund while a retry can still mint.
5. After `createPortal`, poll mother `isRegistered(sourceChainId, pToken)` before enabling deposits. A prior `FactoryNotAllowed` registration is recovered with allowlist + `retryFailedRequest`, not by assuming the portal is ready.

For offline fee accounting, index `PrivacyPortal.OperationFeesPaid`: `portalFee` (protocol, retained), `podFee` (forwarded to inbox), and `podCallbackFee` (callback slice). `isDeposit` / `isNativeWrap` distinguish the operation.
25 changes: 25 additions & 0 deletions coti-privacy-portal/developer-guide/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,30 @@
| `AES key mismatch` | Wrong AES key used to decrypt | Re-onboard wallet to get correct key |
| `invalid BytesLike value` | Passing HardhatEthersSigner to `prepareIT256` | Use `new ethers.Wallet(privateKey, provider)` instead |
| `PRIVATE_AES_KEY_TESTNET not set` | Missing env var | Add 32 hex char key to `.env` (no `0x` prefix) |
| Deposit stuck `Pending` forever | Several distinct causes (see below) | Diagnose with pToken `requests` / `failedRequests` and Inbox `errors` / `getOutboxError` |
| Withdraw stuck `TransferPending` | Transfer-to-portal request Failed | Call `PrivacyPortal.cancelFailedWithdrawal(withdrawalId)`; do not expect underlying release |
| Encode/`validateCiphertext` fail (`failedRequests` as `{ErrorData}` with `errorCode == 2`) | Wrong `it*` signer / encode fail | Clear UI pending; submit a **new** op (not COTI `retryFailedRequest`) |
| Mother registration / `FactoryNotAllowed` | Factory not allowlisted on COTI mother before registration mined | Allowlist factory (`setAllowedFactory`), then permissionless COTI `retryFailedRequest` for the failed registration id |
| Deposits before mother confirms | Portal created but mother `isRegistered` still false | Keep deposits disabled / pause until mother registration confirms |

### Stuck `Pending` deposits — diagnose before refunding

A deposit mint can remain **`Pending`** for different reasons. Do **not** assume every stuck mint is a system-failed encode:

| Observation | Likely cause | Action |
| --- | --- | --- |
| `requests(id).status == SystemFailed` / `failedRequests` decodes to Inbox `{ErrorData}` code `2` | Encode / `validateCiphertext` failed before COTI mint logic | Clear UI pending; call `refundFailedDeposit` (permissionless) after SystemFailed |
| COTI Inbox `errors[id].errorCode == 1` | Target execution reverted (retryable) | Call permissionless `retryFailedRequest` on COTI; do **not** refund while mint may still succeed |
| No COTI incoming request / never mined | Relayer lag or miner not ingesting | Wait for miner; do not treat as SystemFailed |
| Mother never registered / factory not allowlisted | Registration one-way failed or never confirmed | Fix allowlist + `retryFailedRequest`; keep user deposits off until `isRegistered` |

**Break-glass:** some deployments expose an admin refund for still-`Pending` escrows. That is operationally dangerous if COTI mint can still succeed afterward—prefer SystemFailed refunds and retries.

### Privacy Portal recovery

* **System-failed deposit (mint):** underlying stays escrowed until anyone calls `refundFailedDeposit(mintRequestId)` after `pToken.requests(id).status == SystemFailed` (funds always return to the depositor). App `raise` / `Failed` is **not** refundable (mint should not raise). Portal protocol fee is kept.
* **Failed withdraw (transfer):** after the transfer request is `Failed` or `SystemFailed`, call `cancelFailedWithdrawal(withdrawalId)` to mark the withdrawal `Failed`. Underlying is **not** released; the user still holds pTokens; portal fee is kept.
* **Factory → mother registration:** `createPortal` **submits** a one-way registration message; it does **not** wait for the mother to confirm. Keep deposits disabled until `PodErc20CotiMother.isRegistered(sourceChainId, pToken)` is true. If registration failed with `FactoryNotAllowed` before allowlist, allowlist the factory then call COTI `retryFailedRequest`.

### Security Notes

Expand All @@ -20,3 +44,4 @@
* Self-transfers (`from == to`) are explicitly blocked at the contract level.
* `transferAndCall` is protected by `nonReentrant` but the callback contract must be trusted.
* `totalSupply()` always returns `0` for privacy — do not rely on it for supply accounting.
* Inbox `executed` / compact response events mean the return leg was **received**, not that your app callback committed—confirm via pToken / portal status.
2 changes: 1 addition & 1 deletion privacy-on-demand/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Privacy on Demand lets applications use **strong privacy for data and computation** while still using **ordinary EVM chains** (Ethereum, L2s, and other compatible networks) for accounts, assets, and business workflows.

> **Development status:** This Privacy on Demand material and the **COTI PoD SDK** it describes are **under active development**. Treat them accordingly: on-chain and client code **may not yet be fully audited**, and **breaking changes** (APIs, ABIs, addresses, presets, or documentation) can occur as the stack matures. Pin versions, follow release notes, and perform your own review before relying on anything in production.
> **Development status:** This Privacy on Demand material and the **COTI PoD SDK** it describes are **under active development**. Treat them accordingly: pin versions, follow release notes, and perform your own review before relying on anything in production. External audits for related COTI components are listed under [Audit Reports](../security/audit-reports.md); the PoD Inbox and Privacy Portal stacks also received an **internal security review** with documented hardening (see that page). **Breaking changes** (APIs, ABIs, addresses, presets, or documentation) can still occur as the stack matures.

<div style="font-size: 1.45rem; line-height: 1.72; margin-bottom: 2rem;">

Expand Down
4 changes: 3 additions & 1 deletion privacy-on-demand/architecture-and-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ A fuller table lives in the SDK’s [data types](https://github.com/cotitech-io/
## Trust and security highlights (for architects)

- **Callback authentication**: Your contract should only accept **Inbox-originated** callbacks for private results—otherwise anyone could try to spoof answers. The SDK’s `onlyInbox` pattern exists for this boundary ([features](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/03-features.md)).
- **Request correlation**: Private work completes **later**; your system must track **request IDs** and statuses honestly in UX and backends ([Async private operations](async-private-operations.md)).
- **Trusted miner / relayer**: Cross-domain delivery is operated by **registered Inbox miners**. Payload authenticity for private results still rests on **`onlyInbox` callbacks** and your application’s request-status accounting—not on an on-chain proof of the remote execution transcript. Treat miner set ownership (multisig / timelock) as part of the threat model.
- **Request correlation**: Private work completes **later**; your system must track **request IDs** and statuses honestly in UX and backends ([Async private operations](async-private-operations.md)). Do **not** treat Inbox `executed` / compact response events alone as proof that your callback committed.
- **Failure surfaces**: Distinguish **system error** (code `2`, not retryable), **app `raise`**, and **execution failure** (code `1`, permissionless `retryFailedRequest`). Use **`getOutboxError`** for the capped returndata bytes (decode in the client).
- **Key stewardship**: Client-side AES material is powerful; treat it like **credentials**, not analytics metadata ([TypeScript integration](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/06-typescript-integration-ux-development.md)).

## Next steps
Expand Down
36 changes: 36 additions & 0 deletions privacy-on-demand/async-private-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,42 @@ Private execution happens **outside** your chain’s normal synchronous EVM fram

The SDK’s [Async execution](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/05a-async-execution.md) page lists the canonical lifecycle and common mistakes (wrong decode shape, missing `onlyInbox`, expecting same-block completion).

### System errors vs application `raise`

Both are delivered to the **same** source `errorSelector(bytes data)` (same path as `inbox.raise`). Branch with `inbox.inboxErrorType()` (`SystemError` vs `Exception`).

| Kind | When | COTI target ran? | `data` layout | Retryable via `retryFailedRequest`? |
| --- | --- | --- | --- | --- |
| **System error** | Encode / `validateCiphertext` fails before the COTI app runs | No | Inbox `{ErrorData}`: `abi.encode(uint64 code, bytes message)` (code `2`). Sender is `SYSTEM_SENDER` | **No** |
| **App `raise`** | COTI app calls `inbox.raise(...)` | Yes (started) | **dApp-defined** (e.g. `abi.encode(from, to, reason)`) | Submit a **new** request after pending clears |
| **Execution failure** | Target reverts without `raise` (code `1`) | Yes | No automatic source callback; error stored on COTI Inbox | **Yes** on COTI (permissionless) |

**Handler pattern** (see PodERC20 error callbacks):

1. `onlyInbox`; `_errorCallbackContext()` **reverts** unless `inboxErrorType()` is `SystemError`/`Exception`, `sourceRequestId` is linked, and status is Pending.
2. Branch on type: `SystemError` → decode Inbox `{ErrorData}`; `Exception` → decode your app `raise` layout.

### One-way vs two-way error handling

- **`sendOneWayMessage` rejects a non-zero `errorSelector`.** One-way jobs have no return / error callback leg. If you need an `errorSelector` handler, use a **two-way** message.
- System-error and app-`raise` callbacks therefore apply to **two-way** flows that registered an `errorSelector`.

### Execution failure, capped returndata, and `getOutboxError`

When the COTI target reverts without `raise`, the miner records **error code `1`** (`ERROR_CODE_EXECUTION_FAILED`) and stores the first ≤**256** bytes of returndata in `errors[requestId].errorMessage`.

- **`getOutboxError(requestId)`** returns `(code, data)` where `data` is those same raw bytes. Decode `Error(string)` / custom errors in your client (JS/TS).
- If `data.length == 256`, the original returndata may have been longer (cap truncated it).

Anyone may call permissionless **`retryFailedRequest(requestId)`** on COTI while the stored code is still `1`. A retry that fails to **re-encode** the call **reverts** and **keeps** code `1` (it does not flip the request to encode-failed / code `2`).

### What `executed` and response events mean

Inbox flags such as **`executed`** on an incoming request, and compact events such as **`IncomingResponseReceived`**, mean the **return / error leg was ingested** by the Inbox—not that your application callback **committed** successfully.

- A return leg can still leave a **retryable** execution error (`errors[id]` with code `1`) if the callback reverted.
- Product and indexers should treat **application events / request status** (for example pToken `requests(id).status`) as the source of truth for user-visible success or failure—not Inbox `executed` alone.

## What product and support teams should plan for

| Topic | Recommendation |
Expand Down
20 changes: 14 additions & 6 deletions privacy-on-demand/cookbook-private-investor-allocations.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ The public version is useful because it gives you a known baseline: owner assign
- A Solidity toolchain such as Hardhat or Foundry.
- A Sepolia wallet with test ETH for deploys, transactions, and PoD request fees.
- Node.js 18+ for scripts.
- The PoD SDK package: `npm install "@coti/pod-sdk"`.
- The PoD SDK package: `npm install "@coti/pod-sdk"` (ships the vendored `MpcCore.sol` under `@coti/pod-sdk/contracts/utils/mpc/`, so the COTI‑side contract no longer needs the `@coti-io/coti-contracts` package).
- The COTI client crypto package: `npm install "@coti-io/coti-sdk-typescript@^1.0.7"` (provides `decryptUint256({ ciphertextHigh, ciphertextLow }, key)` for the 256‑bit ciphertext shape).
- A way for users to complete PoD onboarding and obtain their account AES key for local decryption.

Before implementing the private version, read:
Expand Down Expand Up @@ -411,21 +412,26 @@ const encryptedAllocation = await CotiPodCrypto.encrypt(

If you use `PodContract.encryptAndCallMethod`, you can pass the plaintext string plus `DataType.itUint256`; the SDK encrypts and encodes the argument before sending the transaction. If the browser or backend already encrypted the value, use `callMethod` with the ciphertext JSON.

Investors decrypt only the ciphertext that was off-boarded to them.
Investors decrypt only the ciphertext that was off-boarded to them. Because `ctUint256` is a struct, the contract read returns a tuple `{ ciphertextHigh, ciphertextLow }`:

```typescript
const ct = await sepoliaAllocations.readResultByRequest(requestId);
const ctHex = typeof ct === "bigint" ? "0x" + ct.toString(16) : String(ct);
const raw = await sepoliaAllocations.readResultByRequest(requestId);
const ct = {
ciphertextHigh: BigInt(raw.ciphertextHigh ?? raw[0]),
ciphertextLow: BigInt(raw.ciphertextLow ?? raw[1]),
};

const plain = CotiPodCrypto.decrypt(
ctHex,
ct,
accountAesKeyFromOnboarding,
DataType.Uint256
);

console.log("private allocation:", plain);
```

Under the hood, the 256‑bit decrypt path calls `decryptUint256({ ciphertextHigh, ciphertextLow }, key)` from `@coti-io/coti-sdk-typescript` (`^1.0.7`). Narrower lanes (`Uint64`, `Uint128`) still take a single ciphertext word.

> **Warning:** Never log, persist, or transmit the account AES key as ordinary application data. Treat it as user-controlled key material.

## Part 7: Allocate and read private allocations
Expand Down Expand Up @@ -482,6 +488,8 @@ function onSetAllocationCompleted(bytes memory resultData) external onlyInbox {

For investor reads, the investor asks COTI to off-board their allocation to their address. The callback stores `ctUint256`, and the investor decrypts locally with their account AES key.

`ctUint256` is a Solidity **struct** with two `ctUint128` limbs (`ciphertextHigh`, `ciphertextLow`), so the decoded local needs a `memory` location and the storage mapping holds the two‑limb tuple.

```solidity
mapping(bytes32 => ctUint256) public allocationReadResults;

Expand All @@ -490,7 +498,7 @@ function onAllocationRead(bytes memory resultData) external onlyInbox {
require(callerChain == COTI_TESTNET_CHAIN_ID && callerContract == cotiAllocationPeer, "not allowed");

bytes32 requestId = IInbox(inbox).inboxSourceRequestId();
ctUint256 allocation = abi.decode(resultData, (ctUint256));
ctUint256 memory allocation = abi.decode(resultData, (ctUint256));

allocationReadResults[requestId] = allocation;
}
Expand Down
13 changes: 12 additions & 1 deletion privacy-on-demand/for-developers-mapping-to-the-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,21 @@ Then deep dives:
| **Callback guard** | [InboxUser.sol](https://github.com/cotitech-io/coti-pod-sdk/blob/main/contracts/InboxUser.sol) (`onlyInbox`) — see [Features](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/03-features.md). |
| **PodLib** | [PodLib.sol](https://github.com/cotitech-io/coti-pod-sdk/blob/main/contracts/mpc/PodLib.sol) and width-specific libraries (`PodLib64`, `PodLib128`, `PodLib256`). |
| **PodUser / presets** | [PodUser.sol](https://github.com/cotitech-io/coti-pod-sdk/blob/main/contracts/mpc/PodUser.sol), network mixins such as [PodUserSepolia.sol](https://github.com/cotitech-io/coti-pod-sdk/blob/main/contracts/mpc/PodUserSepolia.sol) in [Getting started](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/04-getting-started.md). |
| **Types (`it*`, `ct*`, `gt*`)** | [MpcCore.sol](https://github.com/cotitech-io/coti-pod-sdk/blob/main/contracts/utils/mpc/MpcCore.sol) and [Data types](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/contracts/01-it-ct-gt-data-types.md). |
| **Types (`it*`, `ct*`, `gt*`)** | Vendored [MpcCore.sol](https://github.com/cotitech-io/coti-pod-sdk/blob/main/contracts/utils/mpc/MpcCore.sol) (and `MpcInterface.sol`) inside the PoD SDK — no separate `@coti-io/coti-contracts` package is needed for PoD apps. See [Data types](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/contracts/01-it-ct-gt-data-types.md). |
| **Custom COTI calls** | [MpcAbiCodec.sol](https://github.com/cotitech-io/coti-pod-sdk/blob/main/contracts/mpccodec/MpcAbiCodec.sol) and the **custom mode** section of [Writing privacy contracts](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/05-writing-privacy-contracts-on-ethereum.md). |
| **Client crypto** | [coti-pod-crypto.ts](https://github.com/cotitech-io/coti-pod-sdk/blob/main/src/coti-pod-crypto.ts) via `CotiPodCrypto` ([TypeScript integration](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/06-typescript-integration-ux-development.md)). |

## Type model at a glance

The PoD SDK ships **`MpcCore.sol`** under `@coti/pod-sdk/contracts/utils/mpc/`. In the current revision:

- **`gtUint8` … `gtUint256` and `gtBool`** are **user‑defined value types** (`type gtUint256 is uint256`). Pass and assign them like `uint256` — **no `memory` / `calldata` on `gt*` parameters or locals**.
- **`ctUint8` … `ctUint128`** are also user‑defined value types (single `uint256` word).
- **`ctUint256`** is a **struct** `{ ctUint128 ciphertextHigh; ctUint128 ciphertextLow; }` — decoded locals and callback variables must use a `memory` location, and off‑chain reads return the two limbs as a tuple.
- **`itUint*`** (user encrypted inputs, `ciphertext + signature`), **`utUint*`** (dual‑ciphertext), **`gtString`** and **`ctString`** remain structs — keep their `calldata` / `memory` locations.

If you previously imported from **`@coti-io/coti-contracts`** in PoD code, switch the import to **`@coti/pod-sdk/contracts/utils/mpc/MpcCore.sol`**. Off‑chain decryption uses **`@coti-io/coti-sdk-typescript@^1.0.7`**, which exposes `decryptUint256({ ciphertextHigh, ciphertextLow }, accountAesKey)` for the 256‑bit lane.

## Implementation checklist (condensed)

Derived from the SDK’s [Writing privacy contracts](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/05-writing-privacy-contracts-on-ethereum.md) and [Async execution](https://github.com/cotitech-io/coti-pod-sdk/blob/main/docs/05a-async-execution.md):
Expand Down
Loading