Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@

# Artifacts
**/dist/

# Reference repos
contracts/
whitepaper/
438 changes: 326 additions & 112 deletions .planning/codebase/ARCHITECTURE.md

Large diffs are not rendered by default.

54 changes: 37 additions & 17 deletions .planning/codebase/CONCERNS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Codebase Concerns

**Analysis Date:** 2026-02-14
**Analysis Date:** 2026-02-17

## Tech Debt

Expand Down Expand Up @@ -31,23 +31,19 @@
- Impact: Lumos is deprecated upstream and will eventually stop receiving updates. Security patches and CKB protocol changes will only be reflected in CCC.
- Fix approach: This is resolved by the app migration above. The new `packages/` libraries already use CCC exclusively via `@ckb-ccc/core`.

### Local Epoch Class Partially Duplicates CCC Upstream (Medium)
### ~~Local Epoch Class Partially Duplicates CCC Upstream~~ (RESOLVED)

- Issue: `packages/utils/src/epoch.ts` defines a custom `Epoch` class (258 lines) that duplicates functionality now available in CCC's `ccc.Epoch`. CCC's `Epoch` class (in `ccc-dev/ccc/packages/core/src/ckb/epoch.ts`, 490 lines) provides `from()`, `add()`, `sub()`, `compare()`, `toUnix()`, `normalizeCanonical()`, `toPackedHex()`, and convenience comparison methods (`lt`, `le`, `eq`, `ge`, `gt`). The local `Epoch` class provides nearly identical functionality but uses different property naming (`number/index/length` vs. CCC's `integer/numerator/denominator`).
- Files:
- `packages/utils/src/epoch.ts` - local `Epoch` class
- `ccc-dev/ccc/packages/core/src/ckb/epoch.ts` - CCC upstream `Epoch` class
- Impact: Maintaining a parallel Epoch implementation means bugs fixed upstream are not automatically inherited. The local class already wraps CCC functions (`ccc.epochFromHex` at line 101, `ccc.epochToHex` at line 111) and accepts `ccc.Epoch` instances (via `deStruct()` at line 240), showing partial integration but also added complexity.
- Fix approach: Replace local `Epoch` with `ccc.Epoch` throughout `packages/`. Both classes provide `from()`, `add()`, `sub()`, `compare()`, and `toUnix()`. The main migration friction is the naming difference. CCC provides deprecated array-style accessors `[0]`, `[1]`, `[2]` for backwards compatibility, easing the transition for code that uses tuple-style access.
- **Resolved in:** commit ae8b5af (`refactor: replace ickb Epoch with ccc.Epoch`)
- Local `packages/utils/src/epoch.ts` (244 lines) was deleted. All packages now use `ccc.Epoch` directly.

### Local UDT Handling May Overlap CCC Upstream (Medium)

- Issue: CCC now has a dedicated `@ckb-ccc/udt` package (at `ccc-dev/ccc/packages/udt/`). The local `packages/utils/src/udt.ts` and `packages/core/src/udt.ts` implement custom UDT handling (`UdtHandler` interface, `IckbUdtManager` class). While the local UDT handling is iCKB-specific (custom balance calculation accounting for DAO deposits), the generic UDT operations like `ccc.udtBalanceFrom()` are already being used from CCC upstream in five locations.
- Issue: CCC now has a dedicated `@ckb-ccc/udt` package (at `ccc-dev/ccc/packages/udt/`). The local `packages/utils/src/udt.ts` and `packages/core/src/udt.ts` implement custom UDT handling (`UdtHandler` interface, `IckbUdtManager` class). While the local UDT handling is iCKB-specific (custom balance calculation accounting for DAO deposits), the generic UDT operations like `ccc.udtBalanceFrom()` are still being used from CCC upstream in `packages/utils/src/udt.ts` (4 locations).
- Files:
- `packages/utils/src/udt.ts` - `UdtHandler` interface, `UdtManager` class (~370 lines)
- `packages/core/src/udt.ts` - `IckbUdtManager` extending UDT handling for iCKB-specific logic
- `ccc-dev/ccc/packages/udt/src/` - CCC upstream UDT package
- Usage of `ccc.udtBalanceFrom()`: `packages/core/src/udt.ts` line 100, `packages/utils/src/udt.ts` lines 166, 193, 319, 363
- Usage of `ccc.udtBalanceFrom()`: `packages/utils/src/udt.ts` lines 169, 197, 323, 368
- Impact: There may be duplicated utility code for standard UDT operations (finding cells, calculating balances). The iCKB-specific extensions (e.g., `IckbUdtManager` which modifies balance calculations based on DAO deposit/withdrawal state) are domain-specific and unlikely to be in CCC.
- Fix approach: Audit the CCC `@ckb-ccc/udt` package to identify which local utilities can be replaced. Keep iCKB-specific extensions but delegate standard UDT operations (cell finding, basic balance) to CCC where possible.

Expand Down Expand Up @@ -220,12 +216,36 @@
- Files: All files under `packages/` and `apps/`
- Risk: The financial logic in `packages/sdk/src/sdk.ts` (order matching, maturity estimation, CKB/UDT conversion), `packages/order/src/order.ts` (order matching algorithm with complex bigint arithmetic), and `packages/dao/src/cells.ts` (DAO interest calculation, maturity computation) handle real cryptocurrency operations. Bugs in these areas could lead to financial loss.
- Priority: High. At minimum, the following should have unit tests:
1. `packages/utils/src/epoch.ts` - Epoch arithmetic (add, sub, compare, toUnix)
2. `packages/order/src/entities.ts` - `Ratio.applyFee()`, `Ratio.convert()`, `Info.validate()`
3. `packages/order/src/order.ts` - `OrderMatcher.match()`, `OrderMatcher.nonDecreasing()`, `OrderManager.bestMatch()`
4. `packages/sdk/src/codec.ts` - `PoolSnapshot` encode/decode roundtrip
5. `packages/dao/src/cells.ts` - `daoCellFrom()` maturity/interest calculations
6. `packages/core/src/logic.ts` - iCKB exchange ratio and conversion logic
1. `packages/utils/src/utils.ts` - `binarySearch`, `asyncBinarySearch`, `gcd`, `min`, `max`, `sum`, `hexFrom`, `isHex`, `shuffle`
2. `packages/utils/src/codec.ts` - `CheckedInt32LE` encode/decode
3. `packages/order/src/entities.ts` - `Ratio.applyFee()`, `Ratio.convert()`, `Info.validate()`
4. `packages/order/src/order.ts` - `OrderMatcher.match()`, `OrderMatcher.nonDecreasing()`, `OrderManager.bestMatch()`
5. `packages/sdk/src/codec.ts` - `PoolSnapshot` encode/decode roundtrip
6. `packages/dao/src/cells.ts` - `daoCellFrom()` maturity/interest calculations
7. `packages/core/src/logic.ts` - iCKB exchange ratio and conversion logic

### TS Exchange Rate Must Match Rust Contract Logic

- What's not tested: The TypeScript exchange rate calculation (`packages/core/src/udt.ts`) must produce identical results to the Rust contract's `deposit_to_ickb()` function (`contracts/scripts/contracts/ickb_logic/src/entry.rs`). Any discrepancy would cause transactions to be rejected on-chain.
- Key formula: `iCKB = capacity * AR_0 / AR_m` with soft cap penalty `amount - (amount - 100000) / 10` when `amount > ICKB_SOFT_CAP_PER_DEPOSIT`
- Contract constants that TS must match:
- `CKB_MINIMUM_UNOCCUPIED_CAPACITY_PER_DEPOSIT = 1,000 * 100_000_000` (1,000 CKB)
- `CKB_MAXIMUM_UNOCCUPIED_CAPACITY_PER_DEPOSIT = 1,000,000 * 100_000_000` (1,000,000 CKB)
- `ICKB_SOFT_CAP_PER_DEPOSIT = 100,000 * 100_000_000` (100,000 iCKB)
- `GENESIS_ACCUMULATED_RATE = 10_000_000_000_000_000` (10^16)
- Reference: `contracts/scripts/contracts/ickb_logic/src/entry.rs` function `deposit_to_ickb()`
- Fix approach: Add cross-validation tests with known inputs/outputs derived from the Rust contract logic

### TS Molecule Codecs Must Match Contract Schemas

- What's not tested: The TypeScript Molecule codec definitions (`@ccc.codec` decorators in `packages/order/src/entities.ts`, `packages/core/src/entities.ts`) must produce byte-identical encodings to the Molecule schema at `contracts/schemas/encoding.mol`. Field order, sizes, and endianness must match exactly.
- Key schemas:
- `ReceiptData { deposit_quantity: Uint32, deposit_amount: Uint64 }` = 12 bytes
- `OwnedOwnerData { owned_distance: Int32 }` = 4 bytes
- `Ratio { ckb_multiplier: Uint64, udt_multiplier: Uint64 }` = 16 bytes
- `OrderInfo { ckb_to_udt: Ratio, udt_to_ckb: Ratio, ckb_min_match_log: Uint8 }` = 33 bytes
- Order cell data: `[UDT amount (16)] [Action (4)] [TX hash/padding (32)] [Index/distance (4)] [OrderInfo (33)] = 89 bytes`
- Fix approach: Add codec roundtrip tests using known byte vectors from the Rust contract tests or manually constructed from the Molecule schema

## Dead Code

Expand All @@ -252,4 +272,4 @@

---

*Concerns audit: 2026-02-14*
*Concerns audit: 2026-02-17*
18 changes: 11 additions & 7 deletions .planning/codebase/CONVENTIONS.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Coding Conventions

**Analysis Date:** 2026-02-14
**Analysis Date:** 2026-02-17

## Important Context

**Tooling:**
- `gh` CLI is NOT available and must NOT be installed. Use `pnpm pr` to open PRs and `pnpm review` to fetch PR comments.

**Legacy vs. New code:**
- `@ickb/lumos-utils@1.4.2` and `@ickb/v1-core@1.4.2` are **LEGACY and DEPRECATED** npm packages. The apps (`apps/bot`, `apps/tester`, `apps/interface`) still depend on them.
- The `packages/` directory contains the **NEW replacement libraries** built on CCC (ckb-ccc), which will eventually replace the legacy packages in the apps.
Expand All @@ -18,7 +21,7 @@

**Files:**
- Use `snake_case` for multi-word source files: `owned_owner.ts`
- Use single lowercase words when possible: `cells.ts`, `entities.ts`, `logic.ts`, `codec.ts`, `utils.ts`, `epoch.ts`, `heap.ts`, `udt.ts`
- Use single lowercase words when possible: `cells.ts`, `entities.ts`, `logic.ts`, `codec.ts`, `utils.ts`, `heap.ts`, `udt.ts`
- Every package has an `index.ts` barrel file that re-exports everything
- Config files at root use dot-prefix convention: `prettier.config.cjs`, `eslint.config.mjs`, `vitest.config.mts`

Expand Down Expand Up @@ -270,16 +273,18 @@ export * from "./utils.js";

## Molecule / Codec Patterns

**Entity classes** use CCC's `mol.Entity.Base` with decorator-based codec definition:
**TS codecs must match the Molecule schema** at `contracts/schemas/encoding.mol`. The on-chain contracts use Molecule for serialization; the TS packages must produce byte-identical encodings.

**Entity classes** use CCC's `ccc.Entity.Base` with decorator-based codec definition:
```typescript
// packages/order/src/entities.ts
@mol.codec(
@ccc.codec(
mol.struct({
ckbScale: mol.Uint64,
udtScale: mol.Uint64,
}),
)
export class Ratio extends mol.Entity.Base<ExchangeRatio, Ratio>() {
export class Ratio extends ccc.Entity.Base<ExchangeRatio, Ratio>() {
constructor(
public ckbScale: ccc.Num,
public udtScale: ccc.Num,
Expand Down Expand Up @@ -324,7 +329,6 @@ let origins: readonly I8Cell[] = Object.freeze([]);
```
- Use `readonly` on class fields and interface members where appropriate
- Use `Readonly<T>` wrapper type for maps and objects: `Readonly<Map<string, readonly Cell[]>>`
- Private constructors for controlled instantiation: `Epoch` in `packages/utils/src/epoch.ts` uses `private constructor`

## Async Generator Pattern

Expand Down Expand Up @@ -369,4 +373,4 @@ process.exit(0);

---

*Convention analysis: 2026-02-14*
*Convention analysis: 2026-02-17*
78 changes: 70 additions & 8 deletions .planning/codebase/INTEGRATIONS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# External Integrations

**Analysis Date:** 2026-02-14
**Analysis Date:** 2026-02-17

## APIs & External Services

Expand Down Expand Up @@ -104,42 +104,104 @@ All interaction with the Nervos CKB Layer 1 blockchain happens via JSON-RPC 2.0.

## Smart Contracts (On-Chain Scripts)

The project interacts with several on-chain CKB scripts defined in `packages/sdk/src/constants.ts`:
The project interacts with several on-chain CKB scripts defined in `packages/sdk/src/constants.ts`. The Rust source code is available in the `contracts/` reference repo (clone via `pnpm reference`). Protocol design is documented in the `whitepaper/` reference repo.

**NervosDAO:**
- Code hash: `0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e`
- Hash type: `type`
- Purpose: Deposit/withdraw CKB with interest (Nervos DAO)
- Managed by: `DaoManager` in `packages/dao/src/dao.ts`
- Constants: `DAO_DEPOSIT_DATA = [0,0,0,0,0,0,0,0]` (8 zero bytes = deposit; non-zero = withdrawal request)
- DAO accumulated rate: extracted from block header at offset 168, size 8 bytes
- Genesis accumulated rate: `AR_0 = 10^16` (used as baseline for iCKB exchange rate)

**iCKB UDT (User Defined Token):**
**iCKB UDT (xUDT Token):**
- Code hash: `0x50bd8d6680b8b9cf98b73f3c08faf8b2a21914311954118ad6609be6e78a1b95`
- Hash type: `data1`
- Purpose: The iCKB token type script
- Args: `[iCKB_Logic_Hash, 0x00000080]` (0x80000000 = owner mode by input type)
- Token script hash: `0xd485c2271949c232e3f5d46128336c716f90bcbf3cb278696083689fbbcd407a`
- Amount storage: 16 bytes (u128 LE) in cell data
- Purpose: The iCKB token type script (xUDT standard)
- Managed by: `IckbUdtManager` in `packages/core/src/udt.ts`

**iCKB Logic:**
- Code hash: `0x2a8100ab5990fa055ab1b50891702e1e895c7bd1df6322cd725c1a6115873bd3`
- Hash type: `data1`
- Purpose: Core iCKB deposit/receipt logic
- Purpose: Core iCKB deposit/receipt logic (type script)
- Managed by: `LogicManager` in `packages/core/src/logic.ts`
- Contract source: `contracts/scripts/contracts/ickb_logic/`
- Validation rules:
- Empty args required (prevents reuse with different configurations)
- Cell classification: Deposit (iCKB lock + DAO type), Receipt (any lock + iCKB type), UDT (any lock + xUDT type)
- Conservation law: `input_udt + input_receipts = output_udt + input_deposits`
- Deposit size: min 1,000 CKB, max 1,000,000 CKB (unoccupied capacity)
- Soft cap: 100,000 iCKB per deposit; 10% penalty on excess
- Receipt-deposit matching: for each unique deposit amount, deposit count must equal receipt count
- Receipt data format: `[deposit_quantity: u32 LE (4 bytes), deposit_amount: u64 LE (8 bytes)]` = 12 bytes total
- Exchange rate: `iCKB = capacity * AR_0 / AR_m` where AR_m = accumulated rate at deposit block
- Error codes: NotEmptyArgs(5), ScriptMisuse(6), DepositTooSmall(7), DepositTooBig(8), EmptyReceipt(9), ReceiptMismatch(10), AmountMismatch(11), AmountUnreasonablyBig(12)

**Owned Owner:**
- Code hash: `0xacc79e07d107831feef4c70c9e683dac5644d5993b9cb106dca6e74baa381bd0`
- Hash type: `data1`
- Purpose: Withdrawal ownership tracking
- Purpose: Withdrawal ownership tracking (lock script)
- Managed by: `OwnedOwnerManager` in `packages/core/src/owned_owner.ts`
- Contract source: `contracts/scripts/contracts/owned_owner/`
- Design: Solves NervosDAO constraint that deposit lock and withdrawal lock must have equal size
- Mechanism: Owner cell (type=owned_owner) contains `owned_distance: i32 LE` (4 bytes) pointing to its paired owned cell (lock=owned_owner)
- Validation rules:
- Empty args required
- Owned cells must be DAO withdrawal requests (not deposits)
- 1:1 pairing enforced: exactly 1 owner and 1 owned per MetaPoint, in both inputs and outputs
- Cannot be both lock and type simultaneously
- Error codes: NotEmptyArgs(5), NotWithdrawalRequest(6), ScriptMisuse(7), Mismatch(8)

**Order (Limit Order):**
- Code hash: `0x49dfb6afee5cc8ac4225aeea8cb8928b150caf3cd92fea33750683c74b13254a`
- Hash type: `data1`
- Purpose: On-chain limit orders for CKB/iCKB exchange
- Purpose: On-chain limit orders for CKB/UDT exchange (lock script)
- Managed by: `OrderManager` in `packages/order/src/order.ts`
- Contract source: `contracts/scripts/contracts/limit_order/`
- Lifecycle: Mint (create order + master cell) -> Match (partial/full fill) -> Melt (destroy fulfilled order)
- Order cell data layout (88-89 bytes):
- `[0:16]` UDT amount (u128 LE)
- `[16:20]` Action (u32 LE): 0=Mint, 1=Match
- `[20:52]` TX hash (Mint: all zeros padding) or master outpoint hash (Match)
- `[52:56]` Master distance (Mint: i32 relative offset) or master index (Match: u32 absolute)
- `[56:64]` CKB->UDT ckb_multiplier (u64 LE)
- `[64:72]` CKB->UDT udt_multiplier (u64 LE)
- `[72:80]` UDT->CKB ckb_multiplier (u64 LE)
- `[80:88]` UDT->CKB udt_multiplier (u64 LE)
- `[88:89]` ckb_min_match_log (u8): minimum match = `1 << n`, range 0..=64
- Validation rules:
- Empty args required
- Mint: output has order + master; padding must be all zeros
- Match: value conservation `in_ckb * ckb_mul + in_udt * udt_mul <= out_ckb * ckb_mul + out_udt * udt_mul`
- Melt: input has order + master; no output
- Concavity check: `c2u.ckb_mul * u2c.udt_mul >= c2u.udt_mul * u2c.ckb_mul` (round-trip cannot lose value)
- DOS prevention: partial matches must meet minimum threshold (`1 << ckb_min_match_log`)
- Order info (ratios, min match, UDT hash) must be immutable across matches
- Cannot modify already-fulfilled orders
- Error codes: NotEmptyArgs(5), DuplicatedMaster(6), InvalidAction(7), NonZeroPadding(8), InvalidRatio(9), InvalidCkbMinMatchLog(10), ConcaveRatio(11), BothRatioNull(12), MissingUdtType(13), SameMaster(14), ScriptMisuse(15), DifferentInfo(16), InvalidMatch(17), DecreasingValue(18), AttemptToChangeFulfilled(19), InsufficientMatch(20), InvalidConfiguration(21)

**Molecule Schema (`contracts/schemas/encoding.mol`):**
```molecule
struct ReceiptData { deposit_quantity: Uint32, deposit_amount: Uint64 }
struct OwnedOwnerData { owned_distance: Int32 }
struct Ratio { ckb_multiplier: Uint64, udt_multiplier: Uint64 }
struct OrderInfo { ckb_to_udt: Ratio, udt_to_ckb: Ratio, ckb_min_match_log: Uint8 }
struct MintOrderData { padding: Byte32, master_distance: Int32, order_info: OrderInfo }
struct MatchOrderData { master_outpoint: OutPoint, order_info: OrderInfo }
union PartialOrderData { MintOrderData, MatchOrderData }
```

**Deployment Groups (Cell Dependencies):**
- Mainnet dep group: TX `0x621a6f38de3b9f453016780edac3b26bfcbfa3e2ecb47c2da275471a5d3ed165` index 0
- Testnet dep group: TX `0xf7ece4fb33d8378344cab11fcd6a4c6f382fd4207ac921cf5821f30712dcd311` index 0
- Known bot scripts: one mainnet bot, one testnet bot (lock scripts in `packages/sdk/src/constants.ts`)
- Deployment TX (mainnet): `0xd7309191381f5a8a2904b8a79958a9be2752dbba6871fa193dab6aeb29dc8f44`
- All scripts deployed with zero lock (immutable, non-upgradable)
- Security audit: Scalebit (2024-09-11), no critical vulnerabilities

**Network configuration:** `IckbSdk.from("mainnet" | "testnet")` in `packages/sdk/src/sdk.ts` selects the appropriate script hashes and dep groups.

Expand Down Expand Up @@ -218,4 +280,4 @@ CCC (`@ckb-ccc/core`) is the most critical external dependency. Key capabilities

---

*Integration audit: 2026-02-14*
*Integration audit: 2026-02-17*
9 changes: 6 additions & 3 deletions .planning/codebase/STACK.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Technology Stack

**Analysis Date:** 2026-02-14
**Analysis Date:** 2026-02-17

## Languages

**Primary:**
- TypeScript 5.9.3 - All source code across packages and apps

**On-Chain (reference):**
- Rust 2021 edition - On-chain CKB smart contracts in `contracts/` reference repo (3 contracts + shared utils, ~1,163 lines). Built with Capsule v0.10.5, `no_std` + alloc-only runtime, targeting RISC-V. Uses `ckb-std 0.15.3` and `primitive_types` crate for C256 safe math.

**Secondary:**
- Bash - `ccc-dev/record.sh`, `ccc-dev/replay.sh` for local CCC dev build setup
- JavaScript (CJS) - `.pnpmfile.cjs` for pnpm hook overrides, `prettier.config.cjs`
Expand Down Expand Up @@ -110,7 +113,7 @@ The repo supports using a local development build of CCC for testing unpublished
- Applies to `dependencies`, `devDependencies`, and `optionalDependencies`
- Effect: all workspace packages transparently use the local CCC build instead of npm versions

**CCC upstream contributions:** The maintainer contributed UDT and Epoch support to CCC upstream (now merged). Some local code in `packages/utils/src/epoch.ts` and `packages/utils/src/udt.ts` may overlap with features now available natively in CCC.
**CCC upstream contributions:** The maintainer contributed UDT and Epoch support to CCC upstream (now merged). The local Epoch class has been deleted (replaced by `ccc.Epoch`). Some local UDT handling in `packages/utils/src/udt.ts` may still overlap with features now available in CCC's `@ckb-ccc/udt` package.

## Configuration

Expand Down Expand Up @@ -181,4 +184,4 @@ The repo supports using a local development build of CCC for testing unpublished

---

*Stack analysis: 2026-02-14*
*Stack analysis: 2026-02-17*
Loading