diff --git a/.changeset/adopt-ccc-udt.md b/.changeset/adopt-ccc-udt.md new file mode 100644 index 0000000..afa1c66 --- /dev/null +++ b/.changeset/adopt-ccc-udt.md @@ -0,0 +1,17 @@ +--- +"@ickb/core": major +"@ickb/dao": minor +"@ickb/order": major +"@ickb/utils": major +"@ickb/sdk": major +--- + +Adopt CCC udt.Udt as IckbUdt base class, replacing homegrown UDT infrastructure + +- Rewrite `IckbUdtManager` as `IckbUdt` extending CCC's `udt.Udt` base class +- Accept code OutPoints instead of pre-built CellDep arrays +- Override `infoFrom()` to value iCKB's three cell representations (xUDT, receipt, deposit) +- Remove `udtHandler` parameter from `LogicManager` and `OwnedOwnerManager` +- Replace `UdtHandler` with plain `udtScript` in `OrderManager` +- Delete `UdtHandler`, `UdtManager`, `UdtCell`, `ErrorTransactionInsufficientCoin` from `@ickb/utils` +- Widen `DaoManager.isDeposit()` to accept `CellAny` diff --git a/.planning/PROJECT.md b/.planning/PROJECT.md index 3659620..e1222ed 100644 --- a/.planning/PROJECT.md +++ b/.planning/PROJECT.md @@ -24,7 +24,7 @@ Clean, CCC-aligned library packages published to npm that frontends can depend o ### Active - [ ] Remove SmartTransaction — replace with `ccc.Transaction` + utility functions -- [ ] Adopt CCC UDT handling — investigate subclassing `Udt` for iCKB's multi-representation value (xUDT + receipts + deposits) +- [ ] Adopt CCC UDT handling — `IckbUdt extends udt.Udt` decided (Phase 3); managers get plain `ccc.Script` not `udt.Udt` instances (Phase 4); implementation in Phase 5 - [ ] Systematic CCC alignment audit — replace local utilities with CCC equivalents from merged upstream PRs - [ ] Migrate bot app from Lumos to CCC + new packages - [ ] Migrate interface app from Lumos to CCC + new packages (straight swap, same UI) @@ -73,10 +73,13 @@ Clean, CCC-aligned library packages published to npm that frontends can depend o | Decision | Rationale | Outcome | |----------|-----------|---------| | Remove SmartTransaction, use ccc.Transaction directly | SmartTransaction concept abandoned by ecosystem, no adoption from CCC maintainers | — Pending | -| Investigate CCC Udt subclassing for iCKB | iCKB value is multi-representation (xUDT + receipts + deposits); need to determine if CCC's Udt can be extended | — Pending | +| Subclass CCC Udt for iCKB | iCKB value is multi-representation (xUDT + receipts + deposits); Phase 3 confirmed `IckbUdt extends udt.Udt` with `infoFrom` override is feasible and chosen | Decided (Phase 3) | +| IckbUdt uses individual code deps | CCC author dislikes dep groups for breaking semantics; IckbUdt overrides `addCellDeps` with xUDT + Logic code OutPoints; other managers keep dep groups for now | Decided (Phase 5 context) | +| Drop compressState, accept CCC errors | `completeInputsByBalance` replaces `completeUdt`; CCC's `ErrorUdtInsufficientCoin` replaces custom error class; callers format messages | Decided (Phase 5 context) | +| Managers get plain ccc.Script only | LogicManager/OwnedOwnerManager udtHandler removed (Phase 5), matching OrderManager pattern (Phase 4); udt.Udt instance lives at SDK level | Decided (Phase 5 context) | | Library refactor before app migration | Clean packages first, then migrate apps on stable foundation | — Pending | | Interface app: straight migration only | No UI/UX redesign — swap Lumos internals for CCC packages | — Pending | | Track CCC PR #328 (FeePayer) | Could become CCC-native solution for what SmartTransaction does for fee completion | — Pending | --- -*Last updated: 2026-02-20 after initialization* +*Last updated: 2026-02-26 after Phase 5 context (code deps for IckbUdt, manager udtHandler removal, compressState dropped, CCC error adoption)* diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 9a68233..ba36c29 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -15,10 +15,10 @@ Requirements for initial milestone. Each maps to roadmap phases. - [x] **SMTX-04**: `getHeader()` function and `HeaderKey` type are removed from `@ickb/utils`; all call sites inline CCC client calls (`client.getTransactionWithHeader()`, `client.getHeaderByNumber()`); header caching handled transparently by `ccc.Client.cache` - [x] **SMTX-05**: UDT handler registration (`addUdtHandlers()`) is replaced by direct `Udt` instance usage or standalone utility functions - [x] **SMTX-06**: 64-output NervosDAO limit check is consolidated into a single utility function (currently scattered across 6 locations) -- [ ] **SMTX-07**: `IckbUdtManager` multi-representation UDT balance logic (xUDT + receipts + deposits) survives removal intact -- conservation law `Input UDT + Input Receipts = Output UDT + Input Deposits` is preserved +- [x] **SMTX-07**: `IckbUdtManager` multi-representation UDT balance logic (xUDT + receipts + deposits) survives removal intact -- conservation law `Input UDT + Input Receipts = Output UDT + Input Deposits` preserved via accurate balance reporting in `IckbUdt.infoFrom` (on-chain script is authoritative enforcer) - [ ] **SMTX-08**: `IckbSdk.estimate()` and `IckbSdk.maturity()` continue working after SmartTransaction removal - [ ] **SMTX-09**: All 5 library packages (`@ickb/utils`, `@ickb/dao`, `@ickb/order`, `@ickb/core`, `@ickb/sdk`) compile and pass type checking after removal -- [ ] **SMTX-10**: Deprecated CCC API calls (`udtBalanceFrom`, `getInputsUdtBalance`, `getOutputsUdtBalance`, `completeInputsByUdt`) are replaced with `@ckb-ccc/udt` equivalents +- [x] **SMTX-10**: Deprecated CCC API calls (`udtBalanceFrom`, `getInputsUdtBalance`, `getOutputsUdtBalance`, `completeInputsByUdt`) are replaced with `@ckb-ccc/udt` equivalents ### CCC Utility Deduplication @@ -33,8 +33,8 @@ Requirements for initial milestone. Each maps to roadmap phases. - [x] **UDT-01**: Feasibility assessment completed: can `IckbUdt extends udt.Udt` override `infoFrom()` or `getInputsInfo()`/`getOutputsInfo()` to account for receipt cells and deposit cells alongside xUDT cells - [x] **UDT-02**: Header access pattern for receipt value calculation is designed -- determine whether `client.getCellWithHeader()`, `client.getTransactionWithHeader()`, or direct CCC client calls are used within the Udt override (`getHeader()` utility removed in Phase 1) - [x] **UDT-03**: Decision documented: subclass CCC `Udt` vs. keep custom `UdtHandler` interface vs. hybrid approach -- [ ] **UDT-04**: If subclassing is viable, `IckbUdt` class is implemented in `@ickb/core` with multi-representation balance calculation -- [ ] **UDT-05**: If subclassing is not viable, `IckbUdtManager` is refactored to work with plain `ccc.Transaction` (no SmartTransaction dependency) while maintaining a compatible interface +- [x] **UDT-04**: `IckbUdt extends udt.Udt` implemented in `@ickb/core` with `infoFrom` override for multi-representation balance; individual code deps via overridden `addCellDeps`; `typeScriptFrom` static method; LogicManager/OwnedOwnerManager `udtHandler` removed +- [x] **UDT-05**: N/A -- Phase 3 confirmed subclassing IS viable (option a chosen) ## v2 Requirements @@ -85,12 +85,12 @@ Which phases cover which requirements. Updated during roadmap creation. | SMTX-02 | Phase 1 | Complete | SmartTransaction class deleted from @ickb/utils (01-03) | | SMTX-03 | Phase 6 | Pending | | | SMTX-04 | Phase 1 | Complete | getHeader()/HeaderKey removed, CCC client calls inlined | -| SMTX-05 | Phase 1, 4, 5 | Complete | addUdtHandlers() replaced with tx.addCellDeps(udtHandler.cellDeps) (01-03); UdtHandler/UdtManager replacement deferred to Phase 4-5 | +| SMTX-05 | Phase 1, 4, 5 | In Progress | addUdtHandlers() replaced with tx.addCellDeps(udtHandler.cellDeps) (01-03); Phase 4: OrderManager.udtHandler→udtScript; Phase 5: LogicManager/OwnedOwnerManager.udtHandler removed + UdtHandler/UdtManager/ErrorTransactionInsufficientCoin/findUdts/UdtCell deleted from utils | | SMTX-06 | Phase 1 | Complete | DAO check contributed to CCC core via forks/ccc/ (01-01) | -| SMTX-07 | Phase 5 | Pending | | +| SMTX-07 | Phase 5 | Complete | | | SMTX-08 | Phase 6 | Pending | | | SMTX-09 | Phase 7 | Pending | | -| SMTX-10 | Phase 4, 5 | Pending | Deprecated calls in dao/order (Phase 4) and core (Phase 5) | +| SMTX-10 | Phase 5 | Complete | Deprecated ccc.udtBalanceFrom() calls are in utils (UdtManager) and core (IckbUdtManager), not dao/order. All replaced when UdtManager deleted and IckbUdt implemented in Phase 5 | | DEDUP-01 | Phase 2 | Complete | max()/min() replaced with Math.max()/Math.min() for number-typed contexts, local deleted (02-01) | | DEDUP-02 | Phase 2 | Complete | gcd() replaced with ccc.gcd(), local deleted (02-01) | | DEDUP-03 | Phase 2 | Complete | isHex() deleted, only used internally by deleted hexFrom() (02-01) | @@ -99,8 +99,8 @@ Which phases cover which requirements. Updated during roadmap creation. | UDT-01 | Phase 3 | Complete | | | UDT-02 | Phase 3 | Complete | | | UDT-03 | Phase 3 | Complete | | -| UDT-04 | Phase 5 | Pending | | -| UDT-05 | Phase 5 | Pending | | +| UDT-04 | Phase 5 | Complete | IckbUdt with infoFrom override, code deps, typeScriptFrom, manager cleanup | +| UDT-05 | Phase 3 | N/A | Subclassing confirmed viable — option (a) chosen | **Coverage:** - v1 requirements: 20 total @@ -109,4 +109,4 @@ Which phases cover which requirements. Updated during roadmap creation. --- *Requirements defined: 2026-02-21* -*Last updated: 2026-02-24 after Phase 3 completion (UDT-01 through UDT-03 completed; Phase 3 complete)* +*Last updated: 2026-02-26 after Phase 5 context (UDT-05 N/A — subclassing viable; SMTX-05 expanded with LogicManager/OwnedOwnerManager cleanup; UDT-04 detailed with code deps, typeScriptFrom, manager cleanup)* diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 27dc965..79373c3 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -15,8 +15,8 @@ Decimal phases appear between their surrounding integers in numeric order. - [x] **Phase 1: SmartTransaction Removal (feature-slice)** - Delete SmartTransaction class and infrastructure across all packages; contribute 64-output DAO limit check to CCC core; migrate all method signatures to ccc.TransactionLike - [x] **Phase 2: CCC Utility Adoption** - Replace local utility functions that duplicate CCC equivalents across all packages; preserve iCKB-unique utilities - [x] **Phase 3: CCC Udt Integration Investigation** - Assess feasibility of subclassing CCC's Udt class for iCKB's multi-representation value; design header access pattern; document decision -- [ ] **Phase 4: Deprecated CCC API Replacement** - Replace deprecated CCC API calls (`udtBalanceFrom`, etc.) with `@ckb-ccc/udt` equivalents in dao and order packages; finalize UDT handler replacement pattern based on Phase 3 findings -- [ ] **Phase 5: @ickb/core UDT Refactor** - Implement IckbUdt class or refactor IckbUdtManager based on Phase 3 findings; preserve iCKB conservation law; replace deprecated CCC API calls in core +- [x] **Phase 4: Deprecated CCC API Replacement** - Replace UdtHandler dependency in `@ickb/order` with plain `ccc.Script`; remove UDT cellDeps management from OrderManager; correct Phase 3 docs; verify `@ickb/dao` already clean +- [ ] **Phase 5: @ickb/core UDT Refactor** - Implement `IckbUdt extends udt.Udt` with `infoFrom` override; remove `udtHandler` from LogicManager/OwnedOwnerManager; delete UdtHandler/UdtManager/ErrorTransactionInsufficientCoin/findUdts from utils; adopt individual code deps for IckbUdt; update SDK construction and error handling - [ ] **Phase 6: SDK Completion Pipeline** - Wire IckbSdk facade to CCC-native fee completion; verify estimate() and maturity() work end-to-end - [ ] **Phase 7: Full Stack Verification** - Verify all 5 library packages compile clean with no SmartTransaction remnants and no type errors @@ -70,36 +70,39 @@ Plans: - [x] 03-02-PLAN.md — Write formal decision document (feasibility assessment, header access pattern, decision with rationale) ### Phase 4: Deprecated CCC API Replacement -**Goal**: Deprecated CCC API calls are replaced with `@ckb-ccc/udt` equivalents in `@ickb/dao` and `@ickb/order`; UDT handler usage is finalized based on Phase 3 findings (method signatures and `addUdtHandlers()` removal already done in Phase 1) -**Depends on**: Phase 1 (signatures migrated), Phase 3 (UDT decision — determines replacement pattern for UdtHandler usage) -**Requirements**: SMTX-05, SMTX-10 +**Goal**: `UdtHandler` dependency in `@ickb/order` is replaced with plain `ccc.Script` (`udtScript`); UDT cellDeps management removed from OrderManager (caller/CCC Udt handles externally during balance completion); `@ickb/dao` verified clean (no UdtHandler, no deprecated APIs); Phase 3 decision doc corrected to match actual codebase state +**Depends on**: Phase 1 (signatures migrated), Phase 3 (UDT decision) +**Requirements**: SMTX-05 **Success Criteria** (what must be TRUE): - 1. No calls to deprecated CCC APIs (`udtBalanceFrom`, `getInputsUdtBalance`, `getOutputsUdtBalance`, `completeInputsByUdt`) exist in `@ickb/dao` or `@ickb/order` - 2. UDT-related operations in `@ickb/dao` and `@ickb/order` use the pattern chosen in Phase 3 (direct `Udt` instance methods, refactored UdtManager, or hybrid) - 3. Both `@ickb/dao` and `@ickb/order` compile successfully -**Plans**: TBD + 1. `OrderManager` constructor accepts `udtScript: ccc.Script` instead of `udtHandler: UdtHandler` -- all 9 `this.udtHandler.script` references replaced with `this.udtScript` + 2. All `tx.addCellDeps(this.udtHandler.cellDeps)` calls removed from `mint()`, `addMatch()`, `melt()` -- UDT cellDeps are caller responsibility + 3. `@ickb/order` no longer imports `UdtHandler` from `@ickb/utils`; no new `@ckb-ccc/udt` dependency added + 4. `@ickb/dao` has no UdtHandler references and no deprecated CCC API calls (verified, no changes needed) + 5. Phase 3 decision doc `03-DECISION.md` "Implementation Guidance for Phase 4" section corrected + 6. `pnpm check:full` passes +**Plans**: 1 plan Plans: -- [ ] 04-01: TBD -- [ ] 04-02: TBD +- [x] 04-01-PLAN.md — Replace OrderManager udtHandler with udtScript: ccc.Script, remove UDT cellDeps, update SDK caller, verify dao and decision doc clean ### Phase 5: @ickb/core UDT Refactor -**Goal**: IckbUdt class is implemented or IckbUdtManager is refactored based on Phase 3 findings; the iCKB conservation law is preserved through the refactor; deprecated CCC API calls are replaced in `@ickb/core`; UdtHandler/UdtManager are removed from `@ickb/utils` (manager method signatures already migrated to `ccc.TransactionLike` in Phase 1) -**Depends on**: Phase 3 (UDT decision), Phase 4 (dao+order UDT pattern finalized) -**Requirements**: SMTX-05, SMTX-07, SMTX-10, UDT-04, UDT-05 +**Goal**: `IckbUdt extends udt.Udt` implemented in `@ickb/core` with `infoFrom` override for multi-representation balance; iCKB conservation law preserved via accurate balance reporting; `udtHandler` removed from LogicManager/OwnedOwnerManager (Phase 4 pattern); `UdtHandler`, `UdtManager`, `ErrorTransactionInsufficientCoin`, `UdtCell`, `findUdts`, `addUdts` deleted from `@ickb/utils`; IckbUdt uses individual code deps (not dep group) with overridden `addCellDeps`; SDK updated to construct `IckbUdt` and handle CCC's `ErrorUdtInsufficientCoin`. See `05-CONTEXT.md` for full decisions. +**Depends on**: Phase 3 (UDT decision), Phase 4 (order UDT pattern finalized) +**Requirements**: SMTX-05, SMTX-07, SMTX-10, UDT-04 **Success Criteria** (what must be TRUE): - 1. The iCKB conservation law (`Input UDT + Input Receipts = Output UDT + Input Deposits`) is enforced correctly in the refactored code -- multi-representation UDT balance logic survives intact - 2. If Phase 3 concluded subclassing is viable: `IckbUdt extends udt.Udt` exists in `@ickb/core` with overridden `infoFrom()` that accounts for xUDT cells, receipt cells, and deposit cells - 3. If Phase 3 concluded subclassing is not viable: `IckbUdtManager` is refactored to work with plain `ccc.Transaction` while maintaining a compatible interface for balance calculation - 4. `UdtHandler` interface and `UdtManager` class are removed from `@ickb/utils` (their responsibilities absorbed by the Phase 3 outcome implementation) - 5. No calls to deprecated CCC APIs exist in `@ickb/core` - 6. `@ickb/core` compiles successfully with no SmartTransaction imports -**Plans**: TBD + 1. `IckbUdt extends udt.Udt` exists in `@ickb/core` with overridden `infoFrom()` that accurately values xUDT cells (positive), receipt cells (positive, input only), and deposit cells (negative, input only) -- conservation law preserved via correct sign conventions + 2. `IckbUdt.addCellDeps` overridden to add individual code deps (xUDT code OutPoint + iCKB Logic code OutPoint) instead of dep group + 3. `IckbUdtManager.calculateScript` renamed to `IckbUdt.typeScriptFrom` (static, same params) + 4. `LogicManager` and `OwnedOwnerManager` no longer take `udtHandler` parameter; `tx.addCellDeps(this.udtHandler.cellDeps)` calls removed (4 sites) -- UDT cellDeps are caller responsibility (Phase 4 pattern) + 5. `UdtHandler` interface, `UdtManager` class, `ErrorTransactionInsufficientCoin` class, `UdtCell` interface, `findUdts`, `addUdts`, `isUdtSymbol` deleted from `@ickb/utils` + 6. No calls to deprecated CCC APIs (`ccc.udtBalanceFrom`) exist in `@ickb/core` or `@ickb/utils` + 7. SDK constructs `IckbUdt` instead of `IckbUdtManager`; passes `ickbUdt.script` to managers; handles `ErrorUdtInsufficientCoin` from CCC (not old `ErrorTransactionInsufficientCoin`) + 8. `pnpm check:full` passes +**Plans**: 2 plans Plans: -- [ ] 05-01: TBD -- [ ] 05-02: TBD -- [ ] 05-03: TBD +- [ ] 05-01-PLAN.md — Implement IckbUdt extends udt.Udt in core (infoFrom, addCellDeps, typeScriptFrom); remove udtHandler from LogicManager/OwnedOwnerManager; add @ckb-ccc/udt dependency +- [ ] 05-02-PLAN.md — Delete UDT infrastructure from utils (UdtHandler, UdtManager, ErrorTransactionInsufficientCoin, UdtCell, findUdts, addUdts); update SDK to construct IckbUdt with code OutPoints; verify pnpm check:full ### Phase 6: SDK Completion Pipeline **Goal**: IckbSdk facade uses CCC-native fee completion pipeline; estimate() and maturity() continue working after SmartTransaction removal @@ -140,7 +143,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 | 1. SmartTransaction Removal (feature-slice) | 3/3 | Complete | 2026-02-22 | | 2. CCC Utility Adoption | 1/1 | Complete | 2026-02-23 | | 3. CCC Udt Integration Investigation | 2/2 | Complete | 2026-02-24 | -| 4. Deprecated CCC API Replacement | 0/2 | Not started | - | -| 5. @ickb/core UDT Refactor | 0/3 | Not started | - | +| 4. Deprecated CCC API Replacement | 1/1 | Complete | 2026-02-26 | +| 5. @ickb/core UDT Refactor | 0/2 | Not started | - | | 6. SDK Completion Pipeline | 0/2 | Not started | - | | 7. Full Stack Verification | 0/1 | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 2d2714f..1a8a8d3 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -1,3 +1,16 @@ +--- +gsd_state_version: 1.0 +milestone: v1.0 +milestone_name: milestone +status: unknown +last_updated: "2026-02-26T16:31:09.127Z" +progress: + total_phases: 5 + completed_phases: 5 + total_plans: 9 + completed_plans: 9 +--- + # Project State ## Project Reference @@ -5,23 +18,23 @@ See: .planning/PROJECT.md (updated 2026-02-20) **Core value:** Clean, CCC-aligned library packages published to npm that frontends can depend on to interact with iCKB contracts -- no Lumos, no abandoned abstractions, no duplicated functionality with CCC. -**Current focus:** Phase 4: Deprecated CCC API Replacement +**Current focus:** Phase 5: @ickb/core UDT Refactor ## Current Position -Phase: 3 of 7 (CCC Udt Integration Investigation) -- COMPLETE -Plan: 2 of 2 in current phase (all plans complete) -Status: Phase 3 complete, ready for Phase 4 -Last activity: 2026-02-24 -- Plan 03-02 decision document complete (execute-phase) +Phase: 5 of 7 (@ickb/core UDT Refactor) -- Complete +Plan: 2 of 2 in current phase (all complete) +Status: Phase 5 complete -- all UDT infrastructure deleted, SDK rewired +Last activity: 2026-02-26 -- Completed 05-02-PLAN.md (delete UDT infra + wire SDK) -Progress: [████░░░░░░] 43% +Progress: [██████████] 100% ## Performance Metrics **Velocity:** -- Total plans completed: 6 -- Average duration: ~12min -- Total execution time: 1.2 hours +- Total plans completed: 9 +- Average duration: ~10min +- Total execution time: 1.5 hours **By Phase:** @@ -30,10 +43,12 @@ Progress: [████░░░░░░] 43% | 01 | 3/3 | 52min | 17min | | 02 | 1/1 | 7min | 7min | | 03 | 2/2 | 9min | 4.5min | +| 04 | 1/1 | 4min | 4min | +| 05 | 2/2 | 11min | 5.5min | **Recent Trend:** -- Last 5 plans: 01-03 (~16min), 02-01 (~7min), 03-01 (~5min), 03-02 (~4min) -- Trend: accelerating +- Last 5 plans: 03-02 (~4min), 04-01 (~4min), 05-01 (~7min), 05-02 (~4min) +- Trend: stable at ~5min per plan *Updated after each plan completion* @@ -70,6 +85,31 @@ Recent decisions affecting current work: - [03-02]: Conservation law: accurate balance reporting only; on-chain script is authoritative enforcer; build-time validation optional later - [03-02]: Cell discovery boundary: infoFrom values cells already in transaction; callers (LogicManager/OwnedOwnerManager) find and add receipt/deposit cells - [03-02]: UdtHandler interface and UdtManager class to be deleted in Phase 5, replaced by udt.Udt type +- [04-context]: DaoManager never had UdtHandler parameter -- @ickb/dao has no UDT-related code, no changes needed in Phase 4 +- [04-context]: OrderManager gets udtScript: ccc.Script (not udt.Udt) -- simpler pattern than Phase 3 anticipated +- [04-context]: UDT cellDeps removed from OrderManager -- caller/CCC Udt manages externally during balance completion +- [04-context]: No deprecated CCC API calls in dao or order packages -- those calls (ccc.udtBalanceFrom) are in utils (UdtManager) and core (IckbUdtManager), handled in Phase 5 +- [04-context]: Pattern established: managers receive plain ccc.Script for UDT type identification; udt.Udt instance lives at SDK/caller level +- [04-context]: Phase 3 decision doc corrected (DaoManager row removed, OrderManager updated, Phase 4 guidance rewritten) +- [04-01]: OrderManager receives ccc.Script (not udt.Udt) -- simpler than Phase 3 anticipated +- [04-01]: UDT cellDeps are caller responsibility -- documented via JSDoc @remarks on mint/addMatch/melt +- [05-context]: IckbUdt uses individual code deps (xUDT + Logic OutPoints), not dep group -- CCC author dislikes dep groups for breaking semantics +- [05-context]: Override addCellDeps on IckbUdt to add both code deps; other managers keep dep groups for now (mixed coexistence OK) +- [05-context]: Drop compressState feature -- CCC completeInputsByBalance handles completion; no wrapper needed +- [05-context]: Accept CCC's ErrorUdtInsufficientCoin; delete ErrorTransactionInsufficientCoin from utils; callers format messages +- [05-context]: Delete findUdts/addUdts/UdtCell/isUdtSymbol -- all internal to UdtManager, no external consumers +- [05-context]: LogicManager/OwnedOwnerManager: remove udtHandler param + cellDeps calls (Phase 4 pattern); UdtHandler interface has zero consumers → delete +- [05-context]: calculateScript renamed to IckbUdt.typeScriptFrom (static, same params) +- [05-context]: ValueComponents kept as-is for Phase 5; redesign deferred (ambiguous naming in multi-UDT context) +- [05-context]: Phase 5 handles SDK error handling updates (not deferred to Phase 6) +- [05-context]: CCC isUdt length check (>=16 bytes) equivalent to old >=34 hex chars -- no iCKB-specific reason for old threshold +- [05-context]: Matching bot infoFrom caching concern noted for researcher -- CCC Client.cache handles header dedup +- [05-01]: IckbUdt.infoFrom handles three cell types: xUDT (positive), receipt (positive via ickbValue), deposit (negative via ickbValue) +- [05-01]: addCellDeps adds individual code deps (xUDT + Logic OutPoints), not dep group +- [05-01]: CellAny cast to Cell for daoManager.isDeposit is safe: input cells are Cell instances, output cells gated by !cell.outPoint check +- [05-02]: IckbUdt.typeScriptFrom computes type script dynamically from raw UDT and Logic scripts (not hardcoded) +- [05-02]: Devnet code OutPoints fallback to cellDeps[0].outPoint -- pragmatic since devnet deps are typically depType: code +- [05-02]: ErrorTransactionInsufficientCoin had zero catch blocks across entire codebase -- clean deletion with no migration ### Pending Todos @@ -85,6 +125,7 @@ None yet. ## Session Continuity -Last session: 2026-02-24 -Stopped at: Completed 03-02-PLAN.md (Phase 3 complete) -Resume file: Phase 4 planning needed +Last session: 2026-02-26 +Stopped at: Completed 05-02-PLAN.md -- Phase 5 complete +Resume file: N/A (all phases complete) +Next action: PR preparation or Phase 6 planning diff --git a/.planning/phases/03-ccc-udt-integration-investigation/03-DECISION.md b/.planning/phases/03-ccc-udt-integration-investigation/03-DECISION.md index 94048c4..9bf35b9 100644 --- a/.planning/phases/03-ccc-udt-integration-investigation/03-DECISION.md +++ b/.planning/phases/03-ccc-udt-integration-investigation/03-DECISION.md @@ -260,15 +260,17 @@ This code sketch is derived from: | Current | Replacement | Package | |---------|-------------|---------| -| `UdtHandler` interface | Deleted -- replaced by `udt.Udt` instance | `@ickb/utils` | -| `UdtManager` class | Deleted -- replaced by `udt.Udt` base class | `@ickb/utils` | -| `IckbUdtManager` class | `IckbUdt extends udt.Udt` | `@ickb/core` | -| `DaoManager` constructor `udtHandler` param | `DaoManager` receives `udt.Udt` instance | `@ickb/dao` | -| `OrderManager` constructor `udtHandler` param | `OrderManager` receives `udt.Udt` instance | `@ickb/order` | -| `tx.getInputsUdtBalance()` (deprecated) | `ickbUdt.getInputsInfo(client, tx)` | All packages | -| `tx.getOutputsUdtBalance()` (deprecated) | `ickbUdt.getOutputsInfo(client, tx)` | All packages | -| `tx.completeInputsByUdt()` (deprecated) | `ickbUdt.completeInputsByBalance(tx, signer)` | All packages | -| `ccc.udtBalanceFrom()` (deprecated) | `udt.Udt.balanceFromUnsafe(outputData)` | All packages | +| `UdtHandler` interface | Deleted (Phase 5) | `@ickb/utils` | +| `UdtManager` class | Deleted (Phase 5) | `@ickb/utils` | +| `IckbUdtManager` class | `IckbUdt extends udt.Udt` (Phase 5) | `@ickb/core` | +| `OrderManager` constructor `udtHandler: UdtHandler` | `udtScript: ccc.Script` (Phase 4) | `@ickb/order` | +| `tx.addCellDeps(this.udtHandler.cellDeps)` in OrderManager | Removed -- caller/CCC Udt handles UDT cellDeps (Phase 4) | `@ickb/order` | +| `tx.getInputsUdtBalance()` (deprecated) | `ickbUdt.getInputsInfo(client, tx)` (Phase 5) | `@ickb/core` | +| `tx.getOutputsUdtBalance()` (deprecated) | `ickbUdt.getOutputsInfo(client, tx)` (Phase 5) | `@ickb/core` | +| `tx.completeInputsByUdt()` (deprecated) | `ickbUdt.completeInputsByBalance(tx, signer)` (Phase 5) | `@ickb/core` | +| `ccc.udtBalanceFrom()` (deprecated) | `udt.Udt.balanceFromUnsafe(outputData)` (Phase 5) | `@ickb/utils`, `@ickb/core` | + +**Note:** `DaoManager` never had a `UdtHandler` parameter -- it only manages DAO operations (deposit, withdrawal request, withdraw). No UDT-related changes needed in `@ickb/dao`. ### What CCC Features It Gains @@ -366,24 +368,29 @@ The correct pattern is: ## Implementation Guidance for Phases 4-5 -### Phase 4: dao and order Packages (Deprecated API Replacement) +### Phase 4: order Package (UdtHandler Replacement) -Replace deprecated CCC API calls with `udt.Udt` instance methods: +*Updated 2026-02-26 based on Phase 4 discuss-phase. See `04-CONTEXT.md` for full decisions.* -| Deprecated API | Replacement | Notes | -|----------------|-------------|-------| -| `ccc.udtBalanceFrom(outputData)` | `udt.Udt.balanceFromUnsafe(outputData)` | Static method on Udt class | -| `tx.getInputsUdtBalance(client)` | `ickbUdt.getInputsInfo(client, tx)` | Returns `UdtInfo` not `[FixedPoint, FixedPoint]` | -| `tx.getOutputsUdtBalance(client)` | `ickbUdt.getOutputsInfo(client, tx)` | Returns `UdtInfo` not `[FixedPoint, FixedPoint]` | -| `tx.completeInputsByUdt(signer, handler)` | `ickbUdt.completeInputsByBalance(tx, signer)` | On Udt instance, not Transaction | +**Scope clarification:** `@ickb/dao` has no `UdtHandler` references and no deprecated CCC API calls -- it only manages DAO operations. No changes needed. `@ickb/order` uses `UdtHandler` for two things: the UDT type script (`.script`) and cellDeps (`.cellDeps`). Neither package calls deprecated CCC APIs like `udtBalanceFrom` directly -- those calls are in `@ickb/utils`'s `UdtManager` class (deleted in Phase 5). -Manager constructor changes: -- `DaoManager` and `OrderManager` receive a `udt.Udt` instance instead of `UdtHandler` -- The `UdtHandler.cellDeps` pattern is replaced by `udt.addCellDeps(tx)` (inherited from `ssri.Trait`) -- Balance queries use the Udt instance: `this.udt.getInputsInfo(client, tx)` instead of `this.udtHandler.getInputsUdtBalance(client, tx)` +**OrderManager constructor change:** +- `udtHandler: UdtHandler` replaced with `udtScript: ccc.Script` +- All 9 `this.udtHandler.script` references become `this.udtScript` +- No `@ckb-ccc/udt` dependency needed -- `ccc.Script` comes from existing `@ckb-ccc/core` + +**UDT cellDeps removal:** +- Remove all `tx.addCellDeps(this.udtHandler.cellDeps)` calls from `mint()`, `addMatch()`, `melt()` +- UDT cellDeps are caller responsibility -- CCC Udt adds its own cellDeps during balance completion +- OrderManager keeps `tx.addCellDeps(this.cellDeps)` for its own order script deps +- Add JSDoc note on affected methods: caller must ensure UDT cellDeps are present + +**Pattern established:** Managers receive plain `ccc.Script` for UDT type identification. The `udt.Udt` instance (including `IckbUdt`) lives at SDK/caller level and handles cellDeps + balance operations externally. This is simpler than initially anticipated. ### Phase 5: core Package (IckbUdt Implementation) +*Updated 2026-02-26: Phase 5 discuss-phase expanded scope significantly. See `05-CONTEXT.md` in `.planning/phases/05-ickb-core-udt-refactor/` for authoritative decisions including: individual code deps (not dep group), LogicManager/OwnedOwnerManager udtHandler removal, typeScriptFrom rename, findUdts/UdtCell/ErrorTransactionInsufficientCoin deletion, CCC error adoption. The guidance below is the Phase 3 starting point; 05-CONTEXT.md supersedes it.* + **Create `IckbUdt extends udt.Udt`** in `packages/core/src/udt.ts`: ``` @@ -417,7 +424,8 @@ Override: **Update SDK (`packages/sdk/src/sdk.ts`):** - Construct `IckbUdt` instance instead of `IckbUdtManager` -- Pass `IckbUdt` instance to managers that need UDT operations +- Pass `ickbUdt.script` (plain `ccc.Script`) to `OrderManager` constructor, not the `IckbUdt` instance itself +- `IckbUdt` instance used directly by SDK for balance queries and completion -- not propagated to dao/order managers - Update balance queries from `[FixedPoint, FixedPoint]` tuple to `UdtInfo` destructuring --- diff --git a/.planning/phases/04-deprecated-ccc-api-replacement/04-01-PLAN.md b/.planning/phases/04-deprecated-ccc-api-replacement/04-01-PLAN.md new file mode 100644 index 0000000..fcb42b1 --- /dev/null +++ b/.planning/phases/04-deprecated-ccc-api-replacement/04-01-PLAN.md @@ -0,0 +1,214 @@ +--- +phase: 04-deprecated-ccc-api-replacement +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - packages/order/src/order.ts + - packages/sdk/src/constants.ts +autonomous: true +requirements: + - SMTX-05 + +must_haves: + truths: + - "OrderManager constructor accepts udtScript: ccc.Script as its third parameter" + - "OrderManager methods do not add UDT cellDeps to transactions" + - "@ickb/order does not import UdtHandler from @ickb/utils" + - "SDK constructs OrderManager with ickbUdt.script (a ccc.Script), not the full IckbUdtManager instance" + - "pnpm check:full passes with zero errors" + artifacts: + - path: "packages/order/src/order.ts" + provides: "OrderManager with udtScript: ccc.Script parameter, no UDT cellDeps" + contains: "public readonly udtScript: ccc.Script" + - path: "packages/sdk/src/constants.ts" + provides: "SDK caller passing ickbUdt.script to OrderManager" + contains: "ickbUdt.script" + key_links: + - from: "packages/sdk/src/constants.ts" + to: "packages/order/src/order.ts" + via: "OrderManager constructor call" + pattern: "new OrderManager\\(.*ickbUdt\\.script\\)" + - from: "packages/order/src/order.ts" + to: "@ckb-ccc/core" + via: "ccc.Script type for udtScript" + pattern: "udtScript: ccc\\.Script" +--- + + +Replace UdtHandler dependency in @ickb/order with plain ccc.Script; remove UDT cellDeps management from OrderManager; update SDK caller site. + +Purpose: OrderManager only needs the UDT type script for cell identification (type script matching, output cell construction). The full UdtHandler interface with balance/completion methods is unnecessary coupling. UDT cellDeps management is moved to the caller/CCC Udt during balance completion, aligning with CCC's completion pipeline pattern. + +Output: Modified order.ts and constants.ts; verified @ickb/dao is already clean; verified Phase 3 decision doc already corrected. + + + +@/home/node/.claude/get-shit-done/workflows/execute-plan.md +@/home/node/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/04-deprecated-ccc-api-replacement/04-CONTEXT.md +@.planning/phases/04-deprecated-ccc-api-replacement/04-RESEARCH.md + + + + +From packages/order/src/order.ts (BEFORE -- current state): +```typescript +import { type UdtHandler } from "@ickb/utils"; + +export class OrderManager implements ScriptDeps { + constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly udtHandler: UdtHandler, // CHANGE THIS + ) {} +} +``` + +9 udtHandler references to replace (verified line-by-line): +- Line 7: `type UdtHandler,` in import -> REMOVE from import +- Line 22: JSDoc `@param udtHandler` -> `@param udtScript` +- Line 27: constructor `public readonly udtHandler: UdtHandler` -> `public readonly udtScript: ccc.Script` +- Line 42: `this.udtHandler.script` -> `this.udtScript` (isOrder) +- Line 190: `tx.addCellDeps(this.udtHandler.cellDeps)` -> DELETE LINE (mint) +- Line 196: `this.udtHandler.script` -> `this.udtScript` (mint output) +- Line 229: `tx.addCellDeps(this.udtHandler.cellDeps)` -> DELETE LINE (addMatch) +- Line 236: `this.udtHandler.script` -> `this.udtScript` (addMatch output) +- Line 519: `tx.addCellDeps(this.udtHandler.cellDeps)` -> DELETE LINE (melt) +- Line 635: `this.udtHandler.script` -> `this.udtScript` (findSimpleOrders) + +From packages/sdk/src/constants.ts line 78: +```typescript +const order = new OrderManager(d.order.script, d.order.cellDeps, ickbUdt); +// ickbUdt is IckbUdtManager which implements UdtHandler +// Change to: ickbUdt.script +``` + +From @ickb/utils (UdtHandler interface -- reference only, NOT modified): +```typescript +export interface UdtHandler extends ScriptDeps { + script: ccc.Script; + cellDeps: ccc.CellDep[]; +} +``` + + + + + + + Task 1: Replace UdtHandler with udtScript in OrderManager and update SDK caller + packages/order/src/order.ts, packages/sdk/src/constants.ts + +In `packages/order/src/order.ts`: + +1. Remove `type UdtHandler,` from the `@ickb/utils` import statement (line 7). Keep all other imports (`BufferedGenerator`, `defaultFindCellsLimit`, `ExchangeRatio`, `ScriptDeps`, `ValueComponents`). + +2. Update JSDoc on line 22: change `@param udtHandler - The handler for UDT (User Defined Token).` to `@param udtScript - The UDT (User Defined Token) type script.` + +3. Update constructor parameter on line 27: change `public readonly udtHandler: UdtHandler,` to `public readonly udtScript: ccc.Script,` + +4. In `isOrder()` (line 42): change `this.udtHandler.script` to `this.udtScript` + +5. In `mint()`: + - DELETE line 190: `tx.addCellDeps(this.udtHandler.cellDeps);` + - Change line 196: `type: this.udtHandler.script,` to `type: this.udtScript,` + - Add JSDoc `@remarks` to mint()'s existing JSDoc block (after the existing content, before `@param`): `@remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via CCC Udt balance completion).` + +6. In `addMatch()`: + - DELETE line 229: `tx.addCellDeps(this.udtHandler.cellDeps);` + - Change line 236: `type: this.udtHandler.script,` to `type: this.udtScript,` + - Add JSDoc `@remarks` to addMatch()'s existing JSDoc block: `@remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via CCC Udt balance completion).` + +7. In `melt()`: + - DELETE line 519: `tx.addCellDeps(this.udtHandler.cellDeps);` + - Add JSDoc `@remarks` to melt()'s existing JSDoc block: `@remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via CCC Udt balance completion).` + +8. In `findSimpleOrders()` (line 635): change `script: this.udtHandler.script,` to `script: this.udtScript,` + +In `packages/sdk/src/constants.ts`: + +9. Change line 78 from: + `const order = new OrderManager(d.order.script, d.order.cellDeps, ickbUdt);` + to: + `const order = new OrderManager(d.order.script, d.order.cellDeps, ickbUdt.script);` + +Apply changes top-to-bottom within each file to avoid line number drift issues. + + + cd /workspaces/stack && pnpm check:full + + + - OrderManager constructor parameter is `udtScript: ccc.Script` (not `udtHandler: UdtHandler`) + - All 4 `.script` accesses simplified from `this.udtHandler.script` to `this.udtScript` + - All 3 `tx.addCellDeps(this.udtHandler.cellDeps)` lines deleted from mint/addMatch/melt + - `UdtHandler` no longer appears in @ickb/order imports + - JSDoc @remarks added to mint(), addMatch(), melt() about caller cellDeps responsibility + - SDK passes `ickbUdt.script` to OrderManager constructor + - `pnpm check:full` passes + + + + + Task 2: Verify @ickb/dao and Phase 3 decision doc are already clean + + +Verify (grep-based, no changes expected): + +1. Confirm @ickb/dao has no UdtHandler references: + `grep -r "UdtHandler\|udtHandler" packages/dao/src/` should return no matches. + +2. Confirm @ickb/dao has no deprecated CCC API calls: + `grep -r "udtBalanceFrom\|getInputsUdtBalance\|getOutputsUdtBalance\|completeInputsByUdt" packages/dao/src/` should return no matches. + +3. Confirm Phase 3 decision doc `03-DECISION.md` lines 369-388 already contain the correct Phase 4 guidance (updated 2026-02-26 in commit c7ba503): + - States DaoManager never had UdtHandler + - States OrderManager gets `udtScript: ccc.Script` + - States UDT cellDeps removed from OrderManager + If content is correct, no changes needed (verification only). + +4. Confirm @ickb/order no longer imports UdtHandler after Task 1: + `grep -r "UdtHandler" packages/order/src/` should return no matches. + +All four checks must pass. If any check fails, investigate and fix. These are expected to all pass (dao was always clean, decision doc was pre-corrected, order was cleaned in Task 1). + + + cd /workspaces/stack && grep -r "UdtHandler\|udtHandler" packages/dao/src/ packages/order/src/ ; echo "exit: $?" + + + - @ickb/dao confirmed clean: no UdtHandler references, no deprecated CCC API calls + - Phase 3 decision doc verified correct (no changes needed) + - @ickb/order confirmed clean: no remaining UdtHandler references after Task 1 + + + + + + +1. `pnpm check:full` passes -- all 5 library packages compile with zero type errors +2. `grep -r "UdtHandler\|udtHandler" packages/order/src/ packages/dao/src/` returns no matches +3. `grep -r "udtBalanceFrom\|getInputsUdtBalance\|getOutputsUdtBalance\|completeInputsByUdt" packages/dao/src/ packages/order/src/` returns no matches +4. `grep "udtScript: ccc.Script" packages/order/src/order.ts` confirms new parameter +5. `grep "ickbUdt.script" packages/sdk/src/constants.ts` confirms SDK caller update + + + +- OrderManager uses `udtScript: ccc.Script` instead of `udtHandler: UdtHandler` +- No UDT cellDeps added by OrderManager (3 lines deleted) +- @ickb/order has zero imports of UdtHandler +- SDK constructs OrderManager with `ickbUdt.script` +- @ickb/dao verified clean (no changes needed) +- Phase 3 decision doc verified correct (no changes needed) +- `pnpm check:full` passes + + + +After completion, create `.planning/phases/04-deprecated-ccc-api-replacement/04-01-SUMMARY.md` + diff --git a/.planning/phases/04-deprecated-ccc-api-replacement/04-01-SUMMARY.md b/.planning/phases/04-deprecated-ccc-api-replacement/04-01-SUMMARY.md new file mode 100644 index 0000000..8db6f50 --- /dev/null +++ b/.planning/phases/04-deprecated-ccc-api-replacement/04-01-SUMMARY.md @@ -0,0 +1,104 @@ +--- +phase: 04-deprecated-ccc-api-replacement +plan: 01 +subsystem: api +tags: [ccc, udt, order, refactor, decoupling] + +# Dependency graph +requires: + - phase: 03-ccc-udt-integration-investigation + provides: Decision to use ccc.Script for UDT type identification in managers +provides: + - OrderManager with udtScript: ccc.Script parameter (no UdtHandler dependency) + - UDT cellDeps removed from OrderManager (caller/CCC Udt responsibility) + - SDK caller site updated to pass ickbUdt.script +affects: [05-remaining-ccc-migration] + +# Tech tracking +tech-stack: + added: [] + patterns: [managers-receive-plain-script, caller-manages-udt-celldeps] + +key-files: + created: [] + modified: + - packages/order/src/order.ts + - packages/sdk/src/constants.ts + +key-decisions: + - "OrderManager receives ccc.Script (not udt.Udt) -- simpler than Phase 3 anticipated" + - "UDT cellDeps are caller responsibility -- documented via JSDoc @remarks on mint/addMatch/melt" + +patterns-established: + - "Manager plain-script pattern: managers receive plain ccc.Script for UDT type identification, udt.Udt instance lives at SDK/caller level" + - "Caller cellDeps pattern: transaction-building methods do not add UDT cellDeps; callers ensure them via CCC Udt balance completion" + +requirements-completed: [SMTX-05] + +# Metrics +duration: 4min +completed: 2026-02-26 +--- + +# Phase 4 Plan 01: Replace UdtHandler with udtScript Summary + +**OrderManager decoupled from UdtHandler: constructor takes ccc.Script, UDT cellDeps removed, SDK caller updated** + +## Performance + +- **Duration:** 4 min +- **Started:** 2026-02-26T11:11:01Z +- **Completed:** 2026-02-26T11:14:56Z +- **Tasks:** 2 +- **Files modified:** 2 + +## Accomplishments +- OrderManager constructor parameter changed from `udtHandler: UdtHandler` to `udtScript: ccc.Script` +- All 4 `this.udtHandler.script` accesses simplified to `this.udtScript` +- All 3 `tx.addCellDeps(this.udtHandler.cellDeps)` lines deleted from mint/addMatch/melt +- `UdtHandler` import removed from @ickb/order +- JSDoc `@remarks` added to mint(), addMatch(), melt() documenting caller cellDeps responsibility +- SDK caller updated to pass `ickbUdt.script` instead of `ickbUdt` +- @ickb/dao verified clean (no UdtHandler, no deprecated CCC APIs) +- Phase 3 decision doc verified correct (no changes needed) + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Replace UdtHandler with udtScript in OrderManager and update SDK caller** - `9e6e3d8` (feat) +2. **Task 2: Verify @ickb/dao and Phase 3 decision doc are already clean** - no commit (verification-only, no changes) + +## Files Created/Modified +- `packages/order/src/order.ts` - OrderManager: udtScript parameter, removed UDT cellDeps, updated JSDoc +- `packages/sdk/src/constants.ts` - SDK getConfig: passes ickbUdt.script to OrderManager constructor + +## Decisions Made +- OrderManager receives `ccc.Script` directly (not `udt.Udt`) -- simpler pattern than Phase 3 anticipated, since OrderManager only needs the type script for cell identification +- UDT cellDeps responsibility moved to caller, documented via JSDoc `@remarks` on affected methods + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered +None + +## User Setup Required +None - no external service configuration required. + +## Next Phase Readiness +- Phase 4 complete (single plan phase) +- Pattern established: managers receive plain `ccc.Script`, `udt.Udt` lives at caller level +- Ready for Phase 5: IckbUdt implementation in @ickb/core, UdtHandler/UdtManager deletion in @ickb/utils + +## Self-Check: PASSED + +- FOUND: packages/order/src/order.ts +- FOUND: packages/sdk/src/constants.ts +- FOUND: 04-01-SUMMARY.md +- FOUND: commit 9e6e3d8 + +--- +*Phase: 04-deprecated-ccc-api-replacement* +*Completed: 2026-02-26* diff --git a/.planning/phases/04-deprecated-ccc-api-replacement/04-CONTEXT.md b/.planning/phases/04-deprecated-ccc-api-replacement/04-CONTEXT.md new file mode 100644 index 0000000..8aaea70 --- /dev/null +++ b/.planning/phases/04-deprecated-ccc-api-replacement/04-CONTEXT.md @@ -0,0 +1,65 @@ +# Phase 4: Deprecated CCC API Replacement - Context + +**Gathered:** 2026-02-26 +**Status:** Ready for planning + + +## Phase Boundary + +Replace `UdtHandler` dependency in `@ickb/order` with a plain `ccc.Script` parameter; remove UDT cellDeps management from OrderManager (caller/CCC Udt handles it externally during balance completion); verify `@ickb/dao` has no deprecated CCC API calls (it doesn't); correct Phase 3 decision document inaccuracies. UdtHandler/UdtManager deletion happens in Phase 5, not here. + + + + +## Implementation Decisions + +### OrderManager parameter design +- Replace `udtHandler: UdtHandler` constructor parameter with `udtScript: ccc.Script` +- OrderManager only needs the UDT type script -- not the full `udt.Udt` class or `UdtHandler` interface +- No new `@ckb-ccc/udt` dependency needed on `@ickb/order` -- `ccc.Script` comes from existing `@ckb-ccc/core` +- Strict parameter swap: all 9 `this.udtHandler.script` references become `this.udtScript` +- Keep `ScriptDeps` interface on OrderManager (still describes its own script + cellDeps) +- Keep `ExchangeRatio`, `ValueComponents`, and other `@ickb/utils` imports unchanged +- Update JSDoc `@param` for the renamed parameter +- Do NOT audit unrelated imports -- only replace UdtHandler + +### CellDeps migration pattern +- Remove all `tx.addCellDeps(this.udtHandler.cellDeps)` calls from `mint()`, `addMatch()`, and `melt()` +- UDT cellDeps are now caller responsibility -- CCC Udt adds its own cellDeps during balance completion +- OrderManager still adds its own cellDeps via `tx.addCellDeps(this.cellDeps)` (order script deps) +- Add JSDoc note on `mint()`, `addMatch()`, `melt()`: caller must ensure UDT cellDeps are added to the transaction +- `ScriptDeps` interface unchanged -- still correctly describes OrderManager's own deps + +### Phase scope boundaries +- `@ickb/dao`: No changes needed. Already clean (no UdtHandler, no deprecated CCC APIs). Verified by `pnpm check:full` +- `@ickb/order`: Replace UdtHandler with udtScript, remove UDT cellDeps calls +- `@ickb/utils`: Leave UdtManager's 3 deprecated `ccc.udtBalanceFrom()` calls for Phase 5 (UdtManager is being deleted there) +- Update roadmap success criteria to reflect actual changes (UdtHandler replacement, not deprecated API removal in dao/order) +- Correct Phase 3 decision document: rewrite the "Implementation Guidance for Phase 4" section to match actual decisions (DaoManager never had UdtHandler; OrderManager gets ccc.Script not udt.Udt) +- Import audit of remaining @ickb/utils imports: out of scope + +### Claude's Discretion +- Exact JSDoc wording for the cellDeps caller-responsibility notes +- Whether to update the Phase 3 decision's replacement mapping table or restructure the section + + + + +## Specific Ideas + +- The pattern established here is simpler than Phase 3 anticipated: managers get `ccc.Script`, not `udt.Udt` instances. The `udt.Udt` instance (including `IckbUdt`) lives at the SDK/caller level, not in dao/order managers. +- Phase 5 should note that dao/order managers don't need `udt.Udt` propagated to them -- the Udt instance handles completion externally. + + + + +## Deferred Ideas + +None -- discussion stayed within phase scope. + + + +--- + +*Phase: 04-deprecated-ccc-api-replacement* +*Context gathered: 2026-02-26* diff --git a/.planning/phases/04-deprecated-ccc-api-replacement/04-RESEARCH.md b/.planning/phases/04-deprecated-ccc-api-replacement/04-RESEARCH.md new file mode 100644 index 0000000..a64a6f0 --- /dev/null +++ b/.planning/phases/04-deprecated-ccc-api-replacement/04-RESEARCH.md @@ -0,0 +1,365 @@ +# Phase 4: Deprecated CCC API Replacement - Research + +**Researched:** 2026-02-26 +**Domain:** CKB UDT handler interface replacement in @ickb/order package +**Confidence:** HIGH + +## Summary + +Phase 4 is a focused refactoring of `OrderManager` in `@ickb/order` to replace the `UdtHandler` interface dependency with a plain `ccc.Script` parameter. The scope is narrow and well-defined: 9 lines in one file (`order.ts`) need modification, one SDK caller site needs updating, and `@ickb/dao` needs verification (already confirmed clean). The Phase 3 decision document was already corrected in commit `c7ba503` during the discuss-phase -- this should be verified rather than re-done. + +This is a straightforward mechanical refactoring with no new dependencies, no architecture decisions, and no deprecated API calls to replace in the target packages. The pattern established here (managers receive `ccc.Script`, not `udt.Udt`) is the foundation that Phase 5 builds upon. + +**Primary recommendation:** Single plan covering OrderManager parameter swap, cellDeps removal, SDK caller update, dao verification, and Phase 3 doc verification. All changes are interdependent and should be in one atomic commit. + + +## User Constraints (from CONTEXT.md) + +### Locked Decisions +- Replace `udtHandler: UdtHandler` constructor parameter with `udtScript: ccc.Script` +- OrderManager only needs the UDT type script -- not the full `udt.Udt` class or `UdtHandler` interface +- No new `@ckb-ccc/udt` dependency needed on `@ickb/order` -- `ccc.Script` comes from existing `@ckb-ccc/core` +- Strict parameter swap: all 9 `this.udtHandler.script` references become `this.udtScript` +- Keep `ScriptDeps` interface on OrderManager (still describes its own script + cellDeps) +- Keep `ExchangeRatio`, `ValueComponents`, and other `@ickb/utils` imports unchanged +- Update JSDoc `@param` for the renamed parameter +- Do NOT audit unrelated imports -- only replace UdtHandler +- Remove all `tx.addCellDeps(this.udtHandler.cellDeps)` calls from `mint()`, `addMatch()`, and `melt()` +- UDT cellDeps are now caller responsibility -- CCC Udt adds its own cellDeps during balance completion +- OrderManager still adds its own cellDeps via `tx.addCellDeps(this.cellDeps)` (order script deps) +- Add JSDoc note on `mint()`, `addMatch()`, `melt()`: caller must ensure UDT cellDeps are added to the transaction +- `ScriptDeps` interface unchanged -- still correctly describes OrderManager's own deps +- `@ickb/dao`: No changes needed. Already clean (no UdtHandler, no deprecated CCC APIs). Verified by `pnpm check:full` +- `@ickb/order`: Replace UdtHandler with udtScript, remove UDT cellDeps calls +- `@ickb/utils`: Leave UdtManager's 3 deprecated `ccc.udtBalanceFrom()` calls for Phase 5 (UdtManager is being deleted there) +- Update roadmap success criteria to reflect actual changes (UdtHandler replacement, not deprecated API removal in dao/order) +- Correct Phase 3 decision document: rewrite the "Implementation Guidance for Phase 4" section to match actual decisions +- Import audit of remaining @ickb/utils imports: out of scope + +### Claude's Discretion +- Exact JSDoc wording for the cellDeps caller-responsibility notes +- Whether to update the Phase 3 decision's replacement mapping table or restructure the section + +### Deferred Ideas (OUT OF SCOPE) +None -- discussion stayed within phase scope. + + + +## Phase Requirements + +| ID | Description | Research Support | +|----|-------------|-----------------| +| SMTX-05 | UDT handler registration (`addUdtHandlers()`) is replaced by direct `Udt` instance usage or standalone utility functions | Phase 4 portion: OrderManager.udtHandler replaced with udtScript: ccc.Script; UDT cellDeps removed from OrderManager methods. The `UdtHandler` import is removed from @ickb/order. Full deletion of UdtHandler/UdtManager deferred to Phase 5. | + + +## Standard Stack + +### Core +| Library | Version | Purpose | Why Standard | +|---------|---------|---------|--------------| +| `@ckb-ccc/core` | Already in package.json (catalog:) | Provides `ccc.Script`, `ccc.CellDep`, `ccc.Transaction`, `ccc.TransactionLike` | Core CCC types -- `ccc.Script` is the replacement for `UdtHandler.script` | +| `@ickb/utils` | workspace:* | Provides `ScriptDeps`, `ExchangeRatio`, `ValueComponents`, `BufferedGenerator`, `defaultFindCellsLimit` | Shared types/utilities -- UdtHandler import removed but other imports remain | + +### Supporting +No new libraries needed. No dependency changes to package.json. + +### Alternatives Considered +| Instead of | Could Use | Tradeoff | +|------------|-----------|----------| +| `ccc.Script` | `udt.Udt` instance | Over-engineered -- OrderManager only needs type script for cell identification, not balance/completion capabilities. Locked decision: `ccc.Script`. | + +**Installation:** +No installation needed. All required types already available from existing `@ckb-ccc/core` dependency. + +## Architecture Patterns + +### Current Structure (Before Phase 4) +``` +packages/order/src/ + order.ts # OrderManager class -- 9 udtHandler references to change + cells.ts # OrderCell, MasterCell, OrderGroup -- no changes needed + entities.ts # OrderData, Info, Ratio -- no changes needed + index.ts # barrel export -- no changes needed +``` + +### Pattern 1: Manager Receives Plain Script +**What:** OrderManager constructor takes `udtScript: ccc.Script` instead of `udtHandler: UdtHandler`. The manager only uses the UDT type script for cell identification (type script matching in `isOrder`, `findSimpleOrders`, output cell construction in `mint`, `addMatch`). +**When to use:** When a manager needs to identify cells by type script but does not need balance calculation, completion, or cellDeps management for that script. +**Example:** +```typescript +// Before (current): +constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly udtHandler: UdtHandler, +) {} + +// After (Phase 4): +constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly udtScript: ccc.Script, +) {} +``` + +### Pattern 2: Caller-Managed CellDeps +**What:** OrderManager no longer adds UDT cellDeps to transactions. The UDT's cellDeps are the caller's responsibility -- CCC's `udt.Udt` adds its own cellDeps during balance completion (`Trait.addCellDeps(tx)`), so OrderManager does not need to duplicate this. +**When to use:** When a component constructs partial transactions that are completed by a higher-level orchestrator (SDK) using CCC's completion pipeline. +**Example:** +```typescript +// Before (current) -- mint(): +tx.addCellDeps(this.cellDeps); +tx.addCellDeps(this.udtHandler.cellDeps); // REMOVE this line + +// After (Phase 4) -- mint(): +tx.addCellDeps(this.cellDeps); +// UDT cellDeps handled by caller/CCC Udt during balance completion +``` + +### Pattern 3: SDK Caller Passes Script +**What:** The SDK's `getConfig()` constructs `OrderManager` with `ickbUdt.script` (a `ccc.Script`) instead of the full `IckbUdtManager` instance. +**Example:** +```typescript +// Before (packages/sdk/src/constants.ts:78): +const order = new OrderManager(d.order.script, d.order.cellDeps, ickbUdt); + +// After: +const order = new OrderManager(d.order.script, d.order.cellDeps, ickbUdt.script); +``` + +### Anti-Patterns to Avoid +- **Passing full UdtHandler/UdtManager to managers that only need Script:** Over-couples components. Managers should receive the minimum data they need. +- **Adding UDT cellDeps in multiple places:** Creates duplication and ordering issues. CCC's completion pipeline handles cellDeps via `Trait.addCellDeps`. +- **Changing cells.ts or entities.ts:** These files have no UdtHandler dependency. Do not modify them. + +## Don't Hand-Roll + +| Problem | Don't Build | Use Instead | Why | +|---------|-------------|-------------|-----| +| UDT type script identification | Custom `UdtHandler` interface with `.script` property | Plain `ccc.Script` parameter | `ccc.Script` already has `.eq()` for comparison -- no wrapper needed | +| UDT cellDeps management in partial transactions | `tx.addCellDeps(udtHandler.cellDeps)` in every method | CCC's `udt.Udt.addCellDeps(tx)` during completion | CCC manages its own deps -- managers should not duplicate | + +**Key insight:** OrderManager's relationship to UDT is purely type-script-based identification. It checks "is this cell a UDT cell?" and "construct a UDT-typed output cell". Both operations only need `ccc.Script`, not the full `UdtHandler` interface with balance/completion methods. + +## Common Pitfalls + +### Pitfall 1: Forgetting the SDK Caller Site +**What goes wrong:** Changing `OrderManager` constructor but not updating `packages/sdk/src/constants.ts:78` where `new OrderManager(d.order.script, d.order.cellDeps, ickbUdt)` passes the full `IckbUdtManager` instance. +**Why it happens:** The change is in `@ickb/order` but the caller is in `@ickb/sdk`, a different package. +**How to avoid:** Update `constants.ts:78` to pass `ickbUdt.script` instead of `ickbUdt`. TypeScript will catch this at compile time since `IckbUdtManager` is not assignable to `ccc.Script`. +**Warning signs:** Type error during `pnpm check:full` -- "Argument of type 'IckbUdtManager' is not assignable to parameter of type 'Script'". + +### Pitfall 2: Missing the Import Removal +**What goes wrong:** Removing all `udtHandler` usage but leaving `type UdtHandler` in the import statement on line 7 of `order.ts`. +**Why it happens:** Mechanical replacement of property accesses misses the import declaration. +**How to avoid:** After replacing all 9 references, remove `type UdtHandler` from the import statement. The linter will also catch unused imports. +**Warning signs:** `@typescript-eslint/no-unused-vars` or `no-unused-imports` lint error. + +### Pitfall 3: Incorrect Reference Count +**What goes wrong:** Thinking there are only 4 `.script` references when there are actually 9 total `udtHandler` mentions (including JSDoc, constructor parameter, `.script` accesses, and `.cellDeps` accesses). +**Why it happens:** Counting only one property pattern instead of all mentions. +**How to avoid:** The 9 references break down as: + - Line 22: JSDoc `@param udtHandler` -> update to `@param udtScript` + - Line 27: Constructor `public readonly udtHandler: UdtHandler` -> `public readonly udtScript: ccc.Script` + - Line 42: `this.udtHandler.script` -> `this.udtScript` (isOrder) + - Line 190: `this.udtHandler.cellDeps` -> DELETE LINE (mint) + - Line 196: `this.udtHandler.script` -> `this.udtScript` (mint output) + - Line 229: `this.udtHandler.cellDeps` -> DELETE LINE (addMatch) + - Line 236: `this.udtHandler.script` -> `this.udtScript` (addMatch output) + - Line 519: `this.udtHandler.cellDeps` -> DELETE LINE (melt) + - Line 635: `this.udtHandler.script` -> `this.udtScript` (findSimpleOrders filter) + +### Pitfall 4: Phase 3 Decision Doc Re-correction +**What goes wrong:** Attempting to re-correct the Phase 3 decision document when it was already corrected in commit `c7ba503` during the discuss-phase. +**Why it happens:** The roadmap success criterion #5 says to correct it, but the correction was applied proactively. +**How to avoid:** Verify the current content of `03-DECISION.md` lines 369-388 already contains the correct Phase 4 guidance (updated 2026-02-26). If correct, document verification rather than making changes. +**Warning signs:** If lines 369-388 mention "DaoManager UdtHandler replacement" or "udt.Udt instance" for OrderManager, correction is still needed. Current content correctly says neither. + +### Pitfall 5: Breaking @ickb/core Callers of UdtHandler +**What goes wrong:** Modifying `UdtHandler` interface or `UdtManager` class in `@ickb/utils` during Phase 4. +**Why it happens:** Desire to clean up the source of `UdtHandler`. +**How to avoid:** Phase 4 only touches `@ickb/order` (consumer) and `@ickb/sdk` (caller). The `UdtHandler` interface and `UdtManager` class in `@ickb/utils` remain untouched until Phase 5. `@ickb/core`'s `LogicManager` and `OwnedOwnerManager` still use `udtHandler: UdtHandler` -- they are out of scope. + +## Code Examples + +Verified patterns from codebase investigation: + +### OrderManager Constructor Change +```typescript +// packages/order/src/order.ts +// Source: Verified from current codebase line 16-28 + +import { ccc } from "@ckb-ccc/core"; +import { + BufferedGenerator, + defaultFindCellsLimit, + type ExchangeRatio, + type ScriptDeps, + // NOTE: UdtHandler import REMOVED + type ValueComponents, +} from "@ickb/utils"; + +export class OrderManager implements ScriptDeps { + /** + * Creates an instance of OrderManager. + * + * @param script - The order script. + * @param cellDeps - The cell dependencies for the order. + * @param udtScript - The UDT (User Defined Token) type script. + */ + constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly udtScript: ccc.Script, + ) {} +``` + +### isOrder Method Change +```typescript +// packages/order/src/order.ts +// Source: Verified from current codebase line 39-44 + +isOrder(cell: ccc.Cell): boolean { + return ( + cell.cellOutput.lock.eq(this.script) && + Boolean(cell.cellOutput.type?.eq(this.udtScript)) // was: this.udtHandler.script + ); +} +``` + +### mint() cellDeps Removal and JSDoc Update +```typescript +// packages/order/src/order.ts +// Source: Verified from current codebase lines 154-209 + +/** + * Mints a new order cell and appends it to the transaction. + * + * ...existing JSDoc... + * + * @remarks Caller must ensure UDT cellDeps are added to the transaction + * (e.g., via CCC Udt balance completion). + */ +mint( + txLike: ccc.TransactionLike, + lock: ccc.Script, + info: InfoLike, + amounts: ValueComponents, +): ccc.Transaction { + const tx = ccc.Transaction.from(txLike); + // ...data creation... + + tx.addCellDeps(this.cellDeps); + // REMOVED: tx.addCellDeps(this.udtHandler.cellDeps); + + const position = tx.addOutput( + { + lock: this.script, + type: this.udtScript, // was: this.udtHandler.script + }, + data.toBytes(), + ); + // ...rest unchanged... +} +``` + +### SDK Caller Update +```typescript +// packages/sdk/src/constants.ts +// Source: Verified from current codebase line 78 + +// Before: +const order = new OrderManager(d.order.script, d.order.cellDeps, ickbUdt); + +// After: +const order = new OrderManager(d.order.script, d.order.cellDeps, ickbUdt.script); +``` + +## State of the Art + +| Old Approach | Current Approach | When Changed | Impact | +|--------------|------------------|--------------|--------| +| `UdtHandler` interface with script + cellDeps + balance methods | Plain `ccc.Script` for type identification; `udt.Udt` for cellDeps/balance at SDK level | Phase 4 (this phase) | Managers are simpler; UDT completion logic centralized in CCC | +| `tx.addCellDeps(udtHandler.cellDeps)` in every method | CCC Udt handles its own cellDeps during completion | Phase 4 (this phase) | No duplicate cellDep management; aligns with CCC patterns | + +**Deprecated/outdated:** +- `UdtHandler` interface: Still exists in `@ickb/utils` -- deleted in Phase 5. Phase 4 only removes its usage from `@ickb/order`. +- `UdtManager` class: Still exists in `@ickb/utils` -- deleted in Phase 5. Not touched in Phase 4. + +## Precise Change Map + +### File: `packages/order/src/order.ts` + +| Line | Current | After | Category | +|------|---------|-------|----------| +| 7 | `type UdtHandler,` | (remove from import) | Import cleanup | +| 22 | `@param udtHandler - The handler for UDT` | `@param udtScript - The UDT type script.` | JSDoc | +| 27 | `public readonly udtHandler: UdtHandler,` | `public readonly udtScript: ccc.Script,` | Constructor | +| 42 | `this.udtHandler.script` | `this.udtScript` | isOrder() | +| 190 | `tx.addCellDeps(this.udtHandler.cellDeps);` | (remove line) | mint() cellDeps | +| 196 | `type: this.udtHandler.script,` | `type: this.udtScript,` | mint() output | +| 229 | `tx.addCellDeps(this.udtHandler.cellDeps);` | (remove line) | addMatch() cellDeps | +| 236 | `type: this.udtHandler.script,` | `type: this.udtScript,` | addMatch() output | +| 519 | `tx.addCellDeps(this.udtHandler.cellDeps);` | (remove line) | melt() cellDeps | +| 635 | `script: this.udtHandler.script,` | `script: this.udtScript,` | findSimpleOrders() | + +Additionally: JSDoc `@remarks` notes on `mint()`, `addMatch()`, `melt()` for caller responsibility. + +### File: `packages/sdk/src/constants.ts` + +| Line | Current | After | Category | +|------|---------|-------|----------| +| 78 | `new OrderManager(d.order.script, d.order.cellDeps, ickbUdt)` | `new OrderManager(d.order.script, d.order.cellDeps, ickbUdt.script)` | Caller update | + +### Files NOT Changed (verified clean) +- `packages/dao/src/dao.ts` -- No UdtHandler, no deprecated CCC APIs +- `packages/dao/src/cells.ts` -- No UdtHandler, no deprecated CCC APIs +- `packages/dao/src/index.ts` -- No UdtHandler +- `packages/order/src/cells.ts` -- No UdtHandler +- `packages/order/src/entities.ts` -- No UdtHandler +- `packages/order/src/index.ts` -- No UdtHandler +- `packages/utils/src/udt.ts` -- UdtHandler/UdtManager remain (Phase 5 scope) +- `packages/core/src/logic.ts` -- udtHandler references remain (Phase 5 scope) +- `packages/core/src/owned_owner.ts` -- udtHandler references remain (Phase 5 scope) +- `packages/core/src/udt.ts` -- IckbUdtManager remains (Phase 5 scope) + +### Phase 3 Decision Doc Verification +- `03-DECISION.md` lines 369-388: Already corrected in commit `c7ba503` (2026-02-26) +- Content now correctly states: DaoManager never had UdtHandler; OrderManager gets `udtScript: ccc.Script`; UDT cellDeps removed from OrderManager +- Action: Verify content is correct, no additional changes needed + +## Open Questions + +1. **Line numbers may shift after edits** + - What we know: Line numbers referenced above are from the current file state + - What's unclear: Removing 3 cellDeps lines shifts all subsequent line numbers + - Recommendation: Apply changes top-to-bottom or use pattern matching rather than absolute line numbers + +2. **SDK type return change** + - What we know: `getConfig()` return type includes `order: OrderManager`. After this change, `OrderManager.udtScript` is `ccc.Script` instead of `OrderManager.udtHandler` being `UdtHandler` + - What's unclear: Whether any SDK callers access `order.udtHandler` directly + - Recommendation: Search for `order.udtHandler` usage in SDK tests/apps before finalizing. None found in library packages. + +## Sources + +### Primary (HIGH confidence) +- Direct codebase inspection of `packages/order/src/order.ts` (all 9 udtHandler references verified line by line) +- Direct codebase inspection of `packages/dao/src/dao.ts`, `packages/dao/src/cells.ts` (confirmed zero UdtHandler/deprecated API usage) +- Direct codebase inspection of `packages/sdk/src/constants.ts:78` (single OrderManager construction site in libraries) +- Git log of `03-DECISION.md` (correction already in commit `c7ba503`) + +### Secondary (MEDIUM confidence) +- Phase 3 decision document `03-DECISION.md` sections on Phase 4 guidance (verified current and accurate) + +### Tertiary (LOW confidence) +- None -- all findings verified from codebase + +## Metadata + +**Confidence breakdown:** +- Standard stack: HIGH - No new dependencies, all types from existing `@ckb-ccc/core` +- Architecture: HIGH - Mechanical refactoring with exact change map verified line-by-line +- Pitfalls: HIGH - All pitfalls discovered from codebase analysis (cross-package caller, import cleanup, reference count) + +**Research date:** 2026-02-26 +**Valid until:** Indefinite -- this is a mechanical refactoring with no external dependency concerns diff --git a/.planning/phases/04-deprecated-ccc-api-replacement/04-VERIFICATION.md b/.planning/phases/04-deprecated-ccc-api-replacement/04-VERIFICATION.md new file mode 100644 index 0000000..47f2061 --- /dev/null +++ b/.planning/phases/04-deprecated-ccc-api-replacement/04-VERIFICATION.md @@ -0,0 +1,120 @@ +--- +phase: 04-deprecated-ccc-api-replacement +verified: 2026-02-26T12:00:00Z +status: passed +score: 5/5 must-haves verified +re_verification: false +--- + +# Phase 4: Deprecated CCC API Replacement — Verification Report + +**Phase Goal:** UdtHandler dependency in @ickb/order is replaced with plain ccc.Script (udtScript); UDT cellDeps management removed from OrderManager (caller/CCC Udt handles externally during balance completion); @ickb/dao verified clean (no UdtHandler, no deprecated APIs); Phase 3 decision doc corrected to match actual codebase state +**Verified:** 2026-02-26T12:00:00Z +**Status:** passed +**Re-verification:** No — initial verification + +--- + +## Goal Achievement + +### Observable Truths + +| # | Truth | Status | Evidence | +|---|-------|--------|----------| +| 1 | OrderManager constructor accepts `udtScript: ccc.Script` as its third parameter | VERIFIED | `packages/order/src/order.ts` line 26: `public readonly udtScript: ccc.Script,` | +| 2 | OrderManager methods do not add UDT cellDeps to transactions | VERIFIED | No `tx.addCellDeps(this.udtHandler.cellDeps)` in order.ts; grep returns exit 1 (no matches) | +| 3 | @ickb/order does not import UdtHandler from @ickb/utils | VERIFIED | Import block at top of order.ts has no `UdtHandler`; `grep -r "UdtHandler" packages/order/src/` returns exit 1 | +| 4 | SDK constructs OrderManager with ickbUdt.script (a ccc.Script), not full IckbUdtManager instance | VERIFIED | `packages/sdk/src/constants.ts` line 78: `new OrderManager(d.order.script, d.order.cellDeps, ickbUdt.script)` | +| 5 | pnpm check:full passes with zero errors | VERIFIED | `pnpm check` exit code 0; lint, build, test all pass; only warnings are chunk size and missing codec exports (pre-existing, unrelated to Phase 4) | + +**Score:** 5/5 truths verified + +--- + +### Required Artifacts + +| Artifact | Expected | Status | Details | +|----------|----------|--------|---------| +| `packages/order/src/order.ts` | OrderManager with udtScript: ccc.Script parameter, no UDT cellDeps | VERIFIED | Contains `public readonly udtScript: ccc.Script` at line 26; no `udtHandler` references; @remarks JSDoc on mint (line 162), addMatch (line 218), melt (line 499) | +| `packages/sdk/src/constants.ts` | SDK caller passing ickbUdt.script to OrderManager | VERIFIED | Line 78: `new OrderManager(d.order.script, d.order.cellDeps, ickbUdt.script)` | + +--- + +### Key Link Verification + +| From | To | Via | Status | Details | +|------|----|-----|--------|---------| +| `packages/sdk/src/constants.ts` | `packages/order/src/order.ts` | OrderManager constructor call | VERIFIED | Line 78 matches pattern `new OrderManager(.*ickbUdt\.script)` | +| `packages/order/src/order.ts` | `@ckb-ccc/core` | ccc.Script type for udtScript | VERIFIED | Line 26: `udtScript: ccc.Script`; imports `ccc` from `@ckb-ccc/core` at line 1 | + +--- + +### Requirements Coverage + +| Requirement | Source Plan | Description | Status | Evidence | +|-------------|-------------|-------------|--------|----------| +| SMTX-05 | 04-01-PLAN.md | UDT handler registration replaced by direct Udt instance usage or standalone utility functions | PARTIAL (Phase 4 contribution verified) | Phase 4 scope: OrderManager.udtHandler replaced with udtScript: ccc.Script, UDT cellDeps removed (caller responsibility). Per REQUIREMENTS.md traceability, SMTX-05 spans Phases 1, 4, and 5. Phase 4's portion is fully delivered. Final completion awaits Phase 5 (UdtHandler/UdtManager deletion from @ickb/utils). | + +**Note on SMTX-05 scope:** The requirement spans three phases. Phase 4 owns the OrderManager portion only. `UdtHandler` interface and `UdtManager` class intentionally remain in `@ickb/utils` and `@ickb/core` — their deletion is Phase 5 work. The REQUIREMENTS.md traceability table and the Phase 3 decision doc both confirm this split. No orphaned requirements found. + +--- + +### Supplementary Verifications + +**@ickb/dao clean check:** +- `grep -r "UdtHandler|udtHandler" packages/dao/src/` — exit 1 (no matches). Clean. +- `grep -r "udtBalanceFrom|getInputsUdtBalance|getOutputsUdtBalance|completeInputsByUdt" packages/dao/src/` — exit 1 (no matches). Clean. + +**Phase 3 decision doc correction:** +- `03-DECISION.md` line 373 contains: `*Updated 2026-02-26 based on Phase 4 discuss-phase. See 04-CONTEXT.md for full decisions.*` +- Line 266 references `udtScript: ccc.Script (Phase 4)` in the change table. +- Line 378 states `udtHandler: UdtHandler replaced with udtScript: ccc.Script`. +- Correction is present and accurate. + +**Commit verification:** +- Commit `9e6e3d8` exists: `feat(04-01): replace UdtHandler with udtScript in OrderManager` +- Modified files: `packages/order/src/order.ts` and `packages/sdk/src/constants.ts` — exactly the planned files. + +--- + +### Anti-Patterns Found + +| File | Line | Pattern | Severity | Impact | +|------|------|---------|----------|--------| +| `packages/order/src/order.ts` | 33 | JSDoc still says "UDT handler's script" in `isOrder()` description | Info | Cosmetic only — code uses `this.udtScript` correctly; JSDoc description is stale prose, not a stub | +| `packages/order/src/order.ts` | 534 | JSDoc says "lock-script cells matching order & UDT handler" in `findOrders()` | Info | Cosmetic only — implementation uses `this.udtScript` correctly at line 637 | + +Neither anti-pattern affects correctness or the phase goal. Both are stale JSDoc prose from before the refactor. + +--- + +### Human Verification Required + +None. All phase goals are verifiable programmatically: +- Parameter type changes are statically verified by TypeScript (pnpm check passes). +- cellDeps calls are verifiable by grep. +- SDK wiring is verifiable by grep. +- Phase 3 decision doc correction is verifiable by grep. + +--- + +### Summary + +Phase 4 goal is fully achieved. Every must-have truth is verified against the actual codebase: + +1. `OrderManager` constructor parameter is `udtScript: ccc.Script` — confirmed in source. +2. All three `tx.addCellDeps(this.udtHandler.cellDeps)` calls are removed from `mint()`, `addMatch()`, `melt()` — grep returns no matches. +3. `UdtHandler` import is absent from `@ickb/order` — grep returns no matches across the entire `packages/order/src/` directory. +4. SDK passes `ickbUdt.script` (not `ickbUdt`) to the OrderManager constructor — confirmed at constants.ts line 78. +5. `pnpm check` passes with exit code 0, confirming TypeScript type safety across all 5 library packages. + +The presence of `UdtHandler` in `@ickb/utils` and `@ickb/core` is expected and intentional — those are deferred to Phase 5 per the roadmap and REQUIREMENTS.md traceability. + +Two stale JSDoc strings (lines 33 and 534 of order.ts) describe the old "UDT handler" concept — these are cosmetic and do not affect goal achievement. + +SMTX-05 is "In Progress" spanning Phases 1, 4, and 5. Phase 4's contribution (OrderManager refactor) is complete. Phase 5 will complete it by deleting `UdtHandler`/`UdtManager` from `@ickb/utils`. + +--- + +_Verified: 2026-02-26T12:00:00Z_ +_Verifier: AI Coworker (gsd-verifier)_ diff --git a/.planning/phases/05-ickb-core-udt-refactor/05-01-PLAN.md b/.planning/phases/05-ickb-core-udt-refactor/05-01-PLAN.md new file mode 100644 index 0000000..6176e68 --- /dev/null +++ b/.planning/phases/05-ickb-core-udt-refactor/05-01-PLAN.md @@ -0,0 +1,497 @@ +--- +phase: 05-ickb-core-udt-refactor +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - pnpm-workspace.yaml + - packages/core/package.json + - packages/core/src/udt.ts + - packages/core/src/logic.ts + - packages/core/src/owned_owner.ts +autonomous: true +requirements: + - SMTX-05 + - SMTX-07 + - SMTX-10 + - UDT-04 + +must_haves: + truths: + - "IckbUdt extends udt.Udt exists in @ickb/core with infoFrom override" + - "infoFrom correctly values xUDT cells (positive), receipt cells (positive, input only), deposit cells (negative, input only)" + - "IckbUdt.addCellDeps adds individual code deps (xUDT + Logic OutPoints), not dep group" + - "IckbUdt.typeScriptFrom static method computes iCKB UDT type script" + - "LogicManager no longer takes udtHandler parameter" + - "OwnedOwnerManager no longer takes udtHandler parameter" + - "No tx.addCellDeps(this.udtHandler.cellDeps) calls remain in LogicManager or OwnedOwnerManager" + artifacts: + - path: "packages/core/src/udt.ts" + provides: "IckbUdt class extending udt.Udt with infoFrom, addCellDeps, typeScriptFrom" + contains: "class IckbUdt extends udt.Udt" + - path: "packages/core/src/logic.ts" + provides: "LogicManager without udtHandler parameter" + - path: "packages/core/src/owned_owner.ts" + provides: "OwnedOwnerManager without udtHandler parameter" + - path: "packages/core/package.json" + provides: "@ckb-ccc/udt dependency" + contains: "@ckb-ccc/udt" + - path: "pnpm-workspace.yaml" + provides: "Catalog entry for @ckb-ccc/udt" + contains: "@ckb-ccc/udt" + key_links: + - from: "packages/core/src/udt.ts" + to: "@ckb-ccc/udt" + via: "import { udt } from '@ckb-ccc/udt'" + pattern: "import.*udt.*from.*@ckb-ccc/udt" + - from: "packages/core/src/udt.ts" + to: "packages/core/src/entities.ts" + via: "ReceiptData.decode for receipt cell valuation" + pattern: "ReceiptData\\.decode" + - from: "packages/core/src/udt.ts" + to: "@ickb/dao" + via: "DaoManager for deposit cell identification" + pattern: "daoManager\\.isDeposit" +--- + + +Implement IckbUdt class extending CCC's udt.Udt in @ickb/core, replacing IckbUdtManager. Remove udtHandler parameter from LogicManager and OwnedOwnerManager. Add @ckb-ccc/udt dependency. + +Purpose: This is the core CCC UDT integration -- IckbUdt.infoFrom provides accurate multi-representation balance (xUDT + receipts + deposits) consumed by CCC's completeInputsByBalance pipeline. Removing udtHandler from managers follows the Phase 4 OrderManager pattern, making UDT cellDeps a caller responsibility. + +Output: Modified core/src/udt.ts (IckbUdt class), modified logic.ts and owned_owner.ts (no udtHandler), updated package.json and catalog. + + + +@/home/node/.claude/get-shit-done/workflows/execute-plan.md +@/home/node/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/05-ickb-core-udt-refactor/05-CONTEXT.md +@.planning/phases/05-ickb-core-udt-refactor/05-RESEARCH.md + + + + +From @ckb-ccc/udt (CCC Udt class -- the base class to extend): +```typescript +import { udt } from "@ckb-ccc/udt"; + +// Constructor: (code: OutPointLike, script: ScriptLike, config?: UdtConfigLike | null) +// - code: xUDT code cell OutPoint (passed to ssri.Trait) +// - script: iCKB UDT type script (token identity) +// No executor needed (legacy xUDT, not SSRI) + +class Udt extends ssri.Trait { + public readonly script: ccc.Script; + public readonly filter: ccc.ClientIndexerSearchKeyFilter; + + constructor(code: ccc.OutPointLike, script: ccc.ScriptLike, config?: UdtConfigLike | null); + + // Override this for multi-representation balance + async infoFrom( + _client: ccc.Client, + cells: ccc.CellAnyLike | ccc.CellAnyLike[], + acc?: UdtInfoLike, + ): Promise; + + // Override this for custom cellDeps + addCellDeps(txLike: ccc.TransactionLike): ccc.Transaction; + + // Checks cell.type matches this.script AND outputData >= 16 bytes + isUdt(cell: ccc.CellAnyLike): boolean; + + // Extracts balance from outputData (LE uint128) + static balanceFromUnsafe(outputData: ccc.HexLike): ccc.Num; +} + +class UdtInfo { + balance: ccc.Num; + capacity: ccc.Num; + count: number; + static from(infoLike?: UdtInfoLike): UdtInfo; + clone(): UdtInfo; + addAssign(infoLike: UdtInfoLike): this; +} +``` + +From packages/core/src/udt.ts (BEFORE -- current IckbUdtManager to replace): +```typescript +export class IckbUdtManager extends UdtManager implements UdtHandler { + constructor( + script: ccc.Script, + cellDeps: ccc.CellDep[], + public readonly logicScript: ccc.Script, + public readonly daoManager: DaoManager, + ) { super(script, cellDeps, "iCKB", "iCKB", 8); } + + static calculateScript(udt: ccc.Script, ickbLogic: ccc.Script): ccc.Script; + override async getInputsUdtBalance(client, txLike): Promise<[FixedPoint, FixedPoint]>; +} + +// These functions/constants MUST be preserved unchanged: +export function ickbValue(ckbUnoccupiedCapacity, header): FixedPoint; +export function convert(isCkb2Udt, amount, rate, accountDepositCapacity?): FixedPoint; +export function ickbExchangeRatio(header, accountDepositCapacity?): ExchangeRatio; +export const ICKB_DEPOSIT_CAP; +// Internal constants (AR_0, depositUsedCapacity, depositCapacityDelta) also preserved +``` + +From packages/core/src/entities.ts: +```typescript +export const ReceiptData = { + decode(data: ccc.HexLike): { depositQuantity: bigint; depositAmount: bigint }; + encode(data: { depositQuantity: number; depositAmount: bigint }): ccc.Hex; +}; +``` + +From @ickb/dao: +```typescript +export class DaoManager { + isDeposit(cell: ccc.Cell): boolean; // Checks outputData for 8 zero bytes +} +``` + +From packages/core/src/logic.ts (BEFORE -- udtHandler to remove): +```typescript +export class LogicManager implements ScriptDeps { + constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly daoManager: DaoManager, + public readonly udtHandler: UdtHandler, // REMOVE + ) {} + // Line 88: tx.addCellDeps(this.udtHandler.cellDeps); // REMOVE (in deposit) + // Line 125: tx.addCellDeps(this.udtHandler.cellDeps); // REMOVE (in completeDeposit) +} +``` + +From packages/core/src/owned_owner.ts (BEFORE -- udtHandler to remove): +```typescript +export class OwnedOwnerManager implements ScriptDeps { + constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly daoManager: DaoManager, + public readonly udtHandler: UdtHandler, // REMOVE + ) {} + // Line 95: tx.addCellDeps(this.udtHandler.cellDeps); // REMOVE (in requestWithdrawal) + // Line 140: tx.addCellDeps(this.udtHandler.cellDeps); // REMOVE (in withdraw) +} +``` + + + + + + + Task 1: Add @ckb-ccc/udt dependency and implement IckbUdt class + pnpm-workspace.yaml, packages/core/package.json, packages/core/src/udt.ts + +**Step 1: Add catalog entry in `pnpm-workspace.yaml`** + +Add `"@ckb-ccc/udt": ^1.12.2` to the catalog section, after the existing `"@ckb-ccc/core": ^1.12.2` entry. This ensures consistency with how `@ckb-ccc/core` is referenced. The `.pnpmfile.cjs` hook will rewrite to `workspace:*` when the local CCC fork is present. + +**Step 2: Add dependency in `packages/core/package.json`** + +Add `"@ckb-ccc/udt": "catalog:"` to the dependencies object, after `"@ckb-ccc/core": "catalog:"`. The dependencies section should become: +```json +"dependencies": { + "@ckb-ccc/core": "catalog:", + "@ckb-ccc/udt": "catalog:", + "@ickb/dao": "workspace:*", + "@ickb/utils": "workspace:*" +} +``` + +**Step 3: Run `pnpm install` to update lockfile** + +**Step 4: Rewrite `packages/core/src/udt.ts`** + +Replace the entire `IckbUdtManager` class with `IckbUdt extends udt.Udt`. Keep all standalone functions (`ickbValue`, `convert`, `ickbExchangeRatio`) and constants (`ICKB_DEPOSIT_CAP`, `AR_0`, `depositUsedCapacity`, `depositCapacityDelta`) UNCHANGED. + +New imports (replacing current imports): +```typescript +import { ccc } from "@ckb-ccc/core"; +import { udt } from "@ckb-ccc/udt"; +import { ReceiptData } from "./entities.js"; +import type { DaoManager } from "@ickb/dao"; +import type { ExchangeRatio } from "@ickb/utils"; +``` + +Note: `UdtManager` and `UdtHandler` imports are removed. `ExchangeRatio` is kept (used by `convert` function). `udt` namespace import is added from `@ckb-ccc/udt`. + +New class definition: +```typescript +/** + * IckbUdt extends CCC's Udt class to provide accurate multi-representation + * balance for iCKB tokens. The iCKB conservation law is: + * Input UDT + Input Receipts = Output UDT + Input Deposits + * + * `infoFrom` values three cell types: + * - xUDT cells: positive balance (standard UDT) + * - Receipt cells: positive balance (input only, valued via ickbValue) + * - Deposit cells: negative balance (input only, withdrawal reduces UDT supply) + * + * Output cells without outPoint are naturally excluded from receipt/deposit + * processing, since only input cells (resolved by CellInput.getCell()) have outPoint. + */ +export class IckbUdt extends udt.Udt { + public readonly logicCode: ccc.OutPoint; + public readonly logicScript: ccc.Script; + public readonly daoManager: DaoManager; + + /** + * Creates an instance of IckbUdt. + * + * @param code - The xUDT code cell OutPoint (passed to base Udt/Trait). + * @param script - The iCKB UDT type script (token identity via args). + * @param logicCode - The iCKB Logic code cell OutPoint. + * @param logicScript - The iCKB Logic script. + * @param daoManager - The DAO manager instance for deposit cell identification. + */ + constructor( + code: ccc.OutPointLike, + script: ccc.ScriptLike, + logicCode: ccc.OutPointLike, + logicScript: ccc.ScriptLike, + daoManager: DaoManager, + ) { + super(code, script); + this.logicCode = ccc.OutPoint.from(logicCode); + this.logicScript = ccc.Script.from(logicScript); + this.daoManager = daoManager; + } + + /** + * Computes the iCKB UDT type script from raw UDT and Logic scripts. + * + * Concatenates the iCKB logic script hash with a fixed 4-byte LE length + * postfix ("00000080") to form the UDT type script args. + * + * @param udt - The raw xUDT script (codeHash and hashType reused). + * @param ickbLogic - The iCKB logic script (hash used for args). + * @returns A new Script with the computed args. + */ + static typeScriptFrom(udt: ccc.Script, ickbLogic: ccc.Script): ccc.Script { + const { codeHash, hashType } = udt; + return new ccc.Script( + codeHash, + hashType, + [ickbLogic.hash(), "00000080"].join("") as ccc.Hex, + ); + } + + /** + * Computes UDT balance info for iCKB's three cell representations. + * + * For each cell: + * - xUDT cell (type === this.script, data >= 16 bytes): adds positive balance + * - Receipt cell (type === logicScript, has outPoint): adds positive balance + * via ickbValue of deposit amount * quantity + * - Deposit cell (lock === logicScript, isDeposit, has outPoint): adds negative + * balance via ickbValue of free capacity (withdrawal reduces UDT supply) + * + * Cells without outPoint (output cells from getOutputsInfo) skip receipt/deposit + * processing -- correct by design since these only appear as inputs. + * + * @param client - CKB client for header fetches (receipt/deposit valuation). + * @param cells - Cell or array of cells to evaluate. + * @param acc - Optional accumulator for running totals. + * @returns UdtInfo with balance, capacity, and count. + */ + override async infoFrom( + client: ccc.Client, + cells: ccc.CellAnyLike | ccc.CellAnyLike[], + acc?: udt.UdtInfoLike, + ): Promise { + const info = udt.UdtInfo.from(acc).clone(); + + for (const cellLike of [cells].flat()) { + const cell = ccc.CellAny.from(cellLike); + + // Standard xUDT cell -- delegate to base class pattern + if (this.isUdt(cell)) { + info.addAssign({ + balance: udt.Udt.balanceFromUnsafe(cell.outputData), + capacity: cell.cellOutput.capacity, + count: 1, + }); + continue; + } + + // Receipt and deposit cells need outPoint for header fetch. + // Output cells (no outPoint) are skipped -- correct by design. + if (!cell.outPoint) { + continue; + } + + const { type, lock } = cell.cellOutput; + + // Receipt cell: type === logicScript + if (type && this.logicScript.eq(type)) { + const txWithHeader = await client.getTransactionWithHeader( + cell.outPoint.txHash, + ); + if (!txWithHeader?.header) { + throw new Error("Header not found for txHash"); + } + + const { depositQuantity, depositAmount } = + ReceiptData.decode(cell.outputData); + info.addAssign({ + balance: ickbValue(depositAmount, txWithHeader.header) * + depositQuantity, + capacity: cell.cellOutput.capacity, + count: 1, + }); + continue; + } + + // Deposit cell: lock === logicScript AND isDeposit + if (this.logicScript.eq(lock) && this.daoManager.isDeposit(cell)) { + const txWithHeader = await client.getTransactionWithHeader( + cell.outPoint.txHash, + ); + if (!txWithHeader?.header) { + throw new Error("Header not found for txHash"); + } + + info.addAssign({ + balance: -ickbValue(cell.capacityFree, txWithHeader.header), + capacity: cell.cellOutput.capacity, + count: 1, + }); + continue; + } + } + + return info; + } + + /** + * Adds iCKB-specific cell dependencies to a transaction. + * + * Adds individual code deps (not dep group) for: + * - xUDT code cell (this.code from ssri.Trait) + * - iCKB Logic code cell (this.logicCode) + * + * @param txLike - The transaction to add cell deps to. + * @returns The transaction with cell deps added. + */ + override addCellDeps(txLike: ccc.TransactionLike): ccc.Transaction { + const tx = ccc.Transaction.from(txLike); + // xUDT code dep + tx.addCellDeps({ outPoint: this.code, depType: "code" }); + // iCKB Logic code dep + tx.addCellDeps({ outPoint: this.logicCode, depType: "code" }); + return tx; + } +} +``` + +**Key implementation notes:** +- `daoManager.isDeposit(cell)` receives a `CellAny` from `CellAny.from(cellLike)`. In `infoFrom` called from `getInputsInfo`, input cells are resolved via `CellInput.getCell()` which returns `Cell` (extends `CellAny`). `DaoManager.isDeposit` takes `ccc.Cell`, and `Cell extends CellAny`, so this works for input cells. For output cells, the `!cell.outPoint` check gates them before `isDeposit` is reached. +- `balance` in `UdtInfo` uses `ccc.Num` (bigint), which can go negative for deposit cells. This is intentional -- CCC's `completeInputsByBalance` checks `info.balance >= 0` to stop. +- The `ickbValue` call for receipt cells multiplies by `depositQuantity` (a receipt can represent multiple deposits). +- The `ickbValue` call for deposit cells uses `cell.capacityFree` (available via `CellAny.capacityFree` getter). + +**PRESERVE all code below the class definition unchanged:** +- `ickbValue` function +- `ICKB_DEPOSIT_CAP` constant +- `convert` function +- `ickbExchangeRatio` function +- `AR_0`, `depositUsedCapacity`, `depositCapacityDelta` constants + + + cd /workspaces/stack && pnpm install && grep -q "@ckb-ccc/udt" pnpm-workspace.yaml && grep -q "@ckb-ccc/udt" packages/core/package.json && grep -q "class IckbUdt extends udt.Udt" packages/core/src/udt.ts && grep -q "typeScriptFrom" packages/core/src/udt.ts && echo "PASS" + + + - @ckb-ccc/udt added to pnpm-workspace.yaml catalog + - @ckb-ccc/udt added to @ickb/core package.json dependencies + - IckbUdt extends udt.Udt class exists with infoFrom, addCellDeps, typeScriptFrom + - IckbUdtManager class no longer exists + - calculateScript renamed to typeScriptFrom (static) + - No imports of UdtManager or UdtHandler from @ickb/utils in core/src/udt.ts + - All standalone functions and constants preserved unchanged + + + + + Task 2: Remove udtHandler from LogicManager and OwnedOwnerManager + packages/core/src/logic.ts, packages/core/src/owned_owner.ts + +**In `packages/core/src/logic.ts`:** + +1. Remove `type UdtHandler,` from the `@ickb/utils` import on line 6. Keep `defaultFindCellsLimit`, `type ScriptDeps`, `unique`. + +2. Update JSDoc on line 29: remove `@param udtHandler - The handler for User Defined Tokens (UDTs).` + +3. Remove constructor parameter on line 34: delete `public readonly udtHandler: UdtHandler,` + +4. In `deposit()` method (line 88): delete `tx.addCellDeps(this.udtHandler.cellDeps);` + Add JSDoc `@remarks` to deposit()'s existing JSDoc block: `@remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via ickbUdt.addCellDeps(tx)).` + +5. In `completeDeposit()` method (line 125): delete `tx.addCellDeps(this.udtHandler.cellDeps);` + Add JSDoc `@remarks` to completeDeposit()'s existing JSDoc block: `@remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via ickbUdt.addCellDeps(tx)).` + +**In `packages/core/src/owned_owner.ts`:** + +1. Remove `type UdtHandler,` from the `@ickb/utils` import on line 6. Keep `defaultFindCellsLimit`, `unique`, `type ScriptDeps`. + +2. Update JSDoc on line 24: remove `@param udtHandler - The handler for User Defined Tokens (UDTs).` + +3. Remove constructor parameter on line 29: delete `public readonly udtHandler: UdtHandler,` + +4. In `requestWithdrawal()` method (line 95): delete `tx.addCellDeps(this.udtHandler.cellDeps);` + Add JSDoc `@remarks` to requestWithdrawal()'s existing JSDoc block: `@remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via ickbUdt.addCellDeps(tx)).` + +5. In `withdraw()` method (line 140): delete `tx.addCellDeps(this.udtHandler.cellDeps);` + Add JSDoc `@remarks` to withdraw()'s existing JSDoc block: `@remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via ickbUdt.addCellDeps(tx)).` + +This matches Phase 4's OrderManager pattern exactly. UDT cellDeps are now caller responsibility -- documented via JSDoc @remarks. + + + cd /workspaces/stack && ! grep -rq "UdtHandler\|udtHandler" packages/core/src/logic.ts packages/core/src/owned_owner.ts && echo "PASS" + + + - LogicManager constructor has 3 parameters (script, cellDeps, daoManager) -- no udtHandler + - OwnedOwnerManager constructor has 3 parameters (script, cellDeps, daoManager) -- no udtHandler + - All 4 tx.addCellDeps(this.udtHandler.cellDeps) calls deleted (2 in logic.ts, 2 in owned_owner.ts) + - UdtHandler no longer imported in either file + - JSDoc @remarks added to deposit(), completeDeposit(), requestWithdrawal(), withdraw() about caller cellDeps responsibility + + + + + + +1. `grep -q "class IckbUdt extends udt.Udt" packages/core/src/udt.ts` confirms IckbUdt class exists +2. `grep -q "typeScriptFrom" packages/core/src/udt.ts` confirms renamed static method +3. `grep -q "override async infoFrom" packages/core/src/udt.ts` confirms infoFrom override +4. `grep -q "override addCellDeps" packages/core/src/udt.ts` confirms addCellDeps override +5. `grep -r "UdtHandler\|udtHandler" packages/core/src/logic.ts packages/core/src/owned_owner.ts` returns no matches +6. `grep -r "UdtManager\|IckbUdtManager" packages/core/src/udt.ts` returns no matches (old class removed) +7. `grep -q "@ckb-ccc/udt" packages/core/package.json` confirms dependency added +8. `grep -q "@ckb-ccc/udt" pnpm-workspace.yaml` confirms catalog entry added + + + +- IckbUdt extends udt.Udt with infoFrom override for multi-representation balance +- addCellDeps override adds individual code deps (xUDT + Logic) +- typeScriptFrom static method replaces calculateScript +- LogicManager and OwnedOwnerManager no longer take udtHandler parameter +- All 4 udtHandler.cellDeps calls removed from managers +- @ckb-ccc/udt dependency added to core package.json and workspace catalog +- No imports of UdtManager, UdtHandler, or IckbUdtManager remain in @ickb/core +- All standalone functions (ickbValue, convert, ickbExchangeRatio) and constants preserved unchanged + + + +After completion, create `.planning/phases/05-ickb-core-udt-refactor/05-01-SUMMARY.md` + diff --git a/.planning/phases/05-ickb-core-udt-refactor/05-01-SUMMARY.md b/.planning/phases/05-ickb-core-udt-refactor/05-01-SUMMARY.md new file mode 100644 index 0000000..a2793ed --- /dev/null +++ b/.planning/phases/05-ickb-core-udt-refactor/05-01-SUMMARY.md @@ -0,0 +1,120 @@ +--- +phase: 05-ickb-core-udt-refactor +plan: 01 +subsystem: core +tags: [udt, ccc, xudt, ickb, balance, cellDeps] + +# Dependency graph +requires: + - phase: 04-deprecated-api-udt-pattern + provides: UDT pattern (managers receive ccc.Script, cellDeps are caller responsibility) + - phase: 03-udt-investigation + provides: IckbUdt subclass design (infoFrom override for multi-representation balance) +provides: + - IckbUdt class extending udt.Udt with infoFrom, addCellDeps, typeScriptFrom + - LogicManager without udtHandler parameter + - OwnedOwnerManager without udtHandler parameter + - "@ckb-ccc/udt" dependency in @ickb/core +affects: [05-02, sdk, bot] + +# Tech tracking +tech-stack: + added: ["@ckb-ccc/udt"] + patterns: [IckbUdt subclass with multi-representation infoFrom, individual code deps] + +key-files: + created: [] + modified: + - packages/core/src/udt.ts + - packages/core/src/logic.ts + - packages/core/src/owned_owner.ts + - packages/core/package.json + - pnpm-workspace.yaml + +key-decisions: + - "IckbUdt.infoFrom handles three cell types: xUDT (positive), receipt (positive via ickbValue), deposit (negative via ickbValue)" + - "addCellDeps adds individual code deps (xUDT + Logic OutPoints), not dep group" + - "Widened DaoManager.isDeposit to accept CellAny — cleaner than type assertion, only inspects fields CellAny provides" + +patterns-established: + - "IckbUdt subclass pattern: extend udt.Udt, override infoFrom for custom balance, override addCellDeps for custom deps" + - "Manager cellDeps responsibility: managers document caller responsibility via JSDoc @remarks" + +requirements-completed: [SMTX-05, SMTX-07, SMTX-10, UDT-04] + +# Metrics +duration: 7min +completed: 2026-02-26 +--- + +# Phase 5 Plan 1: IckbUdt Implementation Summary + +**IckbUdt extends CCC udt.Udt with multi-representation balance (xUDT + receipts + deposits) and individual code deps; udtHandler removed from LogicManager and OwnedOwnerManager** + +## Performance + +- **Duration:** 7 min +- **Started:** 2026-02-26T16:05:59Z +- **Completed:** 2026-02-26T16:12:50Z +- **Tasks:** 2 +- **Files modified:** 5 + +## Accomplishments +- Replaced IckbUdtManager with IckbUdt extending CCC's udt.Udt class, providing accurate multi-representation balance via infoFrom override +- Added addCellDeps override with individual code deps (xUDT + Logic OutPoints) instead of dep group +- Renamed calculateScript to typeScriptFrom static method +- Removed udtHandler parameter and cellDeps calls from LogicManager (deposit, completeDeposit) and OwnedOwnerManager (requestWithdrawal, withdraw) +- Added @ckb-ccc/udt dependency to workspace catalog and @ickb/core + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Add @ckb-ccc/udt dependency and implement IckbUdt class** - `4cd87ea` (feat) +2. **Task 2: Remove udtHandler from LogicManager and OwnedOwnerManager** - `e5dd4c3` (refactor) + +## Files Created/Modified +- `packages/core/src/udt.ts` - IckbUdt class extending udt.Udt with infoFrom, addCellDeps, typeScriptFrom; standalone functions preserved +- `packages/core/src/logic.ts` - LogicManager without udtHandler; JSDoc @remarks on deposit/completeDeposit +- `packages/core/src/owned_owner.ts` - OwnedOwnerManager without udtHandler; JSDoc @remarks on requestWithdrawal/withdraw +- `packages/core/package.json` - Added @ckb-ccc/udt dependency +- `pnpm-workspace.yaml` - Added @ckb-ccc/udt catalog entry + +## Decisions Made +- Widened DaoManager.isDeposit() to accept CellAny instead of adding a type assertion — cleaner than casting, and isDeposit only inspects cellOutput.type and outputData which CellAny provides +- addCellDeps adds individual code deps (not dep group) per CCC author preference, matching 05-CONTEXT decision + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Widened DaoManager.isDeposit to accept CellAny** +- **Found during:** Task 1 (IckbUdt class implementation) +- **Issue:** daoManager.isDeposit() takes ccc.Cell but infoFrom processes CellAny; TypeScript type mismatch +- **Fix:** Widened DaoManager.isDeposit() parameter from ccc.Cell to ccc.CellAny in packages/dao/src/dao.ts (cleaner than a type assertion) +- **Files modified:** packages/dao/src/dao.ts +- **Verification:** TypeScript compilation passes with no errors across all packages + +--- + +**Total deviations:** 1 auto-fixed (1 bug fix) +**Impact on plan:** Upstream signature widened for type correctness. Runtime behavior unchanged. No scope creep. + +## Issues Encountered +None + +## User Setup Required +None - no external service configuration required. + +## Next Phase Readiness +- IckbUdt class ready for SDK integration (05-02) +- UdtHandler interface has zero consumers in core after this plan -- ready for deletion in 05-02 +- All standalone functions (ickbValue, convert, ickbExchangeRatio) preserved unchanged for downstream consumers + +## Self-Check: PASSED + +All files exist, all commits verified. + +--- +*Phase: 05-ickb-core-udt-refactor* +*Completed: 2026-02-26* diff --git a/.planning/phases/05-ickb-core-udt-refactor/05-02-PLAN.md b/.planning/phases/05-ickb-core-udt-refactor/05-02-PLAN.md new file mode 100644 index 0000000..f844bb1 --- /dev/null +++ b/.planning/phases/05-ickb-core-udt-refactor/05-02-PLAN.md @@ -0,0 +1,418 @@ +--- +phase: 05-ickb-core-udt-refactor +plan: 02 +type: execute +wave: 2 +depends_on: ["05-01"] +files_modified: + - packages/utils/src/udt.ts + - packages/utils/src/index.ts + - packages/sdk/src/constants.ts +autonomous: true +requirements: + - SMTX-05 + - SMTX-07 + - SMTX-10 + - UDT-04 + +must_haves: + truths: + - "UdtHandler interface, UdtManager class, ErrorTransactionInsufficientCoin, UdtCell, findUdts, addUdts, isUdtSymbol are deleted from @ickb/utils" + - "packages/utils/src/udt.ts file no longer exists" + - "@ickb/utils no longer exports any UDT-related types" + - "SDK constructs IckbUdt instead of IckbUdtManager" + - "SDK passes ickbUdt.script to OrderManager" + - "LogicManager and OwnedOwnerManager constructed without ickbUdt argument" + - "pnpm check:full passes with zero errors" + - "SDK audited for ErrorTransactionInsufficientCoin catch blocks -- zero found, no migration required; ErrorUdtInsufficientCoin is CCC's responsibility and surfaces to callers unchanged" + artifacts: + - path: "packages/utils/src/index.ts" + provides: "Barrel export without udt.ts" + - path: "packages/sdk/src/constants.ts" + provides: "getConfig() constructing IckbUdt with code OutPoints" + contains: "new IckbUdt" + key_links: + - from: "packages/sdk/src/constants.ts" + to: "packages/core/src/udt.ts" + via: "import { IckbUdt } and constructor call" + pattern: "new IckbUdt\\(" + - from: "packages/sdk/src/constants.ts" + to: "packages/core/src/udt.ts" + via: "IckbUdt.typeScriptFrom for script computation" + pattern: "IckbUdt\\.typeScriptFrom" +--- + + +Delete UDT infrastructure from @ickb/utils (UdtHandler, UdtManager, ErrorTransactionInsufficientCoin, UdtCell, findUdts, addUdts, isUdtSymbol). Update SDK to construct IckbUdt with individual code OutPoints instead of IckbUdtManager with dep group. Verify the full stack compiles. + +Purpose: With IckbUdt replacing IckbUdtManager (Plan 05-01) and all managers cleaned of udtHandler references, the entire UDT infrastructure in @ickb/utils has zero consumers. Deleting it eliminates ~400 lines of duplicated code. The SDK wiring completes the refactor by constructing IckbUdt with the CCC-aligned code OutPoint pattern. + +Output: Deleted utils/src/udt.ts, updated utils/src/index.ts, rewritten SDK constants.ts. Full stack compiles via pnpm check:full. + + + +@/home/node/.claude/get-shit-done/workflows/execute-plan.md +@/home/node/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/05-ickb-core-udt-refactor/05-CONTEXT.md +@.planning/phases/05-ickb-core-udt-refactor/05-RESEARCH.md +@.planning/phases/05-ickb-core-udt-refactor/05-01-SUMMARY.md + + + + +From packages/core/src/udt.ts (AFTER Plan 05-01 -- new IckbUdt class): +```typescript +import { udt } from "@ckb-ccc/udt"; + +export class IckbUdt extends udt.Udt { + constructor( + code: ccc.OutPointLike, // xUDT code cell OutPoint + script: ccc.ScriptLike, // iCKB UDT type script + logicCode: ccc.OutPointLike, // iCKB Logic code cell OutPoint + logicScript: ccc.ScriptLike, // iCKB Logic script + daoManager: DaoManager, // DAO manager for deposit detection + ); + static typeScriptFrom(udt: ccc.Script, ickbLogic: ccc.Script): ccc.Script; + override async infoFrom(client, cells, acc?): Promise; + override addCellDeps(txLike): ccc.Transaction; +} +``` + +From packages/core/src/logic.ts (AFTER Plan 05-01 -- no udtHandler): +```typescript +export class LogicManager implements ScriptDeps { + constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly daoManager: DaoManager, + // udtHandler REMOVED + ) {} +} +``` + +From packages/core/src/owned_owner.ts (AFTER Plan 05-01 -- no udtHandler): +```typescript +export class OwnedOwnerManager implements ScriptDeps { + constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly daoManager: DaoManager, + // udtHandler REMOVED + ) {} +} +``` + +From packages/sdk/src/constants.ts (BEFORE -- current state to modify): +```typescript +import { IckbUdtManager, LogicManager, OwnedOwnerManager } from "@ickb/core"; + +export function getConfig(d, bots): { + managers: { + dao: DaoManager; + ickbUdt: IckbUdtManager; // CHANGE to IckbUdt + logic: LogicManager; + ownedOwner: OwnedOwnerManager; + order: OrderManager; + }; + bots: ccc.Script[]; +} + +// Current construction: +const ickbUdt = new IckbUdtManager( + d.udt.script, // script + d.udt.cellDeps, // cellDeps (dep group) + d.logic.script, // logicScript + dao, // daoManager +); +const logic = new LogicManager(d.logic.script, d.logic.cellDeps, dao, ickbUdt); +const ownedOwner = new OwnedOwnerManager(d.ownedOwner.script, d.ownedOwner.cellDeps, dao, ickbUdt); +``` + +Code cell OutPoints for IckbUdt construction (from forks/contracts/scripts/deployment/): +``` +Mainnet xUDT code: txHash 0xc07844ce21b38e4b071dd0e1ee3b0e27afd8d7532491327f39b786343f558ab7, index 0x0 +Mainnet Logic code: txHash 0xd7309191381f5a8a2904b8a79958a9be2752dbba6871fa193dab6aeb29dc8f44, index 0x0 +Testnet xUDT code: txHash 0xbf6fb538763efec2a70a6a3dcb7242787087e1030c4e7d86585bc63a9d337f5f, index 0x0 +Testnet Logic code: txHash 0x9ac989b3355764f76cdce02c69dedb819fdfbcbda49a7db1a2c9facdfdb9a7fe, index 0x0 +``` + +ScriptDeps interface (PRESERVED in @ickb/utils -- NOT deleted): +```typescript +// From packages/utils/src/utils.ts +export interface ScriptDeps { + script: ccc.Script; + cellDeps: ccc.CellDep[]; +} +``` + + + + + + + Task 1: Delete utils/src/udt.ts and update barrel export + packages/utils/src/udt.ts, packages/utils/src/index.ts + +**Step 1: Delete `packages/utils/src/udt.ts`** + +Delete the entire file. This removes: +- `UdtHandler` interface (zero consumers after Plan 05-01) +- `ErrorTransactionInsufficientCoin` class (zero catch blocks across entire codebase) +- `UdtManager` class (replaced by `IckbUdt extends udt.Udt`) +- `UdtCell` interface (internal to `UdtManager`) +- `findUdts` method (internal to `UdtManager`) +- `addUdts` method (internal to `UdtManager`) +- `isUdtSymbol` constant (internal to `UdtManager`) + +~406 lines deleted. + +**Step 2: Update `packages/utils/src/index.ts`** + +Remove the line `export * from "./udt.js";`. The file should become: +```typescript +export * from "./codec.js"; +export * from "./heap.js"; +export * from "./utils.js"; +``` + +**IMPORTANT: `ScriptDeps` interface is NOT in udt.ts.** It lives in `packages/utils/src/utils.ts` and has 6+ consumers remaining (DaoManager, LogicManager, OwnedOwnerManager, OrderManager, getConfig). It is preserved by this change. `ExchangeRatio` is also in `utils.ts`, not `udt.ts`. `ValueComponents` is also in `utils.ts`. All of these survive. + + + cd /workspaces/stack && ! test -f packages/utils/src/udt.ts && ! grep -q "udt.js" packages/utils/src/index.ts && grep -q "ScriptDeps" packages/utils/src/utils.ts && echo "PASS" + + + - packages/utils/src/udt.ts deleted (~406 lines removed) + - export * from "./udt.js" removed from index.ts + - UdtHandler, UdtManager, ErrorTransactionInsufficientCoin, UdtCell, findUdts, addUdts, isUdtSymbol all gone + - ScriptDeps, ExchangeRatio, ValueComponents preserved in utils.ts (unaffected) + + + + + Task 2: Update SDK getConfig to construct IckbUdt with code OutPoints and verify full stack + packages/sdk/src/constants.ts + +**Step 1: Update imports in `packages/sdk/src/constants.ts`** + +Change the `@ickb/core` import from: +```typescript +import { IckbUdtManager, LogicManager, OwnedOwnerManager } from "@ickb/core"; +``` +to: +```typescript +import { IckbUdt, LogicManager, OwnedOwnerManager } from "@ickb/core"; +``` + +**Step 2: Add code cell OutPoint constants** + +Add these constants AFTER the existing script constants (after `ORDER` on line 154) and BEFORE the dep group constants (`MAINNET_DEP_GROUP` on line 159): + +```typescript +/** + * Mainnet xUDT code cell OutPoint. + * Source: forks/contracts/scripts/deployment/mainnet/deployment.toml + */ +const MAINNET_XUDT_CODE = { + txHash: + "0xc07844ce21b38e4b071dd0e1ee3b0e27afd8d7532491327f39b786343f558ab7", + index: "0x0", +}; + +/** + * Mainnet iCKB Logic code cell OutPoint. + * Source: forks/contracts/scripts/deployment/mainnet/deployment.toml + */ +const MAINNET_LOGIC_CODE = { + txHash: + "0xd7309191381f5a8a2904b8a79958a9be2752dbba6871fa193dab6aeb29dc8f44", + index: "0x0", +}; + +/** + * Testnet xUDT code cell OutPoint. + * Source: forks/contracts/scripts/deployment/testnet/deployment.toml + */ +const TESTNET_XUDT_CODE = { + txHash: + "0xbf6fb538763efec2a70a6a3dcb7242787087e1030c4e7d86585bc63a9d337f5f", + index: "0x0", +}; + +/** + * Testnet iCKB Logic code cell OutPoint. + * Source: forks/contracts/scripts/deployment/testnet/deployment.toml + */ +const TESTNET_LOGIC_CODE = { + txHash: + "0x9ac989b3355764f76cdce02c69dedb819fdfbcbda49a7db1a2c9facdfdb9a7fe", + index: "0x0", +}; +``` + +**Step 3: Update return type annotation** + +Change the return type from `ickbUdt: IckbUdtManager` to `ickbUdt: IckbUdt` (line 37). + +**Step 4: Rewrite IckbUdt construction in getConfig()** + +Replace the existing IckbUdtManager construction (lines 60-65): +```typescript +const ickbUdt = new IckbUdtManager( + d.udt.script, + d.udt.cellDeps, + d.logic.script, + dao, +); +``` + +For mainnet/testnet paths, the construction needs code OutPoints. The current flow creates `d` from `from(UDT, depGroup)` etc. which gives `ScriptDeps` with `script` and `cellDeps`. The `IckbUdt` constructor needs `code: OutPointLike` (xUDT code OutPoint) and `logicCode: OutPointLike` (Logic code OutPoint) which are NOT in `ScriptDeps`. + +For the mainnet/testnet case: extract code OutPoints based on the network string BEFORE it gets reassigned. The cleanest approach: + +Replace the entire IckbUdt construction block with: +```typescript +const isMainnet = typeof d !== "string" ? undefined : d === "mainnet"; +// ... after d is reassigned to ScriptDeps ... +const ickbUdt = new IckbUdt( + isMainnet !== undefined + ? (isMainnet ? MAINNET_XUDT_CODE : TESTNET_XUDT_CODE) + : d.udt.cellDeps[0]?.outPoint ?? { txHash: "0x", index: "0x0" }, + IckbUdt.typeScriptFrom( + ccc.Script.from(d.udt.script), + ccc.Script.from(d.logic.script), + ), + isMainnet !== undefined + ? (isMainnet ? MAINNET_LOGIC_CODE : TESTNET_LOGIC_CODE) + : d.logic.cellDeps[0]?.outPoint ?? { txHash: "0x", index: "0x0" }, + d.logic.script, + dao, +); +``` + +Wait -- the existing code reassigns `d` inside the `if` block. For devnet, `d` is already a config object. Let me trace the flow more carefully. + +Actually, the cleanest approach: capture the network string before `d` gets reassigned. + +Add `const isMainnet = ...` BEFORE the `if (d === "mainnet" || d === "testnet")` block: + +```typescript +const network = typeof d === "string" ? d : undefined; +``` + +Then the construction becomes: +```typescript +const ickbUdt = new IckbUdt( + network ? (network === "mainnet" ? MAINNET_XUDT_CODE : TESTNET_XUDT_CODE) + : d.udt.cellDeps[0]?.outPoint ?? { txHash: "0x", index: "0x0" }, + IckbUdt.typeScriptFrom( + ccc.Script.from(d.udt.script), + ccc.Script.from(d.logic.script), + ), + network ? (network === "mainnet" ? MAINNET_LOGIC_CODE : TESTNET_LOGIC_CODE) + : d.logic.cellDeps[0]?.outPoint ?? { txHash: "0x", index: "0x0" }, + d.logic.script, + dao, +); +``` + +Note: For devnet, the OutPoints are derived from the first cellDep's outPoint (best available from the ScriptDeps interface). The devnet config shape remains unchanged -- this is a pragmatic fallback. Document this with a comment. + +Alternative (simpler, per Claude's discretion): Since the devnet path is secondary and code OutPoints are only used by `addCellDeps`, devnet users can also use their dep group OutPoints as code OutPoints if their deps happen to be `depType: "code"` (which they are in local dev). Use `d.udt.cellDeps[0]?.outPoint` with a fallback. + +**Step 5: Update LogicManager and OwnedOwnerManager construction** + +Remove the `ickbUdt` argument from LogicManager and OwnedOwnerManager constructors: + +Change: +```typescript +const logic = new LogicManager( + d.logic.script, + d.logic.cellDeps, + dao, + ickbUdt, +); +const ownedOwner = new OwnedOwnerManager( + d.ownedOwner.script, + d.ownedOwner.cellDeps, + dao, + ickbUdt, +); +``` + +To: +```typescript +const logic = new LogicManager(d.logic.script, d.logic.cellDeps, dao); +const ownedOwner = new OwnedOwnerManager( + d.ownedOwner.script, + d.ownedOwner.cellDeps, + dao, +); +``` + +The OrderManager line already passes `ickbUdt.script` (from Phase 4). + +**Step 6: Audit SDK and apps for ErrorTransactionInsufficientCoin catch blocks** + +Run: `grep -r "ErrorTransactionInsufficientCoin" packages/sdk/ apps/` and confirm zero catch blocks exist. RESEARCH.md already found zero catch blocks across the entire codebase (the error was only thrown, never caught). Document this finding: `ErrorUdtInsufficientCoin` is CCC's responsibility -- it surfaces to callers unchanged from `completeInputsByBalance`. No SDK-side migration of catch blocks is needed. + +If any catch blocks ARE found (unexpected), replace `ErrorTransactionInsufficientCoin` with `ErrorUdtInsufficientCoin` from `@ckb-ccc/udt` and update the import. + +**Step 7: Run `pnpm check:full`** + +This validates the entire stack compiles: all 5 library packages with strict TypeScript settings. This is the final gate for Phase 5. + +If `pnpm check:full` fails, investigate and fix the errors. Common issues: +- Missing catalog entry for `@ckb-ccc/udt` (should be added in Plan 05-01) +- Type mismatch on `IckbUdt` constructor parameters +- ScriptDeps import breakage (should not happen -- ScriptDeps is in utils.ts, not udt.ts) + + + cd /workspaces/stack && pnpm check:full + + + - SDK imports IckbUdt (not IckbUdtManager) from @ickb/core + - getConfig() return type uses IckbUdt (not IckbUdtManager) + - IckbUdt constructed with code OutPoints (MAINNET_XUDT_CODE, MAINNET_LOGIC_CODE / testnet equivalents) + - IckbUdt.typeScriptFrom used to compute type script (not hardcoded or from ScriptDeps) + - LogicManager constructed without ickbUdt argument (3 params) + - OwnedOwnerManager constructed without ickbUdt argument (3 params) + - Code OutPoint constants added for both mainnet and testnet + - Devnet path uses cellDeps[0].outPoint as fallback for code OutPoints + - SDK and apps audited for ErrorTransactionInsufficientCoin catch blocks -- zero found, no migration required + - pnpm check:full passes with zero errors across all 5 library packages + + + + + + +1. `pnpm check:full` passes -- all 5 library packages compile with zero type errors +2. `! test -f packages/utils/src/udt.ts` -- udt.ts deleted from utils +3. `! grep -q "udt.js" packages/utils/src/index.ts` -- barrel export removed +4. `grep -r "UdtHandler\|UdtManager\|UdtCell\|findUdts\|addUdts\|isUdtSymbol\|ErrorTransactionInsufficientCoin\|IckbUdtManager" packages/` returns no matches +5. `grep -q "new IckbUdt" packages/sdk/src/constants.ts` -- SDK constructs IckbUdt +6. `grep -q "IckbUdt.typeScriptFrom" packages/sdk/src/constants.ts` -- SDK uses typeScriptFrom +7. `grep -r "udtBalanceFrom" packages/` returns no matches (deprecated API eliminated) +8. `grep -r "getInputsUdtBalance\|getOutputsUdtBalance\|completeInputsByUdt" packages/` returns no matches + + + +- packages/utils/src/udt.ts fully deleted (~406 lines removed) +- UdtHandler, UdtManager, ErrorTransactionInsufficientCoin, UdtCell, findUdts, addUdts, isUdtSymbol no longer exist in codebase +- No deprecated CCC API calls (udtBalanceFrom, getInputsUdtBalance, etc.) remain in any package +- SDK constructs IckbUdt with individual code OutPoints (mainnet + testnet) +- SDK uses IckbUdt.typeScriptFrom to compute type script +- LogicManager and OwnedOwnerManager constructed without ickbUdt argument +- ScriptDeps, ExchangeRatio, ValueComponents preserved (not deleted) +- pnpm check:full passes across all 5 library packages + + + +After completion, create `.planning/phases/05-ickb-core-udt-refactor/05-02-SUMMARY.md` + diff --git a/.planning/phases/05-ickb-core-udt-refactor/05-02-SUMMARY.md b/.planning/phases/05-ickb-core-udt-refactor/05-02-SUMMARY.md new file mode 100644 index 0000000..636dc52 --- /dev/null +++ b/.planning/phases/05-ickb-core-udt-refactor/05-02-SUMMARY.md @@ -0,0 +1,105 @@ +--- +phase: 05-ickb-core-udt-refactor +plan: 02 +subsystem: core +tags: [udt, ccc, xudt, ickb, sdk, deletion, code-deps] + +# Dependency graph +requires: + - phase: 05-ickb-core-udt-refactor + plan: 01 + provides: IckbUdt class extending udt.Udt, LogicManager/OwnedOwnerManager without udtHandler +provides: + - Deleted UDT infrastructure from @ickb/utils (~406 lines) + - SDK getConfig() constructing IckbUdt with individual code OutPoints + - Code cell OutPoint constants for mainnet and testnet (xUDT + Logic) +affects: [sdk, bot, apps] + +# Tech tracking +tech-stack: + added: [] + patterns: [IckbUdt constructed with individual code OutPoints, network capture for code OutPoint selection] + +key-files: + created: [] + modified: + - packages/utils/src/index.ts + - packages/sdk/src/constants.ts + deleted: + - packages/utils/src/udt.ts + +key-decisions: + - "IckbUdt.typeScriptFrom computes type script dynamically from raw UDT and Logic scripts (not hardcoded)" + - "Devnet code OutPoints fallback to cellDeps[0].outPoint -- pragmatic since devnet deps are typically depType: code" + - "ErrorTransactionInsufficientCoin had zero catch blocks across entire codebase -- clean deletion with no migration" + +patterns-established: + - "SDK network capture pattern: extract network string before d gets reassigned, use for conditional constants" + - "Code OutPoint constants: hardcoded per-network OutPoints for individual code deps, sourced from deployment.toml" + +requirements-completed: [SMTX-05, SMTX-07, SMTX-10, UDT-04] + +# Metrics +duration: 4min +completed: 2026-02-26 +--- + +# Phase 5 Plan 2: Delete UDT Infrastructure and Wire SDK Summary + +**Deleted UdtHandler/UdtManager/ErrorTransactionInsufficientCoin from @ickb/utils (~406 lines) and rewired SDK to construct IckbUdt with individual xUDT and Logic code OutPoints** + +## Performance + +- **Duration:** 4 min +- **Started:** 2026-02-26T16:16:16Z +- **Completed:** 2026-02-26T16:20:08Z +- **Tasks:** 2 +- **Files modified:** 2 modified, 1 deleted + +## Accomplishments +- Deleted packages/utils/src/udt.ts removing UdtHandler, UdtManager, ErrorTransactionInsufficientCoin, UdtCell, findUdts, addUdts, isUdtSymbol (~406 lines) +- Updated SDK getConfig() to construct IckbUdt with individual code OutPoints (mainnet/testnet) and IckbUdt.typeScriptFrom for script computation +- Removed ickbUdt argument from LogicManager and OwnedOwnerManager construction in SDK +- Added 4 code cell OutPoint constants (mainnet xUDT, mainnet Logic, testnet xUDT, testnet Logic) sourced from deployment.toml +- Confirmed zero ErrorTransactionInsufficientCoin catch blocks across entire codebase -- clean deletion + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Delete utils/src/udt.ts and update barrel export** - `c5d4363` (refactor) +2. **Task 2: Update SDK getConfig to construct IckbUdt with code OutPoints and verify full stack** - `6400cc4` (feat) + +## Files Created/Modified +- `packages/utils/src/udt.ts` - DELETED: removed ~406 lines of UdtHandler, UdtManager, ErrorTransactionInsufficientCoin, UdtCell, findUdts, addUdts, isUdtSymbol +- `packages/utils/src/index.ts` - Removed "export * from ./udt.js" barrel export +- `packages/sdk/src/constants.ts` - IckbUdt import, code OutPoint constants, IckbUdt construction with typeScriptFrom, LogicManager/OwnedOwnerManager 3-param construction + +## Decisions Made +- Used IckbUdt.typeScriptFrom to compute type script dynamically from raw UDT and Logic scripts, rather than passing d.udt.script directly (which would bypass the correct args computation) +- Devnet fallback uses cellDeps[0].outPoint for code OutPoints -- pragmatic since devnet deps are typically depType: code +- Network string captured before d gets reassigned via `const network = typeof d === "string" ? d : undefined` for clean conditional logic + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered +None + +## User Setup Required +None - no external service configuration required. + +## Next Phase Readiness +- Phase 5 complete: IckbUdt replaces all UDT infrastructure, full stack compiles +- All deprecated CCC API calls (udtBalanceFrom, getInputsUdtBalance, etc.) eliminated +- ScriptDeps, ExchangeRatio, ValueComponents preserved for downstream consumers +- Ready for Phase 6 (SDK refactor) or PR preparation + +## Self-Check: PASSED + +All files exist, all commits verified. + +--- +*Phase: 05-ickb-core-udt-refactor* +*Completed: 2026-02-26* diff --git a/.planning/phases/05-ickb-core-udt-refactor/05-CONTEXT.md b/.planning/phases/05-ickb-core-udt-refactor/05-CONTEXT.md new file mode 100644 index 0000000..f970388 --- /dev/null +++ b/.planning/phases/05-ickb-core-udt-refactor/05-CONTEXT.md @@ -0,0 +1,84 @@ +# Phase 5: @ickb/core UDT Refactor - Context + +**Gathered:** 2026-02-26 +**Status:** Ready for planning + + +## Phase Boundary + +Implement `IckbUdt extends udt.Udt` in `@ickb/core` with `infoFrom` override for multi-representation balance (xUDT + receipts + deposits). Delete `UdtHandler` interface, `UdtManager` class, `ErrorTransactionInsufficientCoin`, `UdtCell`, `findUdts`, `addUdts` from `@ickb/utils`. Remove `udtHandler` parameter from `LogicManager` and `OwnedOwnerManager` (same pattern as Phase 4 OrderManager fix). Replace deprecated CCC API calls in `@ickb/core` and `@ickb/utils`. Update SDK to construct and use `IckbUdt`. + + + + +## Implementation Decisions + +### UDT completion & state compression +- Drop `compressState` feature entirely — CCC's `completeInputsByBalance` handles completion +- Callers use `ickbUdt.completeInputsByBalance(tx, signer)` directly — no convenience wrapper +- Destructure return as needed: `const { tx } = await ickbUdt.completeInputsByBalance(...)` — `addedCount` available but not required +- Trust CCC fully for completion — `infoFrom` provides accurate cell valuations, CCC handles dual-constraint (balance + capacity) optimization +- Stick to `completeInputsByBalance` only — `completeInputsAll` and `completeByTransfer` are inherited but not documented for iCKB callers + +### CellDeps strategy +- IckbUdt overrides `addCellDeps` to add both xUDT code dep AND iCKB Logic code dep (individual `depType: "code"` deps, not dep group) +- Constructor takes `code: OutPointLike` (xUDT script code cell) via base class + `logicCode: OutPointLike` (iCKB Logic script code cell) as new param +- Individual code cell OutPoints sourced from `forks/contracts/` (mainnet + testnet deployments) +- Only IckbUdt switches to code deps pattern in Phase 5; other managers (DaoManager, LogicManager, OrderManager, OwnedOwnerManager) keep `CellDep[]` for now +- Mixed patterns (code deps + dep groups) coexist temporarily — `tx.addCellDeps` deduplicates + +### Cell discovery (findUdts) +- Delete `findUdts`, `addUdts`, `UdtCell` interface, `isUdtSymbol` — all internal to `UdtManager`, no external consumers +- CCC's `completeInputs` (used internally by `completeInputsByBalance`) handles cell discovery via `Udt.filter` +- CCC's `isUdt()` length check (>= 16 bytes) is equivalent to current `>= 34` hex chars — no iCKB-specific reason for old threshold + +### Error reporting +- Accept CCC's `ErrorUdtInsufficientCoin` from `completeInputsByBalance` — callers (SDK, UI) format error messages themselves +- Delete `ErrorTransactionInsufficientCoin` class from `@ickb/utils` +- Plain `Error` throws for header-not-found in `infoFrom` (exceptional path — CCC cache should provide headers) +- Phase 5 handles SDK error handling updates (not deferred to Phase 6) + +### calculateScript → typeScriptFrom +- Renamed to `IckbUdt.typeScriptFrom(udt, ickbLogic)` — static method, CCC-aligned naming +- Keep current parameter types: `(udt: ccc.Script, ickbLogic: ccc.Script): ccc.Script` +- Computes the `script` param for IckbUdt constructor (token identity via args) + +### Manager dependency chain +- LogicManager: remove `udtHandler: UdtHandler` constructor param, remove `tx.addCellDeps(this.udtHandler.cellDeps)` calls (2 sites) — UDT cellDeps are caller responsibility +- OwnedOwnerManager: same treatment — remove `udtHandler` param, remove cellDeps calls (2 sites) +- This matches Phase 4's OrderManager pattern exactly +- With all three managers cleaned, `UdtHandler` interface has zero consumers → delete from `@ickb/utils` +- `ScriptDeps` interface: researcher should check if any consumers remain after `UdtHandler` deletion + +### Claude's Discretion +- Constructor parameter for `IckbUdt`: whether to take `CellDep[]` or single `CellDep` for the dep group — Claude picks cleanest pattern +- Internal organization of the `infoFrom` override code +- How to structure the `@ckb-ccc/udt` dependency addition to `@ickb/core` package.json +- Exact migration of SDK `IckbUdtManager` construction to `IckbUdt` construction + + + + +## Specific Ideas + +- CCC Udt's `completeInputsByBalance` capacity handling is more sophisticated than current `completeUdt` — caps capacity at tx fee to avoid over-providing from UDT cells +- CCC's `ErrorUdtInsufficientCoin` has a `reason` field for custom messages — could be used later for iCKB-specific formatting if needed (note for researcher) +- Researcher should check for catch blocks referencing the old `ErrorTransactionInsufficientCoin` class across SDK and apps +- Matching bot scenario: `infoFrom` may be called frequently during order matching. CCC `Client.cache` handles header fetch dedup, but researcher should evaluate if additional caching is needed for high-frequency scenarios +- Individual code cell OutPoints (replacing dep group) can be found in `forks/contracts/` for both mainnet and testnet + + + + +## Deferred Ideas + +- **ValueComponents redesign**: `udtValue` field name is ambiguous in multi-UDT context (which UDT?). CCC's `UdtInfo` scoped to specific Udt instance is cleaner. Evaluate renaming/replacing across all packages in a future phase +- **All managers to code deps**: Switch DaoManager, LogicManager, OrderManager, OwnedOwnerManager from `CellDep[]` to individual code OutPoints (CCC pattern). Phase 5 only migrates IckbUdt +- **infoFrom caching for matching bot**: If matching bot performance becomes a bottleneck, add cell→UdtInfo result caching in IckbUdt to avoid recomputation across trial transactions + + + +--- + +*Phase: 05-ickb-core-udt-refactor* +*Context gathered: 2026-02-26* diff --git a/.planning/phases/05-ickb-core-udt-refactor/05-RESEARCH.md b/.planning/phases/05-ickb-core-udt-refactor/05-RESEARCH.md new file mode 100644 index 0000000..f12f1b8 --- /dev/null +++ b/.planning/phases/05-ickb-core-udt-refactor/05-RESEARCH.md @@ -0,0 +1,589 @@ +# Phase 5: @ickb/core UDT Refactor - Research + +**Researched:** 2026-02-26 +**Domain:** CCC UDT subclassing, iCKB multi-representation balance, code deps migration +**Confidence:** HIGH + +## Summary + +Phase 5 replaces the custom `IckbUdtManager` (extending `UdtManager`) in `@ickb/core` with `IckbUdt` (extending `udt.Udt` from `@ckb-ccc/udt`). The core challenge is overriding `infoFrom()` to account for iCKB's three on-chain value representations: xUDT cells (positive balance), receipt cells (positive, input only), and deposit cells (negative, input only). The CCC `Udt` class provides all required override points and a complete input-completion pipeline (`completeInputsByBalance`) that consumes the output of `infoFrom`. + +Simultaneously, the phase removes `udtHandler` from `LogicManager` and `OwnedOwnerManager` (matching Phase 4's `OrderManager` pattern), deletes the entire UDT infrastructure from `@ickb/utils` (`UdtHandler`, `UdtManager`, `ErrorTransactionInsufficientCoin`, `UdtCell`, `findUdts`, `addUdts`, `isUdtSymbol`), and migrates the SDK from `IckbUdtManager` to `IckbUdt`. The dep group pattern is replaced with individual code deps (xUDT OutPoint + iCKB Logic OutPoint) for `IckbUdt` only. + +All required code cell OutPoints are available in `forks/contracts/scripts/deployment/` for both mainnet and testnet. The `@ckb-ccc/udt` package is already available via the local CCC fork build (workspace-linked by `.pnpmfile.cjs`). + +**Primary recommendation:** Implement `IckbUdt extends udt.Udt` with `infoFrom` override as the sole customization point. Use the CCC `completeInputsByBalance` pipeline for balance completion. Delete all UDT infrastructure from `@ickb/utils` since `IckbUdt` replaces it entirely. + + +## User Constraints (from CONTEXT.md) + +### Locked Decisions +- Drop `compressState` feature entirely -- CCC's `completeInputsByBalance` handles completion +- Callers use `ickbUdt.completeInputsByBalance(tx, signer)` directly -- no convenience wrapper +- Destructure return as needed: `const { tx } = await ickbUdt.completeInputsByBalance(...)` -- `addedCount` available but not required +- Trust CCC fully for completion -- `infoFrom` provides accurate cell valuations, CCC handles dual-constraint (balance + capacity) optimization +- Stick to `completeInputsByBalance` only -- `completeInputsAll` and `completeByTransfer` are inherited but not documented for iCKB callers +- IckbUdt overrides `addCellDeps` to add both xUDT code dep AND iCKB Logic code dep (individual `depType: "code"` deps, not dep group) +- Constructor takes `code: OutPointLike` (xUDT script code cell) via base class + `logicCode: OutPointLike` (iCKB Logic script code cell) as new param +- Individual code cell OutPoints sourced from `forks/contracts/` (mainnet + testnet deployments) +- Only IckbUdt switches to code deps pattern in Phase 5; other managers (DaoManager, LogicManager, OrderManager, OwnedOwnerManager) keep `CellDep[]` for now +- Mixed patterns (code deps + dep groups) coexist temporarily -- `tx.addCellDeps` deduplicates +- Delete `findUdts`, `addUdts`, `UdtCell` interface, `isUdtSymbol` -- all internal to `UdtManager`, no external consumers +- CCC's `completeInputs` (used internally by `completeInputsByBalance`) handles cell discovery via `Udt.filter` +- CCC's `isUdt()` length check (>= 16 bytes) is equivalent to current `>= 34` hex chars -- no iCKB-specific reason for old threshold +- Accept CCC's `ErrorUdtInsufficientCoin` from `completeInputsByBalance` -- callers (SDK, UI) format error messages themselves +- Delete `ErrorTransactionInsufficientCoin` class from `@ickb/utils` +- Plain `Error` throws for header-not-found in `infoFrom` (exceptional path -- CCC cache should provide headers) +- Phase 5 handles SDK error handling updates (not deferred to Phase 6) +- Renamed to `IckbUdt.typeScriptFrom(udt, ickbLogic)` -- static method, CCC-aligned naming +- Keep current parameter types: `(udt: ccc.Script, ickbLogic: ccc.Script): ccc.Script` +- Computes the `script` param for IckbUdt constructor (token identity via args) +- LogicManager: remove `udtHandler: UdtHandler` constructor param, remove `tx.addCellDeps(this.udtHandler.cellDeps)` calls (2 sites) -- UDT cellDeps are caller responsibility +- OwnedOwnerManager: same treatment -- remove `udtHandler` param, remove cellDeps calls (2 sites) +- This matches Phase 4's OrderManager pattern exactly +- With all three managers cleaned, `UdtHandler` interface has zero consumers -> delete from `@ickb/utils` +- `ScriptDeps` interface: researcher should check if any consumers remain after `UdtHandler` deletion + +### Claude's Discretion +- Constructor parameter for `IckbUdt`: whether to take `CellDep[]` or single `CellDep` for the dep group -- Claude picks cleanest pattern +- Internal organization of the `infoFrom` override code +- How to structure the `@ckb-ccc/udt` dependency addition to `@ickb/core` package.json +- Exact migration of SDK `IckbUdtManager` construction to `IckbUdt` construction + +### Deferred Ideas (OUT OF SCOPE) +- **ValueComponents redesign**: `udtValue` field name is ambiguous in multi-UDT context (which UDT?). CCC's `UdtInfo` scoped to specific Udt instance is cleaner. Evaluate renaming/replacing across all packages in a future phase +- **All managers to code deps**: Switch DaoManager, LogicManager, OrderManager, OwnedOwnerManager from `CellDep[]` to individual code OutPoints (CCC pattern). Phase 5 only migrates IckbUdt +- **infoFrom caching for matching bot**: If matching bot performance becomes a bottleneck, add cell->UdtInfo result caching in IckbUdt to avoid recomputation across trial transactions + + + +## Phase Requirements + +| ID | Description | Research Support | +|----|-------------|-----------------| +| SMTX-05 | UDT handler registration replaced by direct Udt instance usage | LogicManager/OwnedOwnerManager `udtHandler` removed (4 sites); `UdtHandler` interface deleted; callers pass `ickbUdt.script` to managers and call `ickbUdt.addCellDeps(tx)` externally | +| SMTX-07 | IckbUdtManager multi-representation balance logic preserved | `IckbUdt.infoFrom()` override handles xUDT cells (positive), receipt cells (positive, input only), deposit cells (negative, input only); conservation law preserved via accurate sign conventions | +| SMTX-10 | Deprecated CCC API calls replaced | `ccc.udtBalanceFrom()` calls (3 in `UdtManager`) eliminated by deleting `UdtManager`; `IckbUdtManager.getInputsUdtBalance()` replaced by `IckbUdt.infoFrom()` override; no deprecated APIs remain | +| UDT-04 | IckbUdt extends udt.Udt implemented | `IckbUdt` class with `infoFrom` override, `addCellDeps` override (individual code deps), `typeScriptFrom` static method, LogicManager/OwnedOwnerManager `udtHandler` removed | + + +## Standard Stack + +### Core +| Library | Version | Purpose | Why Standard | +|---------|---------|---------|--------------| +| `@ckb-ccc/udt` | workspace (local CCC fork) | Base `Udt` class for subclassing | CCC's official UDT abstraction; `infoFrom` is the override point for multi-representation balance | +| `@ckb-ccc/core` | catalog: ^1.12.2 | CKB core types, Transaction, Client | Already used across all packages | +| `@ckb-ccc/ssri` | workspace (local CCC fork) | `ssri.Trait` base class (parent of `Udt`) | Transitive dependency; `Udt extends ssri.Trait` provides `code: OutPoint` | + +### Supporting +| Library | Version | Purpose | When to Use | +|---------|---------|---------|-------------| +| `@ickb/dao` | workspace:* | `DaoManager.isDeposit()` for deposit cell identification | Already a dependency of `@ickb/core`; used in `infoFrom` for deposit cell detection | +| `@ickb/utils` | workspace:* | `ExchangeRatio`, `ValueComponents`, `ScriptDeps`, utility functions | Already a dependency; `ScriptDeps` still used by managers after `UdtHandler` deletion | + +### Alternatives Considered +| Instead of | Could Use | Tradeoff | +|------------|-----------|----------| +| Subclass `Udt` | Keep `UdtManager` | Loses CCC's completion pipeline, keeps duplicated code | +| Override `infoFrom` only | Override `getInputsInfo`/`getOutputsInfo` | `infoFrom` is simpler -- single override handles both inputs and outputs | +| Dep group for IckbUdt | Individual code deps | Dep groups semantically imply "all cells in group needed"; CCC author prefers individual code deps | + +**Installation:** +Add `@ckb-ccc/udt` to `@ickb/core/package.json` dependencies. With the local CCC fork build active, `.pnpmfile.cjs` automatically rewires to workspace:*: +```json +{ + "dependencies": { + "@ckb-ccc/core": "catalog:", + "@ckb-ccc/udt": "catalog:", + "@ickb/dao": "workspace:*", + "@ickb/utils": "workspace:*" + } +} +``` + +**Note on catalog:** `@ckb-ccc/udt` is NOT currently in the pnpm-workspace.yaml catalog. It needs to be added OR use `"workspace:*"` directly since the `.pnpmfile.cjs` hook rewires it anyway. Recommendation: add `"@ckb-ccc/udt": "^1.12.2"` to the catalog for consistency with `@ckb-ccc/core`, though the pnpmfile hook will override it to `workspace:*` when the fork is present. + +## Architecture Patterns + +### IckbUdt Class Structure +``` +@ickb/core/src/udt.ts +├── IckbUdt extends udt.Udt +│ ├── constructor(code, script, logicCode, logicScript, daoManager) +│ ├── static typeScriptFrom(udt, ickbLogic): Script +│ ├── override infoFrom(client, cells, acc?): Promise +│ └── override addCellDeps(txLike): Transaction +├── ickbValue(capacity, header): FixedPoint (unchanged) +├── convert(isCkb2Udt, amount, rate, ...): FixedPoint (unchanged) +├── ickbExchangeRatio(header, ...): ExchangeRatio (unchanged) +└── constants (AR_0, ICKB_DEPOSIT_CAP, etc.) (unchanged) +``` + +### Pattern 1: infoFrom Override for Multi-Representation Balance +**What:** Override `infoFrom()` to value three cell types: xUDT (positive), receipts (positive, input only), deposits (negative, input only). +**When to use:** Called by CCC's `getInputsInfo()` and `getOutputsInfo()` which pass resolved cells. +**Key insight:** `infoFrom` receives `CellAnyLike` which may or may not have `outPoint`. Input cells (from `getInputsInfo`) have `outPoint` (needed for header fetches). Output cells (from `getOutputsInfo`) do not have `outPoint` -- receipt and deposit cells should only appear as inputs, so the absence of `outPoint` naturally excludes them from output-side processing. + +```typescript +// Source: Verified from CCC Udt source (forks/ccc/packages/udt/src/udt/index.ts) +import { udt } from "@ckb-ccc/udt"; + +export class IckbUdt extends udt.Udt { + constructor( + code: ccc.OutPointLike, // xUDT code cell OutPoint (via base Trait) + script: ccc.ScriptLike, // iCKB UDT type script (via base Udt) + public readonly logicCode: ccc.OutPoint, // iCKB Logic code cell OutPoint + public readonly logicScript: ccc.Script, // iCKB Logic script + public readonly daoManager: DaoManager, // for isDeposit check + ) { + super(code, script); + } + + async infoFrom( + client: ccc.Client, + cells: ccc.CellAnyLike | ccc.CellAnyLike[], + acc?: udt.UdtInfoLike, + ): Promise { + const info = udt.UdtInfo.from(acc).clone(); + + for (const cellLike of [cells].flat()) { + const cell = ccc.CellAny.from(cellLike); + + // Standard xUDT cell -- delegate to base class pattern + if (this.isUdt(cell)) { + info.addAssign({ + balance: udt.Udt.balanceFromUnsafe(cell.outputData), + capacity: cell.cellOutput.capacity, + count: 1, + }); + continue; + } + + // Receipt and deposit cells need outPoint for header fetch. + // Output cells (no outPoint) are skipped -- correct by design. + if (!cell.outPoint) { + continue; + } + + const { type, lock } = cell.cellOutput; + + // Receipt cell: type === logicScript + if (type && this.logicScript.eq(type)) { + const txWithHeader = await client.getTransactionWithHeader( + cell.outPoint.txHash, + ); + if (!txWithHeader?.header) { + throw new Error("Header not found for txHash"); + } + const { depositQuantity, depositAmount } = + ReceiptData.decode(cell.outputData); + info.addAssign({ + balance: ickbValue(depositAmount, txWithHeader.header) * + depositQuantity, + capacity: cell.cellOutput.capacity, + count: 1, + }); + continue; + } + + // Deposit cell: lock === logicScript AND isDeposit + if (this.logicScript.eq(lock) && this.daoManager.isDeposit(cell)) { + const txWithHeader = await client.getTransactionWithHeader( + cell.outPoint.txHash, + ); + if (!txWithHeader?.header) { + throw new Error("Header not found for txHash"); + } + info.addAssign({ + balance: -ickbValue(cell.capacityFree, txWithHeader.header), + capacity: cell.cellOutput.capacity, + count: 1, + }); + continue; + } + } + + return info; + } + + override addCellDeps(txLike: ccc.TransactionLike): ccc.Transaction { + const tx = ccc.Transaction.from(txLike); + // xUDT code dep (from base class's this.code) + tx.addCellDeps({ outPoint: this.code, depType: "code" }); + // iCKB Logic code dep + tx.addCellDeps({ outPoint: this.logicCode, depType: "code" }); + return tx; + } + + static typeScriptFrom(udt: ccc.Script, ickbLogic: ccc.Script): ccc.Script { + const { codeHash, hashType } = udt; + return new ccc.Script( + codeHash, + hashType, + [ickbLogic.hash(), "00000080"].join("") as ccc.Hex, + ); + } +} +``` + +### Pattern 2: Manager udtHandler Removal (Phase 4 Pattern) +**What:** Remove `udtHandler: UdtHandler` constructor parameter and `tx.addCellDeps(this.udtHandler.cellDeps)` calls from `LogicManager` and `OwnedOwnerManager`. +**When to use:** Follows Phase 4's `OrderManager` pattern exactly. +**Example (LogicManager):** +```typescript +// BEFORE: +constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly daoManager: DaoManager, + public readonly udtHandler: UdtHandler, // REMOVE +) {} + +// AFTER: +constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly daoManager: DaoManager, +) {} + +// BEFORE (in deposit, completeDeposit methods): +tx.addCellDeps(this.udtHandler.cellDeps); // REMOVE (4 sites total: 2 in LogicManager, 2 in OwnedOwnerManager) + +// Caller responsibility (SDK or app code): +tx = ickbUdt.addCellDeps(tx); // Caller adds UDT cellDeps before or after calling manager methods +``` + +### Pattern 3: SDK Construction Migration +**What:** Replace `IckbUdtManager` construction with `IckbUdt` in `getConfig()`. +**Key change:** `getConfig` needs xUDT code OutPoint and iCKB Logic code OutPoint (not dep group). + +```typescript +// BEFORE: +const ickbUdt = new IckbUdtManager( + d.udt.script, // UDT type script + d.udt.cellDeps, // dep group CellDep[] + d.logic.script, // logic script + dao, // DaoManager +); + +// AFTER: +const ickbUdt = new IckbUdt( + udtCode, // xUDT code cell OutPoint + d.udt.script, // UDT type script (computed via typeScriptFrom) + logicCode, // iCKB Logic code cell OutPoint + d.logic.script, // logic script + dao, // DaoManager +); +``` + +### Anti-Patterns to Avoid +- **Overriding `getInputsInfo`/`getOutputsInfo` instead of `infoFrom`:** The Phase 3 decision (03-02) explicitly chose `infoFrom` as the sole override point. `getInputsInfo` and `getOutputsInfo` both delegate to `infoFrom` already. +- **Adding CCC `isUdt()` workaround:** CCC's `isUdt()` checks `>= 16 bytes` data length, which is equivalent to the old `>= 34` hex chars check. No iCKB-specific override needed. +- **Wrapping `completeInputsByBalance`:** The decision explicitly says "no convenience wrapper." Callers use `ickbUdt.completeInputsByBalance(tx, signer)` directly. +- **Keeping `UdtHandler` for backward compatibility:** The interface has zero consumers after LogicManager/OwnedOwnerManager cleanup. Delete it cleanly. + +## Don't Hand-Roll + +| Problem | Don't Build | Use Instead | Why | +|---------|-------------|-------------|-----| +| UDT cell discovery | Custom `findUdts` generator | `Udt.completeInputs` (via `filter`) | CCC handles dedup, pagination, signer address resolution | +| Balance completion pipeline | Custom `completeUdt` | `Udt.completeInputsByBalance` | CCC handles dual-constraint (balance + capacity) optimization, change output, error throwing | +| UDT balance extraction | `ccc.udtBalanceFrom()` | `Udt.balanceFromUnsafe()` | `udtBalanceFrom` is deprecated; `balanceFromUnsafe` is the replacement | +| Insufficient coin error | Custom `ErrorTransactionInsufficientCoin` | CCC's `ErrorUdtInsufficientCoin` | CCC throws it from `completeInputsByBalance`; has `amount`, `type`, `reason` fields | + +**Key insight:** The entire `UdtManager` class (cell finding, balance calculation, input completion, change output) is replaced by CCC's `Udt` class. The only custom code needed is the `infoFrom` override for multi-representation balance. + +## Common Pitfalls + +### Pitfall 1: Receipt/Deposit Cells in Outputs +**What goes wrong:** `infoFrom` accidentally counts receipt or deposit cells in transaction outputs, breaking the conservation law. +**Why it happens:** `getOutputsInfo` also calls `infoFrom`. If `infoFrom` doesn't distinguish inputs from outputs, receipt/deposit cells could be double-counted. +**How to avoid:** Check for `cell.outPoint` existence. Input cells have `outPoint` (resolved by `CellInput.getCell()`). Output cells created by `Array.from(tx.outputCells)` do NOT have `outPoint`. Receipt and deposit cells should only appear as inputs, so checking `outPoint` naturally excludes them from output processing. +**Warning signs:** Balance reported by `getOutputsInfo` includes non-xUDT cell values. + +### Pitfall 2: DaoManager.isDeposit Requires Full Cell +**What goes wrong:** `daoManager.isDeposit(cell)` called on a `CellAny` that hasn't been fully resolved. +**Why it happens:** `CellAny.from(cellLike)` preserves the `outPoint`, `cellOutput`, and `outputData` from the input. `isDeposit` checks `outputData` for the 8-zero-byte DAO deposit marker. +**How to avoid:** `isDeposit` only checks `outputData` format (8 zero bytes = deposit), not headers. It works on any `CellAny`-compatible object as long as `outputData` is present. The `CellAny.from()` factory preserves `outputData` from the `CellAnyLike`, so this works correctly. Verify by checking `DaoManager.isDeposit` signature accepts `CellAny` (or a compatible type). +**Warning signs:** `isDeposit` returns false for valid deposit cells because `outputData` is missing. + +### Pitfall 3: ScriptDeps Interface Consumers After Deletion +**What goes wrong:** `ScriptDeps` interface deleted along with `UdtHandler`, breaking downstream imports. +**Why it happens:** `ScriptDeps` is used by `LogicManager`, `OwnedOwnerManager`, `OrderManager`, `DaoManager`, and `getConfig()` (SDK constants). It has many consumers beyond `UdtHandler`. +**How to avoid:** `ScriptDeps` MUST be preserved. Only `UdtHandler` extends it and is deleted. `ScriptDeps` remains in `@ickb/utils/utils.ts` and continues to be imported by all manager classes. +**Warning signs:** Compilation errors in packages importing `ScriptDeps`. + +### Pitfall 4: Catalog Entry for @ckb-ccc/udt +**What goes wrong:** `pnpm install` fails because `@ckb-ccc/udt` is not in the catalog and `catalog:` specifier is used. +**Why it happens:** Currently only `@ckb-ccc/core` is in the pnpm-workspace.yaml catalog. The `.pnpmfile.cjs` hook rewires to `workspace:*` when the fork is present, but catalog: specifier needs a matching entry. +**How to avoid:** Either add `"@ckb-ccc/udt": "^1.12.2"` to the catalog in `pnpm-workspace.yaml`, OR use `"workspace:*"` directly in `@ickb/core/package.json`. The pnpmfile hook will rewrite either way when forks are present. Using `catalog:` is cleaner for consistency but requires the catalog entry. +**Warning signs:** `pnpm install` errors about unresolved catalog specifier. + +### Pitfall 5: IckbUdt Constructor and ssri.Trait +**What goes wrong:** `IckbUdt` constructor doesn't properly call `super()` with the right arguments. +**Why it happens:** `Udt extends ssri.Trait`, and `Trait` expects `(code: OutPointLike, executor?: Executor)`. The `Udt` constructor is `(code: OutPointLike, script: ScriptLike, config?: UdtConfigLike)`. `IckbUdt` must pass `code` (xUDT OutPoint) and `script` (iCKB UDT type script) to `super()`, plus store `logicCode` and other iCKB-specific params. +**How to avoid:** Keep constructor simple: `super(code, script)` passes xUDT code OutPoint and iCKB type script to base `Udt`. No executor needed (legacy xUDT, not SSRI). Store `logicCode`, `logicScript`, `daoManager` as own properties. +**Warning signs:** `this.code` doesn't point to xUDT code cell; `this.script` doesn't match iCKB UDT type script. + +## Code Examples + +### IckbUdt addCellDeps Override +```typescript +// Source: Verified pattern from CCC Udt.addCellDeps (forks/ccc/packages/udt/src/udt/index.ts:856-863) +override addCellDeps(txLike: ccc.TransactionLike): ccc.Transaction { + const tx = ccc.Transaction.from(txLike); + // xUDT code dep (base class stores this as this.code from ssri.Trait) + tx.addCellDeps({ outPoint: this.code, depType: "code" }); + // iCKB Logic code dep (new param) + tx.addCellDeps({ outPoint: this.logicCode, depType: "code" }); + return tx; +} +``` + +### SDK getConfig Migration +```typescript +// Source: Current SDK constants.ts construction + planned migration + +// NEW: xUDT and Logic code cell OutPoints (from forks/contracts/scripts/deployment/) +const MAINNET_XUDT_CODE = { txHash: "0xc07844ce21b38e4b071dd0e1ee3b0e27afd8d7532491327f39b786343f558ab7", index: "0x0" }; +const MAINNET_LOGIC_CODE = { txHash: "0xd7309191381f5a8a2904b8a79958a9be2752dbba6871fa193dab6aeb29dc8f44", index: "0x0" }; +const TESTNET_XUDT_CODE = { txHash: "0xbf6fb538763efec2a70a6a3dcb7242787087e1030c4e7d86585bc63a9d337f5f", index: "0x0" }; +const TESTNET_LOGIC_CODE = { txHash: "0x9ac989b3355764f76cdce02c69dedb819fdfbcbda49a7db1a2c9facdfdb9a7fe", index: "0x0" }; + +// In getConfig(): +const ickbUdt = new IckbUdt( + d === "mainnet" ? MAINNET_XUDT_CODE : TESTNET_XUDT_CODE, // xUDT code OutPoint + IckbUdt.typeScriptFrom( // iCKB UDT type script + ccc.Script.from(UDT), + ccc.Script.from(ICKB_LOGIC), + ), + d === "mainnet" ? MAINNET_LOGIC_CODE : TESTNET_LOGIC_CODE, // Logic code OutPoint + ccc.Script.from(ICKB_LOGIC), // Logic script + dao, // DaoManager +); +``` + +### Error Handling Migration in SDK +```typescript +// Source: CCC ErrorUdtInsufficientCoin (forks/ccc/packages/udt/src/udt/index.ts:27-83) + +// BEFORE (old pattern): +import { ErrorTransactionInsufficientCoin } from "@ickb/utils"; +try { + // ... +} catch (e) { + if (e instanceof ErrorTransactionInsufficientCoin) { ... } +} + +// AFTER (CCC pattern): +import { ErrorUdtInsufficientCoin } from "@ckb-ccc/udt"; +try { + const { tx } = await ickbUdt.completeInputsByBalance(tx, signer); +} catch (e) { + if (e instanceof ErrorUdtInsufficientCoin) { + // e.amount: shortfall amount + // e.type: UDT type script + // e.message: "Insufficient coin, need {amount} extra coin" + } +} +``` + +### LogicManager After udtHandler Removal +```typescript +// Source: Current logic.ts with modifications applied + +export class LogicManager implements ScriptDeps { + constructor( + public readonly script: ccc.Script, + public readonly cellDeps: ccc.CellDep[], + public readonly daoManager: DaoManager, + // udtHandler REMOVED + ) {} + + async deposit(txLike, depositQuantity, depositAmount, lock, client) { + let tx = ccc.Transaction.from(txLike); + // ... + tx.addCellDeps(this.cellDeps); + // tx.addCellDeps(this.udtHandler.cellDeps); // REMOVED + // ... + } + + completeDeposit(txLike, receipts) { + const tx = ccc.Transaction.from(txLike); + // ... + tx.addCellDeps(this.cellDeps); + // tx.addCellDeps(this.udtHandler.cellDeps); // REMOVED + // ... + } +} +``` + +## State of the Art + +| Old Approach | Current Approach | When Changed | Impact | +|--------------|------------------|--------------|--------| +| `UdtHandler` interface + `UdtManager` class | CCC `udt.Udt` class with `infoFrom` override | Phase 5 | Entire custom UDT infrastructure deleted | +| `ccc.udtBalanceFrom()` (deprecated) | `udt.Udt.balanceFromUnsafe()` | CCC upstream | Deprecated calls eliminated by deleting `UdtManager` | +| `ccc.ErrorTransactionInsufficientCoin` (deprecated) | `udt.ErrorUdtInsufficientCoin` | CCC upstream | New error class with `amount`, `type`, `reason` fields | +| `IckbUdtManager.getInputsUdtBalance()` | `IckbUdt.infoFrom()` (override) | Phase 5 | Single override point handles both inputs and outputs | +| Dep group `CellDep` for all iCKB scripts | Individual code OutPoints for IckbUdt | Phase 5 | xUDT + Logic code deps added individually | +| `IckbUdtManager.calculateScript()` | `IckbUdt.typeScriptFrom()` | Phase 5 | Static method renamed for CCC alignment | + +**Deprecated/outdated:** +- `UdtHandler` interface: Replaced by CCC `udt.Udt` type +- `UdtManager` class: Replaced by CCC `udt.Udt` class (which has same capabilities + completion pipeline) +- `ErrorTransactionInsufficientCoin`: Replaced by `ErrorUdtInsufficientCoin` from `@ckb-ccc/udt` +- `UdtCell` interface, `findUdts`, `addUdts`, `isUdtSymbol`: All internal to `UdtManager`, no external consumers + +## Detailed Code Analysis + +### Files to Modify + +| File | Changes | Lines Affected | +|------|---------|----------------| +| `packages/core/src/udt.ts` | Replace `IckbUdtManager` with `IckbUdt extends udt.Udt`; rename `calculateScript` to `typeScriptFrom` | ~100 lines (rewrite class, keep `ickbValue`, `convert`, `ickbExchangeRatio`, constants) | +| `packages/core/src/logic.ts` | Remove `udtHandler` constructor param; remove 2 `tx.addCellDeps(this.udtHandler.cellDeps)` calls; remove `UdtHandler` import | ~6 lines changed | +| `packages/core/src/owned_owner.ts` | Remove `udtHandler` constructor param; remove 2 `tx.addCellDeps(this.udtHandler.cellDeps)` calls; remove `UdtHandler` import | ~6 lines changed | +| `packages/core/package.json` | Add `@ckb-ccc/udt` dependency | 1 line | +| `packages/utils/src/udt.ts` | Delete entire file | ~406 lines deleted | +| `packages/utils/src/index.ts` | Remove `export * from "./udt.js"` | 1 line | +| `packages/sdk/src/constants.ts` | Rewrite `getConfig()` to construct `IckbUdt`; add code OutPoint constants; adjust `LogicManager`/`OwnedOwnerManager` construction (no `ickbUdt` arg); pass `ickbUdt.script` to `OrderManager` | ~30 lines | +| `packages/sdk/src/sdk.ts` | No changes needed -- SDK uses managers, not UdtHandler directly | 0 lines | + +### ScriptDeps Consumer Audit + +`ScriptDeps` is imported/used in these locations (MUST survive `UdtHandler` deletion): + +| File | Usage | Status | +|------|-------|--------| +| `packages/utils/src/utils.ts` | Interface definition | Keep (canonical location) | +| `packages/core/src/logic.ts` | `LogicManager implements ScriptDeps` | Keep | +| `packages/core/src/owned_owner.ts` | `OwnedOwnerManager implements ScriptDeps` | Keep | +| `packages/order/src/order.ts` | `OrderManager implements ScriptDeps` | Keep | +| `packages/dao/src/dao.ts` | `DaoManager implements ScriptDeps` | Keep | +| `packages/sdk/src/constants.ts` | `getConfig()` param type | Keep | +| `packages/utils/src/udt.ts` | `UdtHandler extends ScriptDeps`, `UdtManager implements UdtHandler` | Deleted with file | + +**Conclusion:** `ScriptDeps` has 6 consumers remaining after `UdtHandler`/`UdtManager` deletion. It MUST be preserved. + +### ExchangeRatio Consumer Audit + +`ExchangeRatio` from `@ickb/utils` is used in: +| File | Usage | +|------|-------| +| `packages/core/src/udt.ts` | `convert()` function parameter type | +| `packages/sdk/src/sdk.ts` | Imported from `@ickb/utils` (indirect via `@ickb/core`) | + +**Conclusion:** `ExchangeRatio` stays in `@ickb/utils/utils.ts`. Not affected by udt.ts deletion. + +### Catch Blocks for ErrorTransactionInsufficientCoin + +Searched across all apps and packages: +- **`packages/`**: Only thrown in `packages/utils/src/udt.ts:258` (inside `UdtManager.completeUdt`). No catch blocks. +- **`apps/`**: No catch blocks referencing `ErrorTransactionInsufficientCoin`. Zero hits. +- **Conclusion:** Deleting the class has no impact on error handling. CCC's `ErrorUdtInsufficientCoin` (thrown by `completeInputsByBalance`) is the only error callers will encounter going forward. + +### Code Cell OutPoints (from forks/contracts/scripts/deployment/) + +| Network | Script | tx_hash | index | +|---------|--------|---------|-------| +| Mainnet | xUDT | `0xc07844ce21b38e4b071dd0e1ee3b0e27afd8d7532491327f39b786343f558ab7` | 0 | +| Mainnet | iCKB Logic | `0xd7309191381f5a8a2904b8a79958a9be2752dbba6871fa193dab6aeb29dc8f44` | 0 | +| Testnet | xUDT | `0xbf6fb538763efec2a70a6a3dcb7242787087e1030c4e7d86585bc63a9d337f5f` | 0 | +| Testnet | iCKB Logic | `0x9ac989b3355764f76cdce02c69dedb819fdfbcbda49a7db1a2c9facdfdb9a7fe` | 0 | + +Source: `forks/contracts/scripts/deployment/mainnet/deployment.toml` and `forks/contracts/scripts/deployment/testnet/deployment.toml` (HIGH confidence -- direct file examination). + +### CCC Udt.infoFrom Signature Details + +```typescript +// From forks/ccc/packages/udt/src/udt/index.ts:624-641 +async infoFrom( + _client: ccc.Client, // Client for network requests (subclasses use it) + cells: ccc.CellAnyLike | ccc.CellAnyLike[], // Single cell or array + acc?: UdtInfoLike, // Optional accumulator for running totals +): Promise +``` + +Key details: +- `_client` parameter: Base class doesn't use it (local-only operation), but it's available for subclass network requests (header fetches in IckbUdt) +- `cells` is flattened with `[cells].flat()` -- accepts single cell or array +- `acc` is optional starting accumulator; `UdtInfo.from(acc).clone()` creates a safe copy +- Returns `UdtInfo` with `{ balance, capacity, count }` fields (all mutable) +- `balance` can go negative (deposit cells subtract) -- this is intentional for conservation law accounting +- CCC's `completeInputsByBalance` uses `infoFrom` as the accumulator in `completeInputs`, checking `info.balance >= 0 && info.capacity >= 0` to stop + +### CCC getInputsInfo Flow + +```typescript +// From forks/ccc/packages/udt/src/udt/index.ts:1099-1108 +async getInputsInfo(client, txLike): Promise { + const tx = ccc.Transaction.from(txLike); + const inputCells = await Promise.all( + tx.inputs.map((input) => input.getCell(client)), // Resolves to Cell (has outPoint) + ); + return this.infoFrom(client, inputCells); // Passes Cell[] to infoFrom +} +``` + +`CellInput.getCell(client)` returns a `Cell` (extends `CellAny`, has guaranteed `outPoint`). So when `infoFrom` is called from `getInputsInfo`, all cells have `outPoint`. When called from `getOutputsInfo`, cells come from `tx.outputCells` which creates `CellAny` without `outPoint`. + +### DaoManager.isDeposit Compatibility + +```typescript +// DaoManager.isDeposit checks cell.outputData for 8 zero bytes (DAO deposit marker) +// It accepts ccc.Cell which extends CellAny +// CellAny.from(cellLike) preserves outputData from CellAnyLike +// So passing CellAny to isDeposit works IF outputData has 8+ bytes +``` + +The `daoManager.isDeposit(cell)` call in `infoFrom` receives a `CellAny`. Need to verify `isDeposit` accepts `CellAny` (not just `Cell`). Checking the `DaoManager.isDeposit` signature: + +```typescript +// From packages/dao/src/dao.ts (Phase 1 migrated signature) +isDeposit(cell: ccc.Cell): boolean +``` + +This takes `ccc.Cell`, not `ccc.CellAny`. Since `Cell extends CellAny`, a `CellAny` is NOT a `Cell`. However, for input cells in `infoFrom`, `getInputsInfo` resolves to `Cell` objects (via `CellInput.getCell()`), so this works for inputs. For output cells, we skip deposit detection (no `outPoint` check gates it). This is correct by design -- deposits only appear as inputs. + +## Open Questions + +1. **getConfig devnet path** + - What we know: `getConfig()` accepts a devnet object with `ScriptDeps` for each script. Phase 5 changes IckbUdt construction from `ScriptDeps` (script + cellDeps) to code OutPoints. + - What's unclear: The devnet path provides `{ udt: ScriptDeps, logic: ScriptDeps, ... }`. With `IckbUdt` needing code OutPoints instead of `CellDep[]`, the devnet interface needs adjustment. The `udt` entry would need to provide an OutPoint (for xUDT code cell) instead of `cellDeps`. + - Recommendation: For devnet, accept either: (a) a new `codeOutPoint` field in each ScriptDeps-like config, or (b) restructure the devnet config type to match the new needs. Since devnet usage is secondary, keep it minimal -- document the interface change. + +2. **pnpm-workspace.yaml catalog entry** + - What we know: `@ckb-ccc/core` has `catalog:` entry; `@ckb-ccc/udt` does not. + - What's unclear: Whether to add `@ckb-ccc/udt` to catalog or use `workspace:*` directly. + - Recommendation: Add `"@ckb-ccc/udt": "^1.12.2"` to catalog alongside `@ckb-ccc/core` for consistency. The pnpmfile hook rewrites to `workspace:*` when forks are present. + +## Sources + +### Primary (HIGH confidence) +- CCC `Udt` class source: `/workspaces/stack/forks/ccc/packages/udt/src/udt/index.ts` -- `infoFrom`, `addCellDeps`, `completeInputsByBalance`, `getInputsInfo`, `getOutputsInfo` signatures and implementations verified +- CCC `ssri.Trait` source: `/workspaces/stack/forks/ccc/packages/ssri/src/trait.ts` -- constructor `(code: OutPointLike, executor?: Executor)` verified +- CCC `CellAny` class: `/workspaces/stack/forks/ccc/packages/core/src/ckb/transaction.ts:313-432` -- `outPoint` optional, `capacityFree` getter verified +- CCC `ErrorUdtInsufficientCoin`: `/workspaces/stack/forks/ccc/packages/udt/src/udt/index.ts:27-83` -- `amount`, `type`, `reason` fields verified +- Current `IckbUdtManager`: `/workspaces/stack/packages/core/src/udt.ts` -- full source examined +- Current `UdtManager`/`UdtHandler`: `/workspaces/stack/packages/utils/src/udt.ts` -- full source examined +- Current `LogicManager`: `/workspaces/stack/packages/core/src/logic.ts` -- `udtHandler` usage at 2 sites confirmed +- Current `OwnedOwnerManager`: `/workspaces/stack/packages/core/src/owned_owner.ts` -- `udtHandler` usage at 2 sites confirmed +- SDK `getConfig()`: `/workspaces/stack/packages/sdk/src/constants.ts` -- construction flow verified +- Contract deployments: `/workspaces/stack/forks/contracts/scripts/deployment/mainnet/deployment.toml` and testnet equivalent -- xUDT and Logic code cell OutPoints verified + +### Secondary (MEDIUM confidence) +- Phase 3 decision document: `.planning/phases/03-ccc-udt-integration-investigation/03-DECISION.md` -- `infoFrom` as sole override point, cell discovery boundary +- Phase 4 context/execution: `.planning/phases/04-deprecated-ccc-api-replacement/` -- OrderManager pattern for udtHandler removal +- Project STATE.md accumulated decisions -- cross-referenced with source code + +## Metadata + +**Confidence breakdown:** +- Standard stack: HIGH -- all libraries verified from local source code; `@ckb-ccc/udt` available via workspace +- Architecture: HIGH -- `infoFrom` override pattern verified from CCC source; all 4 `udtHandler` removal sites identified and confirmed +- Pitfalls: HIGH -- `outPoint` availability, `ScriptDeps` consumers, catalog entry all verified against source + +**Research date:** 2026-02-26 +**Valid until:** 2026-03-26 (stable -- all source code is local, no external API changes expected) diff --git a/.planning/phases/05-ickb-core-udt-refactor/05-VERIFICATION.md b/.planning/phases/05-ickb-core-udt-refactor/05-VERIFICATION.md new file mode 100644 index 0000000..0d863ec --- /dev/null +++ b/.planning/phases/05-ickb-core-udt-refactor/05-VERIFICATION.md @@ -0,0 +1,102 @@ +--- +phase: 05-ickb-core-udt-refactor +verified: 2026-02-26T17:00:00Z +status: passed +score: 8/8 must-haves verified +re_verification: false +--- + +# Phase 5: @ickb/core UDT Refactor Verification Report + +**Phase Goal:** Implement `IckbUdt extends udt.Udt` in `@ickb/core`, replacing `IckbUdtManager`. Delete entire UDT infrastructure from `@ickb/utils`. Wire SDK to construct `IckbUdt`. +**Verified:** 2026-02-26T17:00:00Z +**Status:** passed +**Re-verification:** No — initial verification + +## Goal Achievement + +### Observable Truths + +| # | Truth | Status | Evidence | +|----|-------|--------|----------| +| 1 | `IckbUdt extends udt.Udt` exists in `@ickb/core` with `infoFrom` override | VERIFIED | `packages/core/src/udt.ts` line 20: `export class IckbUdt extends udt.Udt`, line 84: `override async infoFrom` | +| 2 | `infoFrom` correctly values xUDT cells (positive), receipt cells (positive, input only), deposit cells (negative, input only) | VERIFIED | xUDT: `udt.Udt.balanceFromUnsafe` (line 97); receipt: `ickbValue(depositAmount, ...) * depositQuantity` (lines 124-128); deposit: `-ickbValue(cell.capacityFree, ...)` (lines 146-149); output cells gated by `!cell.outPoint` check (line 106) | +| 3 | `IckbUdt.addCellDeps` overridden to add individual code deps | VERIFIED | `packages/core/src/udt.ts` line 168: `override addCellDeps`; adds xUDT code dep (line 171) and Logic code dep (line 173) with `depType: "code"` | +| 4 | `IckbUdt.typeScriptFrom` static method computes iCKB UDT type script | VERIFIED | `packages/core/src/udt.ts` line 57: `static typeScriptFrom(udt: ccc.Script, ickbLogic: ccc.Script): ccc.Script` | +| 5 | `LogicManager` and `OwnedOwnerManager` no longer take `udtHandler` parameter; no `tx.addCellDeps(this.udtHandler.cellDeps)` calls remain | VERIFIED | `logic.ts` constructor has 3 params (script, cellDeps, daoManager); `owned_owner.ts` constructor has 3 params; grep confirms zero `udtHandler` references across both files | +| 6 | `UdtHandler`, `UdtManager`, `ErrorTransactionInsufficientCoin`, `UdtCell`, `findUdts`, `addUdts`, `isUdtSymbol` deleted from `@ickb/utils` | VERIFIED | `packages/utils/src/udt.ts` deleted; `packages/utils/src/index.ts` has no `udt.js` export; grep across all packages confirms zero references | +| 7 | SDK constructs `IckbUdt` with individual code OutPoints; passes `ickbUdt.script` to `OrderManager` | VERIFIED | `packages/sdk/src/constants.ts` line 3: `import { IckbUdt, ... }`, line 63: `new IckbUdt(...)` with `MAINNET_XUDT_CODE`/`TESTNET_XUDT_CODE` and `MAINNET_LOGIC_CODE`/`TESTNET_LOGIC_CODE`; line 86: `OrderManager(d.order.script, d.order.cellDeps, ickbUdt.script)` | +| 8 | `pnpm check:full` passes | VERIFIED | Exit code 0; all 5 library packages lint, build, and test clean | + +**Score:** 8/8 truths verified + +### Required Artifacts + +| Artifact | Expected | Status | Details | +|----------|----------|--------|---------| +| `packages/core/src/udt.ts` | IckbUdt class extending udt.Udt with infoFrom, addCellDeps, typeScriptFrom | VERIFIED | 249 lines; `class IckbUdt extends udt.Udt` at line 20; all three overrides present | +| `packages/core/src/logic.ts` | LogicManager without udtHandler parameter | VERIFIED | Constructor at line 28-32: 3 params only; zero `udtHandler` references | +| `packages/core/src/owned_owner.ts` | OwnedOwnerManager without udtHandler parameter | VERIFIED | Constructor at line 23-27: 3 params only; zero `udtHandler` references | +| `packages/core/package.json` | `@ckb-ccc/udt` dependency | VERIFIED | Line 57: `"@ckb-ccc/udt": "catalog:"` | +| `pnpm-workspace.yaml` | Catalog entry for `@ckb-ccc/udt` | VERIFIED | Line 16: `"@ckb-ccc/udt": ^1.12.2` | +| `packages/utils/src/udt.ts` | Deleted | VERIFIED | File does not exist; confirmed by `test -f` returning false | +| `packages/utils/src/index.ts` | Barrel export without `udt.js` | VERIFIED | 3 lines only: codec.js, heap.js, utils.js | +| `packages/sdk/src/constants.ts` | `getConfig()` constructing `IckbUdt` with code OutPoints | VERIFIED | `new IckbUdt(` at line 63; `IckbUdt.typeScriptFrom` at line 69; 4 code OutPoint constants added (lines 168-202) | + +### Key Link Verification + +| From | To | Via | Status | Details | +|------|----|-----|--------|---------| +| `packages/core/src/udt.ts` | `@ckb-ccc/udt` | `import { udt } from '@ckb-ccc/udt'` | WIRED | Line 2: exact import; `udt.Udt`, `udt.UdtInfo`, `udt.UdtInfoLike` all used | +| `packages/core/src/udt.ts` | `packages/core/src/entities.ts` | `ReceiptData.decode` for receipt cell valuation | WIRED | Line 3: `import { ReceiptData }`, line 122: `ReceiptData.decode(cell.outputData)` | +| `packages/core/src/udt.ts` | `@ickb/dao` | `daoManager.isDeposit` for deposit cell identification | WIRED | Line 4: `import type { DaoManager }`, line 137: `this.daoManager.isDeposit(cell as ccc.Cell)` | +| `packages/sdk/src/constants.ts` | `packages/core/src/udt.ts` | `import { IckbUdt }` and constructor call | WIRED | Line 2: `import { IckbUdt, ... } from "@ickb/core"`, line 63: `new IckbUdt(...)` | +| `packages/sdk/src/constants.ts` | `packages/core/src/udt.ts` | `IckbUdt.typeScriptFrom` for script computation | WIRED | Line 69: `IckbUdt.typeScriptFrom(ccc.Script.from(d.udt.script), ccc.Script.from(d.logic.script))` | + +### Requirements Coverage + +| Requirement | Source Plan | Description | Status | Evidence | +|-------------|------------|-------------|--------|----------| +| SMTX-05 | 05-01, 05-02 | UDT handler registration replaced by direct Udt instance usage | SATISFIED | LogicManager/OwnedOwnerManager no longer take `udtHandler`; UdtHandler/UdtManager/etc deleted from utils; SDK uses `IckbUdt` instance | +| SMTX-07 | 05-01, 05-02 | IckbUdtManager multi-representation UDT balance logic survives intact | SATISFIED | `IckbUdt.infoFrom` handles xUDT (positive), receipts (positive via `ickbValue`), deposits (negative via `ickbValue`); conservation law encoded in sign conventions | +| SMTX-10 | 05-01, 05-02 | Deprecated CCC API calls replaced | SATISFIED | grep confirms zero `udtBalanceFrom`, `getInputsUdtBalance`, `getOutputsUdtBalance`, `completeInputsByUdt` across all packages | +| UDT-04 | 05-01, 05-02 | `IckbUdt extends udt.Udt` with infoFrom, addCellDeps, typeScriptFrom; managers cleaned | SATISFIED | Full implementation verified; `typeScriptFrom` replaces `calculateScript`; all 4 manager `udtHandler.cellDeps` call sites removed | + +All 4 requirements from plan frontmatter are satisfied. No orphaned requirements found (no additional Phase 5 requirements in REQUIREMENTS.md traceability table beyond those declared). + +### Anti-Patterns Found + +No anti-patterns detected in modified files: +- No TODO/FIXME/HACK/PLACEHOLDER comments +- No empty return stubs (`return null`, `return {}`, etc.) +- No console-only handlers +- All method overrides have substantive implementations + +| File | Line | Pattern | Severity | Impact | +|------|------|---------|----------|--------| +| — | — | — | — | No issues found | + +### Human Verification Required + +None required. All success criteria are verifiable through static code analysis and `pnpm check:full`. + +The following items are noted as runtime-only and untestable without a live CKB node, but they are architectural/wiring correct: + +1. **`infoFrom` header fetch correctness** — The `client.getTransactionWithHeader(cell.outPoint.txHash)` calls fetch headers correctly only when connected to a real CKB RPC. Static analysis confirms the call pattern matches Phase 1's established pattern (replacing `getHeader()`). No human action needed for phase acceptance; this is a runtime concern for Phase 6/7. + +### Gaps Summary + +No gaps found. All 8 observable truths are verified. The phase goal is fully achieved: + +- `IckbUdt extends udt.Udt` is implemented with a substantive `infoFrom` override that handles all three iCKB cell representations +- `addCellDeps` adds individual code deps (not dep group) as specified +- `typeScriptFrom` static method correctly computes the UDT type script +- All `udtHandler` references removed from `LogicManager` and `OwnedOwnerManager` +- The entire UDT infrastructure (`UdtHandler`, `UdtManager`, `ErrorTransactionInsufficientCoin`, `UdtCell`, `findUdts`, `addUdts`, `isUdtSymbol`) is deleted from `@ickb/utils` +- SDK wiring is complete: `IckbUdt` constructed with correct code OutPoints for mainnet and testnet; `IckbUdt.typeScriptFrom` used for script computation; `LogicManager` and `OwnedOwnerManager` constructed without `ickbUdt` +- `pnpm check:full` passes with exit code 0 (both `check:fresh` and `check:ci` stages) + +--- + +_Verified: 2026-02-26T17:00:00Z_ +_Verifier: AI Coworker (gsd-verifier)_ diff --git a/package.json b/package.json index 7671183..57eeac7 100644 --- a/package.json +++ b/package.json @@ -39,5 +39,5 @@ "typescript-eslint": "^8.56.1", "vitest": "^3.2.4" }, - "packageManager": "pnpm@10.30.2+sha512.36cdc707e7b7940a988c9c1ecf88d084f8514b5c3f085f53a2e244c2921d3b2545bc20dd4ebe1fc245feec463bb298aecea7a63ed1f7680b877dc6379d8d0cb4" + "packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017" } diff --git a/packages/core/package.json b/packages/core/package.json index 4ccd4a3..1d5ae7e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -53,6 +53,7 @@ }, "dependencies": { "@ckb-ccc/core": "catalog:", + "@ckb-ccc/udt": "catalog:", "@ickb/dao": "workspace:*", "@ickb/utils": "workspace:*" } diff --git a/packages/core/src/logic.ts b/packages/core/src/logic.ts index 0311b3a..a8ef205 100644 --- a/packages/core/src/logic.ts +++ b/packages/core/src/logic.ts @@ -3,7 +3,6 @@ import { DaoManager } from "@ickb/dao"; import { defaultFindCellsLimit, type ScriptDeps, - type UdtHandler, unique, } from "@ickb/utils"; import { @@ -25,13 +24,11 @@ export class LogicManager implements ScriptDeps { * @param script - The script associated with the manager. * @param cellDeps - The cell dependencies for the manager. * @param daoManager - The DAO manager for handling deposits and receipts. - * @param udtHandler - The handler for User Defined Tokens (UDTs). */ constructor( public readonly script: ccc.Script, public readonly cellDeps: ccc.CellDep[], public readonly daoManager: DaoManager, - public readonly udtHandler: UdtHandler, ) {} /** @@ -63,6 +60,9 @@ export class LogicManager implements ScriptDeps { * @param depositQuantity - The quantity of deposits. * @param depositAmount - The amount of each deposit. * @param lock - The lock script for the output receipt cell. + * + * @remarks Caller must ensure UDT cellDeps are added to the transaction + * (e.g., via ickbUdt.addCellDeps(tx)). */ async deposit( txLike: ccc.TransactionLike, @@ -85,7 +85,6 @@ export class LogicManager implements ScriptDeps { } tx.addCellDeps(this.cellDeps); - tx.addCellDeps(this.udtHandler.cellDeps); const capacities = Array.from( { length: depositQuantity }, @@ -111,6 +110,9 @@ export class LogicManager implements ScriptDeps { * * @param txLike - The transaction to add the receipts to. * @param receipts - The receipts to add to the transaction. + * + * @remarks Caller must ensure UDT cellDeps are added to the transaction + * (e.g., via ickbUdt.addCellDeps(tx)). */ completeDeposit( txLike: ccc.TransactionLike, @@ -122,7 +124,6 @@ export class LogicManager implements ScriptDeps { } tx.addCellDeps(this.cellDeps); - tx.addCellDeps(this.udtHandler.cellDeps); for (const r of receipts) { const hash = r.header.header.hash; diff --git a/packages/core/src/owned_owner.ts b/packages/core/src/owned_owner.ts index ff63e45..154c3a3 100644 --- a/packages/core/src/owned_owner.ts +++ b/packages/core/src/owned_owner.ts @@ -3,7 +3,6 @@ import { defaultFindCellsLimit, unique, type ScriptDeps, - type UdtHandler, } from "@ickb/utils"; import { daoCellFrom, DaoManager } from "@ickb/dao"; import { OwnerData } from "./entities.js"; @@ -20,13 +19,11 @@ export class OwnedOwnerManager implements ScriptDeps { * @param script - The script associated with the OwnedOwner script. * @param cellDeps - The cell dependencies for the OwnedOwner script. * @param daoManager - The DAO manager for handling withdrawal requests. - * @param udtHandler - The handler for User Defined Tokens (UDTs). */ constructor( public readonly script: ccc.Script, public readonly cellDeps: ccc.CellDep[], public readonly daoManager: DaoManager, - public readonly udtHandler: UdtHandler, ) {} /** @@ -64,6 +61,9 @@ export class OwnedOwnerManager implements ScriptDeps { * @param options - Optional parameters for the withdrawal request. * @param options.isReadyOnly - Whether to only process ready deposits (default: false). * @returns void + * + * @remarks Caller must ensure UDT cellDeps are added to the transaction + * (e.g., via ickbUdt.addCellDeps(tx)). */ async requestWithdrawal( txLike: ccc.TransactionLike, @@ -92,7 +92,6 @@ export class OwnedOwnerManager implements ScriptDeps { options, ); tx.addCellDeps(this.cellDeps); - tx.addCellDeps(this.udtHandler.cellDeps); const outputData = OwnerData.encode({ ownedDistance: -deposits.length }); // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -118,6 +117,9 @@ export class OwnedOwnerManager implements ScriptDeps { * @param options - Optional parameters for the withdrawal process. * @param options.isReadyOnly - Whether to only process ready withdrawal groups (default: false). * @returns void + * + * @remarks Caller must ensure UDT cellDeps are added to the transaction + * (e.g., via ickbUdt.addCellDeps(tx)). */ async withdraw( txLike: ccc.TransactionLike, @@ -137,7 +139,6 @@ export class OwnedOwnerManager implements ScriptDeps { } tx.addCellDeps(this.cellDeps); - tx.addCellDeps(this.udtHandler.cellDeps); const requests = withdrawalGroups.map((g) => g.owned); tx = await this.daoManager.withdraw(tx, requests, client); diff --git a/packages/core/src/udt.ts b/packages/core/src/udt.ts index 276fcb2..591d278 100644 --- a/packages/core/src/udt.ts +++ b/packages/core/src/udt.ts @@ -1,50 +1,60 @@ import { ccc } from "@ckb-ccc/core"; +import { udt } from "@ckb-ccc/udt"; import { ReceiptData } from "./entities.js"; import type { DaoManager } from "@ickb/dao"; -import { UdtManager, type ExchangeRatio, type UdtHandler } from "@ickb/utils"; +import type { ExchangeRatio } from "@ickb/utils"; /** - * IckbUdtManager is a class that implements the UdtHandler interface. - * It is responsible for handling UDT (User Defined Token) operations related to iCKB. + * IckbUdt extends CCC's Udt class to provide accurate multi-representation + * balance for iCKB tokens. The iCKB conservation law is: + * Input UDT + Input Receipts = Output UDT + Input Deposits + * + * `infoFrom` values three cell types: + * - xUDT cells: positive balance (standard UDT) + * - Receipt cells: positive balance (input only, valued via ickbValue) + * - Deposit cells: negative balance (input only, withdrawal reduces UDT supply) + * + * Output cells without outPoint are naturally excluded from receipt/deposit + * processing, since only input cells (resolved by CellInput.getCell()) have outPoint. */ -export class IckbUdtManager extends UdtManager implements UdtHandler { +export class IckbUdt extends udt.Udt { + public readonly logicCode: ccc.OutPoint; + public readonly logicScript: ccc.Script; + public readonly daoManager: DaoManager; + /** - * Creates an instance of IckbUdtManager. - * @param script - The script associated with the UDT. - * @param cellDeps - An array of cell dependencies. + * Creates an instance of IckbUdt. + * + * @param code - The xUDT code cell OutPoint (passed to base Udt/Trait). + * @param script - The iCKB UDT type script (token identity via args). + * @param logicCode - The iCKB Logic code cell OutPoint. * @param logicScript - The iCKB Logic script. - * @param daoManager - The DAO manager instance. + * @param daoManager - The DAO manager instance for deposit cell identification. */ constructor( - script: ccc.Script, - cellDeps: ccc.CellDep[], - public readonly logicScript: ccc.Script, - public readonly daoManager: DaoManager, + code: ccc.OutPointLike, + script: ccc.ScriptLike, + logicCode: ccc.OutPointLike, + logicScript: ccc.ScriptLike, + daoManager: DaoManager, ) { - super(script, cellDeps, "iCKB", "iCKB", 8); + super(code, script); + this.logicCode = ccc.OutPoint.from(logicCode); + this.logicScript = ccc.Script.from(logicScript); + this.daoManager = daoManager; } /** - * Calculates and returns the iCKB UDT script by combining the existing UDT script - * with the iCKB logic script’s hash. - * - * This method takes the raw UDT script (user-defined token) and the iCKB logic script, - * then recalculates the UDT script’s `args` field by concatenating: - * 1. The iCKB logic script hash - * 2. A fixed 4-byte little-endian length postfix (`"00000080"`) + * Computes the iCKB UDT type script from raw UDT and Logic scripts. * - * This is used to ensure that the UDT cell carries the correct iCKB lock logic - * within its arguments. + * Concatenates the iCKB logic script hash with a fixed 4-byte LE length + * postfix ("00000080") to form the UDT type script args. * - * @param udt - The original UDT (user-defined token) script whose codeHash and hashType - * will be reused for the new script. - * @param ickbLogic - The iCKB logic script whose `hash()` will be prepended - * to the UDT args. - * @returns A new `ccc.Script` instance with: - * - `codeHash` and `hashType` copied from the `udt` parameter - * - `args` set to the concatenation of `ickbLogic.hash()` and `"00000080"` + * @param udt - The raw xUDT script (codeHash and hashType reused). + * @param ickbLogic - The iCKB logic script (hash used for args). + * @returns A new Script with the computed args. */ - static calculateScript(udt: ccc.Script, ickbLogic: ccc.Script): ccc.Script { + static typeScriptFrom(udt: ccc.Script, ickbLogic: ccc.Script): ccc.Script { const { codeHash, hashType } = udt; return new ccc.Script( codeHash, @@ -54,90 +64,113 @@ export class IckbUdtManager extends UdtManager implements UdtHandler { } /** - * Asynchronously retrieves the iCKB UDT input balance (token amount and capacity) - * for a given transaction. + * Computes UDT balance info for iCKB's three cell representations. + * + * For each cell: + * - xUDT cell (type === this.script, data >= 16 bytes): adds positive balance + * - Receipt cell (type === logicScript, has outPoint): adds positive balance + * via ickbValue of deposit amount * quantity + * - Deposit cell (lock === logicScript, isDeposit, has outPoint): adds negative + * balance via ickbValue of free capacity (withdrawal reduces UDT supply) * - * @param client - The CKB client to query cell data. - * @param txLike - The transaction whose inputs are to be balanced. - * @returns A promise resolving to a tuple: - * - [0]: Total iCKB UDT amount in inputs (as `ccc.FixedPoint`). - * - [1]: Total capacity in UDT-related inputs (as `ccc.FixedPoint`). + * Cells without outPoint (output cells from getOutputsInfo) skip receipt/deposit + * processing -- correct by design since these only appear as inputs. + * + * @param client - CKB client for header fetches (receipt/deposit valuation). + * @param cells - Cell or array of cells to evaluate. + * @param acc - Optional accumulator for running totals. + * @returns UdtInfo with balance, capacity, and count. */ - override async getInputsUdtBalance( + override async infoFrom( client: ccc.Client, - txLike: ccc.TransactionLike, - ): Promise<[ccc.FixedPoint, ccc.FixedPoint]> { - const tx = ccc.Transaction.from(txLike); - return ccc.reduceAsync( - tx.inputs, - async (acc, input) => { - // Get all cell info - await input.completeExtraInfos(client); - const { previousOutput: outPoint, cellOutput, outputData } = input; - - // Input is not well defined - if (!cellOutput || !outputData) { - throw new Error("Unable to complete input"); - } - - const { type, lock } = cellOutput; - - if (!type) { - return acc; + cells: ccc.CellAnyLike | ccc.CellAnyLike[], + acc?: udt.UdtInfoLike, + ): Promise { + const info = udt.UdtInfo.from(acc).clone(); + + for (const cellLike of [cells].flat()) { + const cell = ccc.CellAny.from(cellLike); + + // Standard xUDT cell -- delegate to base class pattern + if (this.isUdt(cell)) { + info.addAssign({ + balance: udt.Udt.balanceFromUnsafe(cell.outputData), + capacity: cell.cellOutput.capacity, + count: 1, + }); + continue; + } + + // Receipt and deposit cells need outPoint for header fetch. + // Output cells (no outPoint) are skipped -- correct by design. + if (!cell.outPoint) { + continue; + } + + const { type, lock } = cell.cellOutput; + + // Receipt cell: type === logicScript + if (type && this.logicScript.eq(type)) { + const txWithHeader = await client.getTransactionWithHeader( + cell.outPoint.txHash, + ); + if (!txWithHeader?.header) { + throw new Error("Header not found for txHash"); } - const cell = new ccc.Cell(outPoint, cellOutput, outputData); - - const [udtValue, capacity] = acc; - - // An iCKB UDT Cell - if (this.isUdt(cell)) { - return [ - udtValue + ccc.numFromBytes(ccc.bytesFrom(outputData).slice(0, 16)), - capacity + cellOutput.capacity, - ]; + const { depositQuantity, depositAmount } = + ReceiptData.decode(cell.outputData); + info.addAssign({ + balance: ickbValue(depositAmount, txWithHeader.header) * + depositQuantity, + capacity: cell.cellOutput.capacity, + count: 1, + }); + continue; + } + + // Deposit cell: lock === logicScript AND isDeposit + // Output cells are gated by the !cell.outPoint check above and never reach here. + if ( + this.logicScript.eq(lock) && + this.daoManager.isDeposit(cell) + ) { + const txWithHeader = await client.getTransactionWithHeader( + cell.outPoint.txHash, + ); + if (!txWithHeader?.header) { + throw new Error("Header not found for txHash"); } - // An iCKB Receipt - if (this.logicScript.eq(type)) { - // Get header of Receipt cell - const txWithHeader = await client.getTransactionWithHeader( - outPoint.txHash, - ); - if (!txWithHeader?.header) { - throw new Error("Header not found for txHash"); - } - - const { depositQuantity, depositAmount } = - ReceiptData.decode(outputData); - - return [ - udtValue + - ickbValue(depositAmount, txWithHeader.header) * depositQuantity, - capacity + cellOutput.capacity, - ]; - } + info.addAssign({ + balance: -ickbValue(cell.capacityFree, txWithHeader.header), + capacity: cell.cellOutput.capacity, + count: 1, + }); + continue; + } + } - // An iCKB Deposit for which the withdrawal is being requested - if (this.logicScript.eq(lock) && this.daoManager.isDeposit(cell)) { - // Get header of Deposit cell - const txWithHeader = await client.getTransactionWithHeader( - outPoint.txHash, - ); - if (!txWithHeader?.header) { - throw new Error("Header not found for txHash"); - } - - return [ - udtValue - ickbValue(cell.capacityFree, txWithHeader.header), - capacity + cellOutput.capacity, - ]; - } + return info; + } - return acc; - }, - [0n, 0n], - ); + /** + * Adds iCKB-specific cell dependencies to a transaction. + * + * Adds individual code deps (not dep group) for: + * - xUDT code cell (this.code from ssri.Trait) + * - iCKB Logic code cell (this.logicCode) + * + * @param txLike - The transaction to add cell deps to. + * @returns The transaction with cell deps added. + */ + override addCellDeps(txLike: ccc.TransactionLike): ccc.Transaction { + const tx = ccc.Transaction.from(txLike); + // xUDT code dep + tx.addCellDeps({ outPoint: this.code, depType: "code" }); + // iCKB Logic code dep + tx.addCellDeps({ outPoint: this.logicCode, depType: "code" }); + return tx; } } diff --git a/packages/dao/src/dao.ts b/packages/dao/src/dao.ts index 23382f2..d01b9d6 100644 --- a/packages/dao/src/dao.ts +++ b/packages/dao/src/dao.ts @@ -27,7 +27,7 @@ export class DaoManager implements ScriptDeps { * @param cell - The cell to check. * @returns True if the cell is a deposit, otherwise false. */ - isDeposit(cell: ccc.Cell): boolean { + isDeposit(cell: ccc.CellAny): boolean { const { cellOutput: { type }, outputData, diff --git a/packages/order/src/order.ts b/packages/order/src/order.ts index c1463ea..525c72a 100644 --- a/packages/order/src/order.ts +++ b/packages/order/src/order.ts @@ -4,7 +4,6 @@ import { defaultFindCellsLimit, type ExchangeRatio, type ScriptDeps, - type UdtHandler, type ValueComponents, } from "@ickb/utils"; import { Info, OrderData, Ratio, Relative, type InfoLike } from "./entities.js"; @@ -19,12 +18,12 @@ export class OrderManager implements ScriptDeps { * * @param script - The order script. * @param cellDeps - The cell dependencies for the order. - * @param udtHandler - The handler for UDT (User Defined Token). + * @param udtScript - The UDT (User Defined Token) type script. */ constructor( public readonly script: ccc.Script, public readonly cellDeps: ccc.CellDep[], - public readonly udtHandler: UdtHandler, + public readonly udtScript: ccc.Script, ) {} /** @@ -39,7 +38,7 @@ export class OrderManager implements ScriptDeps { isOrder(cell: ccc.Cell): boolean { return ( cell.cellOutput.lock.eq(this.script) && - Boolean(cell.cellOutput.type?.eq(this.udtHandler.script)) + Boolean(cell.cellOutput.type?.eq(this.udtScript)) ); } @@ -156,10 +155,12 @@ export class OrderManager implements ScriptDeps { * * The method performs the following: * - Creates order data using the provided amounts and order information. - * - Adds required cell dependencies and UDT handlers to the transaction. + * - Adds required cell dependencies to the transaction. * - Appends the order cell to the outputs with the UDT data and adjusts the CKB capacity. * - Appends a corresponding master cell immediately after the order cell. * + * @remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via CCC Udt balance completion). + * * @param txLike - The transaction to which the order will be added. * @param lock - The lock script for the master cell. * @param info - The information related to the order, usually calculated using OrderManager.convert. @@ -187,13 +188,12 @@ export class OrderManager implements ScriptDeps { }); tx.addCellDeps(this.cellDeps); - tx.addCellDeps(this.udtHandler.cellDeps); // Append order cell to Outputs const position = tx.addOutput( { lock: this.script, - type: this.udtHandler.script, + type: this.udtScript, }, data.toBytes(), ); @@ -215,6 +215,8 @@ export class OrderManager implements ScriptDeps { * - Adds the original order as an input. * - Creates an updated output with adjusted CKB capacity and UDT data. * + * @remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via CCC Udt balance completion). + * * @param txLike - The transaction to which the matches will be added. * @param match - The match object containing partial matches. */ @@ -226,14 +228,13 @@ export class OrderManager implements ScriptDeps { } tx.addCellDeps(this.cellDeps); - tx.addCellDeps(this.udtHandler.cellDeps); for (const { order, ckbOut, udtOut } of partials) { tx.addInput(order.cell); tx.addOutput( { lock: this.script, - type: this.udtHandler.script, + type: this.udtScript, capacity: ckbOut, }, OrderData.from({ @@ -495,6 +496,8 @@ export class OrderManager implements ScriptDeps { * * @param txLike - The transaction to which the groups will be added. * @param groups - The array of OrderGroup instances to melt. + * @remarks Caller must ensure UDT cellDeps are added to the transaction (e.g., via CCC Udt balance completion). + * * @param options - Optional parameters: * @param options.isFulfilledOnly - If true, only groups with fulfilled orders will be melted. * @@ -516,7 +519,6 @@ export class OrderManager implements ScriptDeps { return tx; } tx.addCellDeps(this.cellDeps); - tx.addCellDeps(this.udtHandler.cellDeps); for (const group of groups) { tx.addInput(group.order.cell); @@ -615,7 +617,7 @@ export class OrderManager implements ScriptDeps { * Finds simple orders on the blockchain. * * Queries cells whose lock script equals the order script and whose type script - * matches the UDT handler's script, returning only valid {@link OrderCell} instances. + * matches the UDT type script, returning only valid {@link OrderCell} instances. * * @param client – The client used to interact with the blockchain. * @param limit – Maximum cells to scan per findCells batch. @@ -632,7 +634,7 @@ export class OrderManager implements ScriptDeps { script: this.script, scriptType: "lock", filter: { - script: this.udtHandler.script, + script: this.udtScript, }, scriptSearchMode: "exact", withData: true, diff --git a/packages/sdk/src/constants.ts b/packages/sdk/src/constants.ts index d2f271c..ff9c522 100644 --- a/packages/sdk/src/constants.ts +++ b/packages/sdk/src/constants.ts @@ -1,5 +1,5 @@ import { ccc } from "@ckb-ccc/core"; -import { IckbUdtManager, LogicManager, OwnedOwnerManager } from "@ickb/core"; +import { IckbUdt, LogicManager, OwnedOwnerManager } from "@ickb/core"; import { DaoManager } from "@ickb/dao"; import { OrderManager } from "@ickb/order"; import { unique, type ScriptDeps } from "@ickb/utils"; @@ -34,13 +34,16 @@ export function getConfig( ): { managers: { dao: DaoManager; - ickbUdt: IckbUdtManager; + ickbUdt: IckbUdt; logic: LogicManager; ownedOwner: OwnedOwnerManager; order: OrderManager; }; bots: ccc.Script[]; } { + // Capture network before d gets reassigned to ScriptDeps. + const network = typeof d === "string" ? d : undefined; + // If deps is provided as a network string, use the pre-defined constants. if (d === "mainnet" || d === "testnet") { bots = bots.concat( @@ -57,25 +60,49 @@ export function getConfig( } const dao = new DaoManager(d.dao.script, d.dao.cellDeps); - const ickbUdt = new IckbUdtManager( - d.udt.script, - d.udt.cellDeps, - d.logic.script, - dao, - ); - const logic = new LogicManager( + + // xUDT code cell OutPoint: use known constants for mainnet/testnet, + // fall back to first cellDep's outPoint for devnet. + const xudtCode = network + ? network === "mainnet" + ? MAINNET_XUDT_CODE + : TESTNET_XUDT_CODE + : d.udt.cellDeps[0]?.outPoint; + if (!xudtCode) { + throw new Error( + "devnet config missing xUDT code cell outPoint in udt.cellDeps", + ); + } + + // iCKB Logic code cell OutPoint: same pattern as above. + const logicCode = network + ? network === "mainnet" + ? MAINNET_LOGIC_CODE + : TESTNET_LOGIC_CODE + : d.logic.cellDeps[0]?.outPoint; + if (!logicCode) { + throw new Error( + "devnet config missing Logic code cell outPoint in logic.cellDeps", + ); + } + + const ickbUdt = new IckbUdt( + xudtCode, + IckbUdt.typeScriptFrom( + ccc.Script.from(d.udt.script), + ccc.Script.from(d.logic.script), + ), + logicCode, d.logic.script, - d.logic.cellDeps, dao, - ickbUdt, ); + const logic = new LogicManager(d.logic.script, d.logic.cellDeps, dao); const ownedOwner = new OwnedOwnerManager( d.ownedOwner.script, d.ownedOwner.cellDeps, dao, - ickbUdt, ); - const order = new OrderManager(d.order.script, d.order.cellDeps, ickbUdt); + const order = new OrderManager(d.order.script, d.order.cellDeps, ickbUdt.script); return { managers: { @@ -114,13 +141,15 @@ const DAO = { }; /** - * UDT (User Defined Token) lock script information used for onchain validation. + * Raw xUDT type script (codeHash + hashType only). + * The iCKB UDT type script args are computed dynamically by + * IckbUdt.typeScriptFrom() from ICKB_LOGIC. */ const UDT = { codeHash: "0x50bd8d6680b8b9cf98b73f3c08faf8b2a21914311954118ad6609be6e78a1b95", hashType: "data1", - args: "0xb73b6ab39d79390c6de90a09c96b290c331baf1798ed6f97aed02590929734e800000080", + args: "0x", }; /** @@ -153,6 +182,46 @@ const ORDER = { args: "0x", }; +/** + * Mainnet xUDT code cell OutPoint. + * Source: forks/contracts/scripts/deployment/mainnet/deployment.toml + */ +const MAINNET_XUDT_CODE = { + txHash: + "0xc07844ce21b38e4b071dd0e1ee3b0e27afd8d7532491327f39b786343f558ab7", + index: "0x0", +}; + +/** + * Mainnet iCKB Logic code cell OutPoint. + * Source: forks/contracts/scripts/deployment/mainnet/deployment.toml + */ +const MAINNET_LOGIC_CODE = { + txHash: + "0xd7309191381f5a8a2904b8a79958a9be2752dbba6871fa193dab6aeb29dc8f44", + index: "0x0", +}; + +/** + * Testnet xUDT code cell OutPoint. + * Source: forks/contracts/scripts/deployment/testnet/deployment.toml + */ +const TESTNET_XUDT_CODE = { + txHash: + "0xbf6fb538763efec2a70a6a3dcb7242787087e1030c4e7d86585bc63a9d337f5f", + index: "0x0", +}; + +/** + * Testnet iCKB Logic code cell OutPoint. + * Source: forks/contracts/scripts/deployment/testnet/deployment.toml + */ +const TESTNET_LOGIC_CODE = { + txHash: + "0x9ac989b3355764f76cdce02c69dedb819fdfbcbda49a7db1a2c9facdfdb9a7fe", + index: "0x0", +}; + /** * Mainnet dependency group cell dep. */ diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index eb415f1..ffa8b89 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,4 +1,3 @@ export * from "./codec.js"; export * from "./heap.js"; -export * from "./udt.js"; export * from "./utils.js"; diff --git a/packages/utils/src/udt.ts b/packages/utils/src/udt.ts deleted file mode 100644 index e799067..0000000 --- a/packages/utils/src/udt.ts +++ /dev/null @@ -1,405 +0,0 @@ -import { ccc, mol } from "@ckb-ccc/core"; -import { - defaultFindCellsLimit, - unique, - type ScriptDeps, - type ValueComponents, -} from "./utils.js"; - -/** - * Interface representing a handler for User Defined Tokens (UDTs). - * Extends the ScriptDeps interface to include `script` and `cellDeps`. - */ -export interface UdtHandler extends ScriptDeps { - /** - * Asynchronously retrieves the UDT input balance (token amount and capacity) - * for a given transaction. - * - * @param client - The CKB client to query cell data. - * @param txLike - The transaction whose inputs are to be balanced. - * @returns A promise resolving to a tuple: - * - [0]: Total UDT amount in inputs (as `ccc.FixedPoint`). - * - [1]: Total capacity in UDT inputs (as `ccc.FixedPoint`). - */ - getInputsUdtBalance( - client: ccc.Client, - txLike: ccc.TransactionLike, - ): Promise<[ccc.FixedPoint, ccc.FixedPoint]>; - - /** - * Retrieves the UDT output balance (token amount and capacity) - * for a given transaction. - * - * @param txLike - The transaction whose outputs are to be balanced. - * @returns A tuple: - * - [0]: Total UDT amount in outputs (as `ccc.FixedPoint`). - * - [1]: Total capacity in UDT outputs (as `ccc.FixedPoint`). - */ - getOutputsUdtBalance( - txLike: ccc.TransactionLike, - ): [ccc.FixedPoint, ccc.FixedPoint]; - - /** - * Completes a transaction by adding UDT inputs and/or UDT change outputs as needed. - * - * @param signer - Signer providing client access and account scripts. - * @param txLike - The transaction to adjust. - * @param options.shouldAddInputs - Whether to add inputs if insufficient. Defaults to `true`. - * @param options.compressState - Whether to collect all UDT cells to compress state rent. Defaults to `false`. - * @returns A promise resolving to a tuple: - * - [0]: The (possibly mutated) transaction. - * - [1]: Number of UDT inputs added. - * - [2]: `true` if a UDT change output was appended; otherwise `false`. - */ - completeUdt( - signer: ccc.Signer, - txLike: ccc.TransactionLike, - options?: { - shouldAddInputs?: boolean; - compressState?: boolean; - }, - ): Promise<[ccc.Transaction, number, boolean]>; - - /** The canonical name of the UDT. */ - name: string; - - /** The ticker or symbol of the UDT. */ - symbol: string; - - /** The number of decimal places of precision for the UDT. */ - decimals: number; -} - -/** - * Error thrown when a transaction has insufficient UDT balance. - * Extends the CKB core `ErrorTransactionInsufficientCoin` by including token symbol - * and decimal precision in the error message. - * - * UDT Handler implementer should use this error class where appropriate. - */ -export class ErrorTransactionInsufficientCoin extends ccc.ErrorTransactionInsufficientCoin { - /** - * @param amount - The additional amount required (in fixed-point). - * @param type - The UDT type script. - * @param symbol - Token symbol (e.g., "USDT"). - * @param decimals - Decimal precision of the token. - */ - constructor( - amount: ccc.Num, - type: ccc.Script, - public readonly symbol: string, - public readonly decimals: number, - ) { - super(amount, type); - this.message = `Insufficient coin, need ${ccc.fixedPointToString( - amount, - decimals, - )} extra ${symbol} coin`; - } -} - -/** - * UdtManager implements UdtHandler for managing UDT operations: - * - Detecting UDT cells - * - Computing input/output balances - * - Adding inputs or change outputs to meet desired UDT amounts - */ -export class UdtManager implements UdtHandler { - /** - * @param script - The UDT type script. - * @param cellDeps - Cell dependencies required to use the UDT script. - * @param name - The token's full name. - * @param symbol - The token's symbol or ticker. - * @param decimals - Decimal precision for token amounts. - */ - constructor( - public readonly script: ccc.Script, - public readonly cellDeps: ccc.CellDep[], - public readonly name: string, - public readonly symbol: string, - public readonly decimals: number, - ) {} - - /** - * Determines whether a cell contains this UDT. - * - * @param cell - The cell to inspect. - * @returns `true` if the cell's type script equals `this.script` and - * its data length indicates a UDT; otherwise `false`. - */ - isUdt(cell: ccc.Cell): boolean { - return ( - Boolean(cell.cellOutput.type?.eq(this.script)) && - cell.outputData.length >= 34 - ); - } - - /** - * Asynchronously retrieves the UDT input balance (token amount and capacity) - * for a given transaction. - * - * @param client - The CKB client to query cell data. - * @param txLike - The transaction whose inputs are to be balanced. - * @returns A promise resolving to a tuple: - * - [0]: Total UDT amount in inputs (as `ccc.FixedPoint`). - * - [1]: Total capacity in UDT inputs (as `ccc.FixedPoint`). - */ - getInputsUdtBalance( - client: ccc.Client, - txLike: ccc.TransactionLike, - ): Promise<[ccc.FixedPoint, ccc.FixedPoint]> { - const tx = ccc.Transaction.from(txLike); - return ccc.reduceAsync( - tx.inputs, - async (acc, input) => { - // Get all cell info - await input.completeExtraInfos(client); - const { previousOutput: outPoint, cellOutput, outputData } = input; - - // Input is not well defined - if (!cellOutput || !outputData) { - throw new Error("Unable to complete input"); - } - - // Input is not an UDT - const cell = new ccc.Cell(outPoint, cellOutput, outputData); - if (!this.isUdt(cell)) { - return acc; - } - - // Input is an UDT - const [udtValue, capacity] = acc; - return [ - // eslint-disable-next-line @typescript-eslint/no-deprecated - udtValue + ccc.udtBalanceFrom(outputData), - capacity + cell.cellOutput.capacity, - ]; - }, - [0n, 0n], - ); - } - - /** - * Retrieves the UDT output balance (token amount and capacity) - * for a given transaction. - * - * @param txLike - The transaction whose outputs are to be balanced. - * @returns A tuple: - * - [0]: Total UDT amount in outputs (as `ccc.FixedPoint`). - * - [1]: Total capacity in UDT outputs (as `ccc.FixedPoint`). - */ - getOutputsUdtBalance( - txLike: ccc.TransactionLike, - ): [ccc.FixedPoint, ccc.FixedPoint] { - const tx = ccc.Transaction.from(txLike); - return tx.outputs.reduce( - (acc, output, i) => { - if (!output.type?.eq(this.script)) { - return acc; - } - - // Input is an UDT - const [udtValue, capacity] = acc; - return [ - // eslint-disable-next-line @typescript-eslint/no-deprecated - udtValue + ccc.udtBalanceFrom(tx.outputsData[i] ?? "0x"), - capacity + output.capacity, - ]; - }, - [0n, 0n], - ); - } - - /** - * Completes a transaction by adding UDT inputs and/or UDT change outputs as needed. - * - * @param signer - Signer providing client access and account scripts. - * @param txLike - The transaction to adjust. - * @param options.shouldAddInputs - Whether to add inputs if insufficient. Defaults to `true`. - * @param options.compressState - Whether to collect all UDT cells to compress state rent. Defaults to `false`. - * @returns A promise resolving to a tuple: - * - [0]: The (possibly mutated) transaction. - * - [1]: Number of UDT inputs added. - * - [2]: `true` if a UDT change output was appended; otherwise `false`. - */ - async completeUdt( - signer: ccc.Signer, - txLike: ccc.TransactionLike, - options?: { - shouldAddInputs?: boolean; - compressState?: boolean; - }, - ): Promise<[ccc.Transaction, number, boolean]> { - const tx = ccc.Transaction.from(txLike); - const client = signer.client; - let [inUdt, inCapacity] = await this.getInputsUdtBalance(client, tx); - const [outUdt, outCapacity] = this.getOutputsUdtBalance(tx); - let inAdded = 0; - if ( - (inUdt < outUdt || inCapacity < outCapacity) && - (options?.shouldAddInputs ?? true) - ) { - const compressState = options?.compressState ?? false; - const locks = (await signer.getAddressObjs()).map((a) => a.script); - const udts = []; - for await (const cell of this.findUdts(client, locks)) { - udts.push(cell); - inUdt += cell.udtValue; - inCapacity += cell.ckbValue; - if (!compressState && inUdt >= outUdt && inCapacity >= outCapacity) { - break; - } - } - this.addUdts(tx, udts); - inAdded = udts.length; - } - - if (inUdt < outUdt) { - throw new ErrorTransactionInsufficientCoin( - outUdt - inUdt, - this.script, - this.symbol, - this.decimals, - ); - } - - if (inUdt === outUdt) { - return [tx, inAdded, false]; - } - - tx.addOutput( - { - lock: (await signer.getRecommendedAddressObj()).script, - type: this.script, - }, - mol.Uint128LE.encode(inUdt - outUdt), - ); - - return [tx, inAdded, true]; - } - - /** - * Adds UDT cells to a transaction. - * @param txLike - The transaction to which UDT cells will be added. - * @param udts - An array of UDT cells to add. - * @returns The transaction with UDT cells added. - */ - addUdts(txLike: ccc.TransactionLike, udts: UdtCell[]): ccc.Transaction { - const tx = ccc.Transaction.from(txLike); - if (udts.length === 0) { - return tx; - } - - tx.addCellDeps(this.cellDeps); - - for (const { cell } of udts) { - tx.addInput(cell); - } - return tx; - } - - /** - * Async generator that finds and yields UDT (User‐Defined Token) cells matching the given lock scripts. - * - * @param client - * A CKB client instance providing: - * - `findCells(query, order, limit)` for cached searches - * - `findCellsOnChain(query, order, limit)` for on-chain searches - * - * @param locks - * An array of lock scripts. Only cells whose `cellOutput.lock` matches one of these - * scripts exactly will be considered. - * - * @param options - * Optional parameters to control the search behavior: - * - `onChain?: boolean` - * If `true`, queries the chain directly via `findCellsOnChain`. - * Otherwise, uses local cache via `findCells`. Default: `false`. - * - `limit?: number` - * Maximum number of cells to fetch per lock script in each batch. - * Defaults to `defaultFindCellsLimit` (400). - * - * @yields - * {@link UdtCell} objects for each valid UDT cell found. - * - * @remarks - * - Deduplicates `locks` via `unique(locks)` to avoid redundant queries. - * - Applies an RPC filter: - * • `script: this.script` (the UDT type script) - * - Skips any cell that: - * 1. Does not pass `this.isUdt(cell)` - * 2. Whose lock script does not exactly match the queried `lock` - * - Each yielded `UdtCell` contains: - * • `cell`: original cell data with status - * • `ckbValue`: capacity in shannons - * • `udtValue`: token amount parsed via `ccc.udtBalanceFrom(cell.outputData)` - * • a hidden `[isUdtSymbol]: true` marker - */ - async *findUdts( - client: ccc.Client, - locks: ccc.Script[], - options?: { - /** - * If true, fetch cells directly from the chain RPC. Otherwise, use cached results. - * @default false - */ - onChain?: boolean; - /** - * Batch size per lock script. Defaults to {@link defaultFindCellsLimit}. - */ - limit?: number; - }, - ): AsyncGenerator { - const limit = options?.limit ?? defaultFindCellsLimit; - for (const lock of unique(locks)) { - const findCellsArgs = [ - { - script: lock, - scriptType: "lock", - filter: { - script: this.script, - }, - scriptSearchMode: "exact", - withData: true, - }, - "asc", - limit, - ] as const; - - for await (const cell of options?.onChain - ? client.findCellsOnChain(...findCellsArgs) - : client.findCells(...findCellsArgs)) { - if (!this.isUdt(cell) || !cell.cellOutput.lock.eq(lock)) { - continue; - } - - yield { - cell, - ckbValue: cell.cellOutput.capacity, - // eslint-disable-next-line @typescript-eslint/no-deprecated - udtValue: ccc.udtBalanceFrom(cell.outputData), - [isUdtSymbol]: true, - }; - } - } - } -} - -/** - * Interface representing a UdtCell Cell. - */ -export interface UdtCell extends ValueComponents { - /** - * The underlying cell associated with the UDT Cell. - */ - cell: ccc.Cell; - - /** - * A symbol property indicating that this cell is a UDT Cell. - * This property is always set to true. - */ - [isUdtSymbol]: true; -} - -// Symbol to represent the isUdt property of UDT Cells -const isUdtSymbol = Symbol("isUdt"); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ba306a..384969f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,7 +18,7 @@ importers: devDependencies: '@anthropic-ai/claude-code': specifier: latest - version: 2.1.53 + version: 2.1.58 '@changesets/changelog-github': specifier: ^0.5.2 version: 0.5.2 @@ -30,7 +30,7 @@ importers: version: 9.39.3 '@typescript/native-preview': specifier: latest - version: 7.0.0-dev.20260224.1 + version: 7.0.0-dev.20260225.1 '@vitest/coverage-v8': specifier: 3.2.4 version: 3.2.4(vitest@3.2.4(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2)) @@ -539,7 +539,7 @@ importers: version: 4.3.0(prettier@3.8.1)(typescript@5.9.3) tsdown: specifier: 0.19.0-beta.3 - version: 0.19.0-beta.3(@typescript/native-preview@7.0.0-dev.20260224.1)(synckit@0.11.12)(typescript@5.9.3) + version: 0.19.0-beta.3(@typescript/native-preview@7.0.0-dev.20260225.1)(synckit@0.11.12)(typescript@5.9.3) typescript: specifier: ^5.9.2 version: 5.9.3 @@ -972,7 +972,7 @@ importers: version: 4.3.0(prettier@3.8.1)(typescript@5.9.3) tsdown: specifier: 0.19.0-beta.3 - version: 0.19.0-beta.3(@typescript/native-preview@7.0.0-dev.20260224.1)(synckit@0.11.12)(typescript@5.9.3) + version: 0.19.0-beta.3(@typescript/native-preview@7.0.0-dev.20260225.1)(synckit@0.11.12)(typescript@5.9.3) typescript: specifier: ^5.9.2 version: 5.9.3 @@ -1145,6 +1145,9 @@ importers: '@ckb-ccc/core': specifier: workspace:* version: link:../../forks/ccc/packages/core + '@ckb-ccc/udt': + specifier: workspace:* + version: link:../../forks/ccc/packages/udt '@ickb/dao': specifier: workspace:* version: link:../dao @@ -1203,8 +1206,8 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@anthropic-ai/claude-code@2.1.53': - resolution: {integrity: sha512-iKzS7+ktmxKoGXMyrahsMHpwwI36mIv674exgN7MdUySOFmo0MY4ahVxTMuZQkSXJVgKCcU2OpeSeH8Rz6WDzw==} + '@anthropic-ai/claude-code@2.1.58': + resolution: {integrity: sha512-NWTMNg6jIVtzUKkYj3qAqHXsD1clgVtKgJGmf1p2IXc5jhP1jOlCezSRvztuDH5omabVfkcrFVIMkZDX+Oe+6g==} engines: {node: '>=18.0.0'} hasBin: true @@ -2265,17 +2268,17 @@ packages: cpu: [x64] os: [win32] - '@shikijs/engine-oniguruma@3.22.0': - resolution: {integrity: sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA==} + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} - '@shikijs/langs@3.22.0': - resolution: {integrity: sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA==} + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} - '@shikijs/themes@3.22.0': - resolution: {integrity: sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g==} + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} - '@shikijs/types@3.22.0': - resolution: {integrity: sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==} + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -2512,43 +2515,43 @@ packages: resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260224.1': - resolution: {integrity: sha512-9VHXRhB7sM5DFqdlKaeDww8vuklgfzhYCjBazLCEnuFvb4J+rJ1DodLykc2bL+6kE8k6sdhYi3x8ipfbjtO44g==} + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260225.1': + resolution: {integrity: sha512-3qSsqv7FmM4z09wEpEXdhmgMfiJF/OMOZa41AdgMsXTTRpX2/38hDg2KGhi3fc24M2T3MnLPLTqw6HyTOBaV1Q==} cpu: [arm64] os: [darwin] - '@typescript/native-preview-darwin-x64@7.0.0-dev.20260224.1': - resolution: {integrity: sha512-uCHipPRcIhHnvb7lAM29MQ1QT9pZ+uirqtH630aOMFm8VG3j8mkxVM9iGRLx829n38DMSDLjc3joCrQO3+sDcQ==} + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260225.1': + resolution: {integrity: sha512-F8ZCCX2UESHcbxvnkd1Dn5PTnOOgpGddFHYgn4usyWRMzNZLPP+YjyGALZe9zdR/D8L0uraND0Haok+TPq8xYg==} cpu: [x64] os: [darwin] - '@typescript/native-preview-linux-arm64@7.0.0-dev.20260224.1': - resolution: {integrity: sha512-yFEEq6hD2R70+lTogb211sPdCwz3H5hpYh0+YuKVMPsKo0oM8/jMvgjj2pyutmj/uCKLdbcJ9HP2vJ/13Szbcg==} + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260225.1': + resolution: {integrity: sha512-Up8Z/QNcwce5C4rWnbLNW5w7lRARdyKZcNbB1NMnaswaGOBdeDmdP0wbVsOgJMoDp6vnun+EkvrSft8hWLLhIg==} cpu: [arm64] os: [linux] - '@typescript/native-preview-linux-arm@7.0.0-dev.20260224.1': - resolution: {integrity: sha512-cEWSRQ8b+CXdMJvoG18IjNTvBo+qT22B5imqm6nAssMpyHHQb62PvZGnrA8mPRQNPzLpa5F956j8GwAjyP8hBQ==} + '@typescript/native-preview-linux-arm@7.0.0-dev.20260225.1': + resolution: {integrity: sha512-Iu5rnCmqwGIMUu//BXkl9VQaxAAsqVvFhU4mJoNexNkMxPqVcu9quqYAouY7tN/95WcKzUsPpyRfkThdbNFO/g==} cpu: [arm] os: [linux] - '@typescript/native-preview-linux-x64@7.0.0-dev.20260224.1': - resolution: {integrity: sha512-zGz5kVcCeBRheQwA4jVTAxtbLsBsTkp9AEvWK5AlyCs1rQCUQobBhtx37X4VEmxn4ekIDMxYgaZdlZb7/PGp8w==} + '@typescript/native-preview-linux-x64@7.0.0-dev.20260225.1': + resolution: {integrity: sha512-WWjIfHCWlcriempYYc/sPJ3HFt6znNZKp60nvDNih0+wmxNqEfT5Yzu5zAY0awIe7XLelFSY+bolkpzMYVWEIQ==} cpu: [x64] os: [linux] - '@typescript/native-preview-win32-arm64@7.0.0-dev.20260224.1': - resolution: {integrity: sha512-A0f9ZDQqKvGk/an59HuAJuzoI/wMyrgTd69oX9gFCx7+5E/ajSdgv0Eg1Fco+nyLfT/UVM0CV3ERyWrKzx277w==} + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260225.1': + resolution: {integrity: sha512-lmfQO+HdmPMk0dtPoNo8dZereTUYNQuapsAI7nFHCP8F25I8eGKKXY2nD1R8W1hp/LmVtske1pqKFNN6IOCt5g==} cpu: [arm64] os: [win32] - '@typescript/native-preview-win32-x64@7.0.0-dev.20260224.1': - resolution: {integrity: sha512-Se9JrcMdVLeDYMLn+CKEV3qy1yiildb5N23USGvnC9siNFalz8tVgd589dhRP+ywDhXnbIsZiFKDrZF/7B4wSQ==} + '@typescript/native-preview-win32-x64@7.0.0-dev.20260225.1': + resolution: {integrity: sha512-e4eJyzR9ne0XreqYgQNqfX7SNuaePxggnUtVrLERgBv25QKwdQl72GnSXDhdxZHzrb97YwumiXWMQQJj9h8NCg==} cpu: [x64] os: [win32] - '@typescript/native-preview@7.0.0-dev.20260224.1': - resolution: {integrity: sha512-PU0zBXLvz6RKxbIubT66RCnJXgScdDIhfmNMkvRhOnX/C4SZom5TFSn7BEHC3w8JPj7OSz5OYoubtV1Haty2GA==} + '@typescript/native-preview@7.0.0-dev.20260225.1': + resolution: {integrity: sha512-mUf1aON+eZLupLorX4214n4W6uWIz/lvNv81ErzjJylD/GyJPEJkvDLmgIK3bbvLpMwTRWdVJLhpLCah5Qe8iA==} hasBin: true '@vitejs/plugin-basic-ssl@1.2.0': @@ -3576,15 +3579,15 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - minimatch@10.2.2: - resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} - minimatch@3.1.3: - resolution: {integrity: sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.6: - resolution: {integrity: sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==} + minimatch@9.0.8: + resolution: {integrity: sha512-reYkDYtj/b19TeqbNZCV4q9t+Yxylf/rYBsLb42SXJatTv4/ylq5lEiAmhA/IToxO7NI2UzNMghHoHuaqDkAjw==} engines: {node: '>=16 || 14 >=14.17'} minipass@7.1.3: @@ -4385,7 +4388,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@anthropic-ai/claude-code@2.1.53': + '@anthropic-ai/claude-code@2.1.58': optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -5068,7 +5071,7 @@ snapshots: dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3 - minimatch: 3.1.3 + minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -5089,7 +5092,7 @@ snapshots: ignore: 5.3.2 import-fresh: 3.3.1 js-yaml: 4.1.1 - minimatch: 3.1.3 + minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color @@ -5105,10 +5108,10 @@ snapshots: '@gerrit0/mini-shiki@3.22.0': dependencies: - '@shikijs/engine-oniguruma': 3.22.0 - '@shikijs/langs': 3.22.0 - '@shikijs/themes': 3.22.0 - '@shikijs/types': 3.22.0 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 '@humanfs/core@0.19.1': {} @@ -5519,20 +5522,20 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true - '@shikijs/engine-oniguruma@3.22.0': + '@shikijs/engine-oniguruma@3.23.0': dependencies: - '@shikijs/types': 3.22.0 + '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.22.0': + '@shikijs/langs@3.23.0': dependencies: - '@shikijs/types': 3.22.0 + '@shikijs/types': 3.23.0 - '@shikijs/themes@3.22.0': + '@shikijs/themes@3.23.0': dependencies: - '@shikijs/types': 3.22.0 + '@shikijs/types': 3.23.0 - '@shikijs/types@3.22.0': + '@shikijs/types@3.23.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -5762,7 +5765,7 @@ snapshots: '@typescript-eslint/types': 8.56.1 '@typescript-eslint/visitor-keys': 8.56.1 debug: 4.4.3 - minimatch: 10.2.2 + minimatch: 10.2.4 semver: 7.7.4 tinyglobby: 0.2.15 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -5786,36 +5789,36 @@ snapshots: '@typescript-eslint/types': 8.56.1 eslint-visitor-keys: 5.0.1 - '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260224.1': + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260225.1': optional: true - '@typescript/native-preview-darwin-x64@7.0.0-dev.20260224.1': + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260225.1': optional: true - '@typescript/native-preview-linux-arm64@7.0.0-dev.20260224.1': + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260225.1': optional: true - '@typescript/native-preview-linux-arm@7.0.0-dev.20260224.1': + '@typescript/native-preview-linux-arm@7.0.0-dev.20260225.1': optional: true - '@typescript/native-preview-linux-x64@7.0.0-dev.20260224.1': + '@typescript/native-preview-linux-x64@7.0.0-dev.20260225.1': optional: true - '@typescript/native-preview-win32-arm64@7.0.0-dev.20260224.1': + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260225.1': optional: true - '@typescript/native-preview-win32-x64@7.0.0-dev.20260224.1': + '@typescript/native-preview-win32-x64@7.0.0-dev.20260225.1': optional: true - '@typescript/native-preview@7.0.0-dev.20260224.1': + '@typescript/native-preview@7.0.0-dev.20260225.1': optionalDependencies: - '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260224.1 - '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260224.1 - '@typescript/native-preview-linux-arm': 7.0.0-dev.20260224.1 - '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260224.1 - '@typescript/native-preview-linux-x64': 7.0.0-dev.20260224.1 - '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260224.1 - '@typescript/native-preview-win32-x64': 7.0.0-dev.20260224.1 + '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260225.1 + '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260225.1 + '@typescript/native-preview-linux-arm': 7.0.0-dev.20260225.1 + '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260225.1 + '@typescript/native-preview-linux-x64': 7.0.0-dev.20260225.1 + '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260225.1 + '@typescript/native-preview-win32-x64': 7.0.0-dev.20260225.1 '@vitejs/plugin-basic-ssl@1.2.0(vite@6.4.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2))': dependencies: @@ -6104,7 +6107,7 @@ snapshots: copyfiles@2.4.1: dependencies: glob: 7.2.3 - minimatch: 3.1.3 + minimatch: 3.1.5 mkdirp: 1.0.4 noms: 0.0.0 through2: 2.0.5 @@ -6328,7 +6331,7 @@ snapshots: is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 - minimatch: 3.1.3 + minimatch: 3.1.5 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: @@ -6502,14 +6505,14 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 3.4.3 - minimatch: 9.0.6 + minimatch: 9.0.8 minipass: 7.1.3 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 glob@13.0.6: dependencies: - minimatch: 10.2.2 + minimatch: 10.2.4 minipass: 7.1.3 path-scurry: 2.0.2 @@ -6518,7 +6521,7 @@ snapshots: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.1.3 + minimatch: 3.1.5 once: 1.4.0 path-is-absolute: 1.0.1 @@ -6838,15 +6841,15 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} - minimatch@10.2.2: + minimatch@10.2.4: dependencies: brace-expansion: 5.0.3 - minimatch@3.1.3: + minimatch@3.1.5: dependencies: brace-expansion: 1.1.12 - minimatch@9.0.6: + minimatch@9.0.8: dependencies: brace-expansion: 5.0.3 @@ -7047,7 +7050,7 @@ snapshots: glob: 13.0.6 package-json-from-dist: 1.0.1 - rolldown-plugin-dts@0.20.0(@typescript/native-preview@7.0.0-dev.20260224.1)(rolldown@1.0.0-beta.58)(typescript@5.9.3): + rolldown-plugin-dts@0.20.0(@typescript/native-preview@7.0.0-dev.20260225.1)(rolldown@1.0.0-beta.58)(typescript@5.9.3): dependencies: '@babel/generator': 7.29.1 '@babel/parser': 7.29.0 @@ -7059,7 +7062,7 @@ snapshots: obug: 2.1.1 rolldown: 1.0.0-beta.58 optionalDependencies: - '@typescript/native-preview': 7.0.0-dev.20260224.1 + '@typescript/native-preview': 7.0.0-dev.20260225.1 typescript: 5.9.3 transitivePeerDependencies: - oxc-resolver @@ -7230,7 +7233,7 @@ snapshots: dependencies: '@istanbuljs/schema': 0.1.3 glob: 10.5.0 - minimatch: 10.2.2 + minimatch: 10.2.4 through2@2.0.5: dependencies: @@ -7266,7 +7269,7 @@ snapshots: dependencies: typescript: 5.9.3 - tsdown@0.19.0-beta.3(@typescript/native-preview@7.0.0-dev.20260224.1)(synckit@0.11.12)(typescript@5.9.3): + tsdown@0.19.0-beta.3(@typescript/native-preview@7.0.0-dev.20260225.1)(synckit@0.11.12)(typescript@5.9.3): dependencies: ansis: 4.2.0 cac: 6.7.14 @@ -7277,7 +7280,7 @@ snapshots: obug: 2.1.1 picomatch: 4.0.3 rolldown: 1.0.0-beta.58 - rolldown-plugin-dts: 0.20.0(@typescript/native-preview@7.0.0-dev.20260224.1)(rolldown@1.0.0-beta.58)(typescript@5.9.3) + rolldown-plugin-dts: 0.20.0(@typescript/native-preview@7.0.0-dev.20260225.1)(rolldown@1.0.0-beta.58)(typescript@5.9.3) semver: 7.7.4 tinyexec: 1.0.2 tinyglobby: 0.2.15 @@ -7311,7 +7314,7 @@ snapshots: '@gerrit0/mini-shiki': 3.22.0 lunr: 2.3.9 markdown-it: 14.1.1 - minimatch: 9.0.6 + minimatch: 9.0.8 typescript: 5.9.3 yaml: 2.8.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d8a154b..f1b0c9a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,6 +13,7 @@ packages: catalog: "@ckb-ccc/core": ^1.12.2 + "@ckb-ccc/udt": ^1.12.2 "@types/node": ^24.8.1 minimumReleaseAge: 1440