Repository navigation
Conversation
CCC family 1.17->1.18.2 (covers the integration-audit pin's runtime code plus fixes #445/#448, #446, #451/#452, #453/#454; #444 remains open upstream, mitigated by the HTTP-only fallbacks:[] client rule). In-range refresh across tooling; react override follows the app to 19.2.8; brace-expansion override advanced for new advisories; tsgo pinned (closes C9 with the already-removed stale exclude block); gitignore log/ anchored to /log/. Typecheck clean, 1509 tests pass, audit 12 vulns -> 2 (elliptic ignored-by-policy + nanoid clearing the age gate).
…ct oracle The matcher converted ckb_min_match into UDT units with inverted scales (ceil(m*udtScale/ckbScale) instead of ceil(m*ckbScale/udtScale)), so at asymmetric ratios it emitted partials the deployed limit_order script rejects with InsufficientMatch (over-restrictive in the mainnet regime, under-inclusive in the general one). Adds the entry.rs:116 plain-CKB-delta post-guard as the authoritative check, a freestanding contract oracle in testkit ported line-by-line from the deployed Rust (validate(), deposit_to_ickb; 35 self-tests), oracle adjudication sweeps across four ratio regimes in both directions (11 tests; 4 fail against the unfixed matcher), and pinned contract fixtures: the four iCKB release ELFs (contracts @ ae8a11f) plus the dao binary extracted from mainnet/testnet genesis (byte-identical, provenance in fixtures README).
60 rows generated by the contracts repo's vector-gen crate (byte-exact copies of deposit_to_ickb and validate with build-time drift assertions, commit ae8a11f): 24 deposit-conversion rows incl. exact soft-cap boundaries at three ARs, 36 limit-order rows across both directions, dual-ratio orders, and check-ordering pins. The TS oracle agrees on all 60 (independent double-derivation). Adds node types to testkit for the fixture loader.
Chart: live-tip marker dot, 2px non-scaling mark-spec stroke, tighter mobile y-axis gutter (plot +15% at 390w). Form: brand casing iCKB (was uppercased to ICKB by the grid's blanket uppercase), sentence-case validation errors. Theme: color-scheme dark for UA scrollbars/controls. Explainer: mobile heading scale. Removes an inert text-s utility. Verified via preview snapshots at 1280w/390w, landing and connected (testnet harness) states; a11y inventory and 167 interface tests intact.
250-run seeded properties (VITEST_FC_SEED replayable) across both scale regimes: every emitted partial contract-valid via the oracle, allowance respect, monotone consumption, full-fill shape, empty-below-minimum; min-match boundary band probed explicitly. Core: ickbValue equals the oracle's depositToIckb across both cap branches; convert round-trip loss bounded exactly. Shared oracle helpers extracted for both suites. fast-check ^4.9.0 as root devDependency. 357 tests green, no counterexamples.
Resolves the Phase-2 blocked/unblocked contradiction, re-gates debugger execution to the Phase-2 exit, stages the systemd cutover with a joint rollback pair, derives the journal consumer inventory from the repo, reorders gate/artifact dependencies, slices Phase 3, renames the exported-allowed subtree to shared/ and adds B4 bases to the export contract, records the B4 recipe durably with its re-probe obligation, amends the withdrawal-selection product contract explicitly, and pins the debugger release asset.
FakeClient extends the abstract ccc.Client: every member either consults the in-memory ChainState (cells, headers, transactions, tip, fee stats, send scripting) or throws FakeClientError synchronously — no network path exists, unlike StubClient's live-client fallthrough. Overrides map retained as the error-injection escape hatch; ClientCacheMemory keeps caching paths real. StubClient stays untouched pending call-site migration. One WIP fixture corrected for CCC 1.18's capacity raising (#459): sub-occupancy outputs are illegal on-chain and now normalize at construction.
…valence Fee-drawing properties (0..10^7 + pinned constant) show contract validity of emitted partials is fee-invariant, and a reachability suite proves the entry.rs:116 post-guard is exactly equivalent to the bMinMatch pre-gate for from()-constructed matchers (floor(u*b/a) >= m iff u >= ceil(m*a/b); fee never enters either side): every sub-gate would-be partial adjudicates InsufficientMatch, every admitted partial clears the plain-CKB minimum. Guard retained as defense-in-depth with its comment corrected from 'authoritative' to the proven equivalence.
Per review 003 residuals: section-5 title and open-items reflect the delivered mapping; Phase-2 line carries the debugger exit gate instead of the stale blocker; the sequencing pin replaces the four-consumer coordinated change with the staged cutover over the grep-derived owner inventory (now including tests/support and the supervisor README); shared/ replaces internal/ with the B4 symbols in the root contract and the dropped depcruise rule removed from the body.
No-subpaths confirmed safe (merged barrel adds zero retention); adopts the entity-module-layout rule and the PURE-in-static-block ban; sets the 2KB packed-probe budget for the 3a gate; queues two CCC upstream reports (nested-manifest sideEffects omission, namespace monolith).
…oor gate Replaces the evicted ephemeral B4 evidence (F-006) with a re-runnable probe: tsgo emit + api-extractor over the amendment-7 two-module shape, plus negative controls for ae-forgotten-export and for silent demotion of that gate. The root-export-floor fixture typechecks all 31 contract names against current barrels (type-aware: catches type-only impostors of value names): 4 known-missing flagged for Phase-2 barrel work, 10 pending Phase-2 names tracked, and the contract adopts the existing DAO_OUTPUT_LIMIT name. Re-points at the packed tarball as the 3a exit gate. pnpm probes wires both.
typescript-eslint 8.65 flags vi.fn() spy references typed through ccc.Signer as unbound methods (no this is captured); sonarjs 4.2 does not track node:assert deepEqual through the smoke tests' loops. Five reasoned per-line disables; behavior unchanged, both suites green.
ICKB-014 narrowed to post-deadline presentation in both body sites; Phase-4 body points at the staged cutover amendments instead of the 'coordinated change' phrasing.
F-013: coverage gate green again — the entry.rs:116 mirror is exercised via deliberate direct-constructor desync (the case it defends), and the two FakeClient override/unscripted branches are covered. F-014: root-export floor de-vacuated (DAO_OUTPUT_LIMIT owned by dao and verified) and made nominally complete: result unions and per-entity XBase consts tracked as named pending entries (38 names); provisional status recorded as amendment 14. F-015: committed treeshake reproducer (rollup+esbuild pinned devDeps, pnpm probes): source-scan ban, runtime-breakage reproduction — refined to the truth: rollup DEFAULTS keep the annotated call; the silent class-kept/static-emptied breakage reproduces under moduleSideEffects:false, a consumer config outside our control (amendment 13) — and the 2KiB clean-layout budget. F-016: body reconciled (entity check resolved, Phase-5 line, ICKB-025 supersession wording). F-017/018/019 recorded as amendments 15-17: durable signed-bytes owner keyed by chain/account/hash/inputs; full dependency-identity congruence as fail-closed signing prerequisite; per-consumer confirmation-depth table proposed for maintainer ratification.
…scribe Keeps the dispatch describe under the 80-line function cap.
…ema (review 007 F-005 note)
…05, amendment 18)
F-015: brace-aware static-block scanner with a nested-brace negative control (the naive first-closing-brace regex provably misses deep annotations); amendment-12 barrel claim narrowed to its exploratory status with the packed 3a gate as the enforced form; probes wired into the required gate (pnpm lint -> lint:probes). F-016: resolved open-item removed; the treeshake gate names its real command instead of the nonexistent check:deep. F-017: the inviolable keep-list itself now requires the exact signed bytes (amendment-15 lifecycle), not hash-only persistence.
…-019)
Supersedes the per-consumer depth table: depth 0 everywhere; automated
entities restart from clean state on revert (client reconstruction
replaces the ICKB-020 scoped cache rollback for stack consumers); the
interface communicates state transitions honestly and recovers via the
amendment-15 signed bytes; wait({depth}) stays published for off-chain-
dependent integrators; ICKB-008 recheck becomes the reorg monitor.
Merges the settling-copy residual into the ICKB-014 interface-
communication task.
…cation Recovers e39f713 (cherry-picked: the chaining + clean-state-restart ratification had been orphaned by a silently-failed push plus a shared-checkout reset — push verification hardened hereafter). F-005: amendment 11 now states the distinct-UID/LoadCredential rule it was cited for; amendment 18's mainnet-rejection claim corrected to harness protection (separate never-funded mainnet keys are the asset boundary). F-014: IckbErrorCode ratified and added to the floor manifest (39 names). F-015: the static-block scanner is TypeScript-AST based with nested-brace AND lexical (string-brace) negative controls. F-016: section-1 floor marked provisional-until-3a; section-2 congruence upgraded to the amendment-16 full identity binding. F-017: appendix ICKB-007 constraint upgraded to exact signed bytes. F-019: amendment 15 gains the chained-ancestor retention lifecycle (parent bytes durable until all descendants terminal). F-021: amendment 19 — every mandatory gate lands with its command and CI membership in the slice that introduces it.
…l deposit headers go first Decisions amendment 52(an) (user, 2026-09-20), from Grok 4.6's end-game review (`grok46-endgame-2026-09-20` over `7a08df98`: no confirmed defects), each suggestion verified against the code. The order scan's cache-first origin read trusted a cached response without a block, so the app's long-lived client kept a lagging node's "pending" answer for the session: the order never counted as old and the estimate read it as uncommitted. As CCC's own readers do, the cache is trusted only once the entry carries a block number; no new traffic in the steady state. Only phase 2 names a header by index, read as one byte by the deployed DAO script, so `withdraw` moves its distinct deposit headers to the front of `headerDeps`: the limit counts those alone, the projection's ready batch is the first 256 matured withdrawals whatever the receipts and requests, and the request-ahead-of-withdrawals residual is gone. Nothing else in the stack reads a header by position, each built transaction passes the step once and last, and every base transaction starts empty; `buildBaseTransaction` says so for integrators. Two record corrections: the estimate doc's ten-minute lock-up floor is the bot's twenty since 52(al)(5); 52(al) deleted an inline copy of `pushHeaderDep`, not the function. Order expiration is settled as 52(am) after a Fable 5 consult. `CI=true pnpm check` exit 0 on this tree.
… guards its cursor; one fit loop, one status parser Decisions amendment 52(an), second round (user, 2026-09-20; fresh Opus 5 and Grok 4.6 audits over `f113ef91`, no confirmed defects, every suggestion verified). `daoClaimEpoch` existed because CCC rolled a cycle on equal fractions; the pinned 1.20.0 `calcDaoClaimEpoch` is the same strict comparison, so the copy is gone and the dao.c model suite pins the installed CCC on every run. A non-empty page always moves the indexer's cursor, so `findCells` fails with a named error when a full page brings it back unchanged, instead of looping for ever on a broken node in the app's public pool. One withdrawal fit loop (`fitWithdrawalDeposits`, the caller sorts) replaces the bot's copy; one `rawTransactionStatus` serves the wait and the header read; the order scan's second cache write, `RING_EPOCHS` and the generator's second manager set are gone; the phase-2 `since` carries the packed epoch number rather than a hex that matched by codec coincidence. Stale comments and one README sentence corrected. `CI=true pnpm check` exit 0 on this tree.
Clean-up settled one item at a time (user, 2026-09-20; recorded in decisions amendment 52(an)). Deleted: the closed findings file, the rewrite overview (the root README points at the decision record), the CCC audit companion (its rows built, superseded or contradicted; its six standing CCC and node facts went to the maintainer's cross-project notes for re-verification), PRODUCT.md, the SDK's .npmignore, the two package-level .gitignore files, the private packages' licence sections, ten uncalled package scripts and four root ones, the sampler's tee into a file, a dead eslint override for a folder that no longer exists, seven stale ignore lines. Three small source files are folded into their neighbours: IckbError and the fundable walk into sdk.ts, signerAccountLocks into the sender. Every folder that is not source or tests has a README (scripts, patches, the two docs folders, the vectors' provenance note renamed); the interface's public folder is described in its README since Vite would publish one there. The runtime-config test makes its temporary directory under the OS temp dir; the manual-run examples name the key directory the units use. `CI=true pnpm check` exit 0 on this tree.
The three-day window of 52(v) held only while the SDK was unpublished; seven days is the standing policy before the fork (user, 2026-09-14 and 2026-09-20). A frozen install verifies every lockfile entry against the policy and passes: nothing installed is younger than a week. `CI=true pnpm check` exit 0 on this tree.
The pre-rewrite stack left @ickb/sdk on npm at 1000.0.82, so the 0.1.0 this branch declared could not publish. 9000.0.0 sorts above everything published, so the first release takes the latest tag with no dist-tag surgery, and one number across the repo drops the 1001.0.0 placeholder that marked a package unpublished. Recorded as decisions amendment 52(ao); it supersedes 52(ad)'s first-version clause.
Read the changelogs at ckb-devrel/ccc 3e9087267bd8. Nothing in 1.20.1, 1.21.0 or 1.22.0 fixes a defect on a path we use: the one correctness fix in the range is 1.20.1's UDT input selection, and the stack never calls it, funding completion from the cells the state read returned and using CCC only for completeFeeChangeToLock. Recorded as decisions amendment 52(ap), which closes the bump slice 52(v) left open. The minimumReleaseAge comment still described the three-day window that was restored to seven days before it; it now says what the value is.
Replace the chronological rewrite journal with rationale in the owning package docs and local comments. Add the contributor agreement and its CLAUDE.md symlink. Keep current rules beside their consumers rather than requiring parallel README, register, and comment accounts. Preserve the deployed-contract and CCC constraints, and correct stale descriptions of DAO limits, confirmation, collection, and actor behavior. Document mining-reward maturity as an accepted limitation and whole-transaction sizing as an open design issue. Executable behavior remains unchanged. Consolidate the documentation work and follow-up limitation notes into one concern. CI=true pnpm check passed: 807 Vitest tests, four native tests, required coverage, lint, API, publish, and interface build checks.
The unit setup and update procedure told operators to run `pnpm node:install`, but no such script exists, so pnpm fails with `Command "node:install" not found`. The update procedure stops the services before that step, leaving them down. Use the root README's install command instead.
The liquid-cell sweep checked a 64 KiB prepared-size budget by serializing the whole transaction before every added input, so its cost grew quadratically: an offline probe took about 13 s near 1,500 inputs with no RPC. The 64 KiB was a round compaction target, not a validity bound, and a same-lock input has a fixed size (44 bytes, 52 with an extra witness slot), so a count says the same thing without serializing. The iCKB and plain sweeps now share one limit of 1,000 total inputs, about 50 KB and the one size measured, far under the node's 512,000-byte limit. Required funding and the fee may still pass it. Rejected: a hand-maintained per-input byte counter (duplicates CCC's layout) and 2,000 inputs (script cycles and wallet limits unmeasured). Whole-transaction sizing stays a separate, documented open issue. The tests now prove the sweep stops at the limit, that required iCKB funding and the fee still pass it, and that a move reports a cut-short sweep.
The property asserted `plan.deposit !== undefined || pendingCkb > 0n`, with pendingCkb drawn from [0, 3 deposits) and never passed to planRebalance. It was nonzero in every checked sample, so the assertion held whatever the planner did: disabling deposits in planRebalance left it green. The planner never sees pending withdrawals, which only add CKB on their way back, so the property is now checked with nothing pending: a funded account below the refill line must plan a low_ickb deposit. With deposits disabled the test fails.
Several tests passed or failed for reasons unrelated to their titles. They pinned constants to their own literals, a component to its function name, a fee threshold to its formula and a style to its CSS value. One exercised only the test's own stub client, and one duplicated other coverage. Harmless retuning broke the pins while the documented relations stayed unchecked. The policy test now asserts the rule in sdk/node/docs/policy.md: twice the default minimum match, converted at the genesis rate (the worst case), stays below the refill line. The entrypoint test checks the mounted component by identity. The rest are deleted: their behavior is covered elsewhere, and changing the fee factor fails six maturity-preview tests. Each rewritten test fails when its production line is broken. Reviewed independently; no production code changes.
convert already rejects a ratio that is not populated, so both scales are positive and the divisor is at least 1. A zero amount then yields 0 / b or (b - 1) / b, both 0, in either direction and rounding mode. The early return changed nothing, and its test, titled "covers zero conversion", could not fail without it. The zero test now asserts the formula in all four direction and rounding combinations. The unrelated compare assertion keeps its own test, since it is the one case that exercises cross-multiplication.
The fixture README cited contracts commit ae8a11fa, which has no scripts/vector-gen, and gave absolute paths into one machine's checkout, so the fixture could not be regenerated from the cited revision. Pin c951927e, where the generator is tracked, link its README for the mechanism and the regeneration steps instead of restating them, and record that regenerating there reproduces the committed fixture byte for byte.
The authorization-boundary link pointed at the report's old file name at a fixed contracts commit. The report was renamed to ICKB-Audit-Report.md and keeps evolving on contracts master, so link the current section there.
The ring split the DAO cycle into nextPowerOfTwo(deposit count) segments. A deposit created without iCKB Logic running carries no iCKB, so zero-value deposits could multiply the segments for the cost of their occupied capacity alone, and the bot's ring_coverage rule then deposited on nearly every tip (2,700 of 2,880 tips in a reproduction, against 180). The count now follows pool iCKB, one segment per two deposit caps. Two caps rather than one: a covered ring holds about one cap per segment, which with one cap per segment sits exactly at the next power of two, so the bot's own fills after a doubling would cross it and double the ring again. With two caps of room, fills cause at most one doubling. The bot's seeding check now tests pool value too, so a pool holding only zero-value deposits still seeds. Rationale and rejected alternatives are in sdk/node/docs/policy.md.
The segment count is one loop: double while the segments hold fewer than two caps each. Two helpers for ceiling division and next power of two said the same thing in more code. The policy document keeps the rule, the two-caps reason and the rejected alternatives in one sentence each; the runaway story lives in b202638.
A withdrawal transaction carries at most DAO_OUTPUT_LIMIT / 2 requests, a request and its owner marker each. The user planner applied that cap after fitWithdrawalDeposits; the bot did not, so its completion walk built and discarded every oversized prefix first. A deposit made without iCKB Logic running carries no iCKB and always fits the amount, so a flood of them made that walk long on every turn. fitWithdrawalDeposits now stops at the cap itself, for both callers. No prefix beyond it could ever build, so outcomes do not change. Zero-value deposits are otherwise taken as they come: each returns its occupied capacity with interest and burns no iCKB, a positive payout for the user and the bot alike, so neither filters them.
A deposit made without iCKB Logic running carries no iCKB and always fits a withdrawal chain, so a flood of them with early claims can take every request slot ahead of valued deposits. Withdrawing one returns its 82 CKB occupied capacity, the same a valued deposit returns, so the withdrawer's only cost is the order fee on the displaced iCKB, under an eighth of the 82 CKB per slot the flood costs its sender, paid to the bot. Sorting valued deposits first was rejected: one shannon of free capacity makes a deposit count as valued at the same cost, the threshold objection already recorded for segment counting.
…sories pnpm audit reported 23 advisories on the unchanged lockfile. Twenty-one have patched versions inside the ranges their dependents declare and older than the seven-day release age, so they get security pins in the existing overrides block: axios 1.20.0 (twelve advisories via @ckb-ccc/spore, not imported by the interface), shell-quote 1.11.0 (React Native dev tooling under @ckb-ccc/connector), and the build tooling brace-expansion 5.0.12, fast-uri 3.1.8, source-map-js 1.2.2 and smol-toml 1.9.0. The two with no patch are ignored with their reason: braces under the same React Native chain as the existing image-size ignores, sprintf-js under @microsoft/api-extractor's argparse. Gate exit 0, 808 tests.
pnpm update across the workspace, CCC excluded: the README keeps the tested CCC 1.20.0 set until a relevant upstream fix is evaluated on its own, and the first run moved the catalog to core 1.23.0, so the update was redone with the @ckb-ccc scope excluded and the catalog restored. eslint-plugin-regexp's exact pin moved 3.1.0 to 3.3.1; the minor is older than the release age. @types/node follows its tilde range to 22.20.4. A stale registry metadata cache made typescript-eslint 8.71.0 unresolvable until the @typescript-eslint cache entries were deleted. Gate exit 0, 808 tests.
Every CCC release in the range is older than the seven-day release age. CCC 1.23 sizes mol.union dynamically and rejects it as a struct field, so OrderData failed to load: MasterCodec now uses mol.fixedUnion, whose Relative and OutPoint variants are both 36 bytes, the same wire format. The transport mock assertion in the waitTransaction test matches the payload alone, since the client now passes optional request options as a second argument. The README paragraph keeping 1.20.0 is replaced; the connector-react move from 2.0 to 2.2 is not covered by the gate and awaits live wallet testing. Gate exit 0, 808 tests.
Both vitest packages move together, as the coverage provider follows the runner's major. The vitest@<4.1.0 security override no longer matches anything and is removed. Gate exit 0, 808 tests, coverage 100%. TypeScript 7.0.2 was tried and reverted: the package exports only its version, without the compiler API that typescript-eslint, api-extractor and scripts/tooling/dead-members.ts use, and both tools declare typescript <6.1.0. The README records this beside the dependency policy.
The unopinionated preset gained prefer-ternary, prefer-minimal-ternary, prefer-simplified-conditions and prefer-logical-operator-over-ternary, which flagged 37 if/else returns and ternary placements. Ternary shape is a per-site readability call, and the rewrite the minimal-ternary rule wants in sdk/src/order/master.ts would lose the discriminated union narrowing, so the four are turned off beside the existing exceptions. Gate exit 0, 808 tests.
A 0.x minor, so outside the caret range and moved by hand. The @vitejs/plugin-react peer range ^0.145.0 was already unmet at 0.147; the interface builds and its tests pass on 0.152. Gate exit 0.
The dynamic-size union change landed in CCC 1.23.0, not 1.21, as the installed changelog records under #573; the master codec comment, the README and the CCC commit message said 1.21. api-extractor bundles its own TypeScript and declares no peer range, so the TypeScript 7 note names typescript-eslint alone. Unicorn 76 added one rule to the preset and broadened three that were already in it. CCC 1.22 gave transports a per-request abort signal, so the waitTransaction comments now say the in-flight client operation receives none, which is the fact that keeps late settlements handled. Found by the Fable 5.1 and Opus 5.5 audits of the dependency commits; no behavior change, gate exit 0.
prefer-minimal-ternary, prefer-simplified-conditions and prefer-logical-operator-over-ternary are back on. Nine test fixtures move the ternary inside the array literal, the self-exclusion in maturity.ts reads as a disjunction, masterFrom uses a guard clause so the discriminated union keeps narrowing, and the vite host becomes a guarded assignment, since the two ternary shapes for it each trip one of the rules. prefer-ternary stays off: unicorn 76 extended it to a guard clause before the final return, which turns a chain of guard clauses into an if followed by a ternary. Gate exit 0, 808 tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This replaces the whole TypeScript stack: one published SDK, three single-turn actors, one app, and a test kit.
The previous stack grew faster than it settled: thirteen workspaces, a supervisor and a launcher around the bot, five published packages, and transaction logic spread so thin that following one conversion from the click to the signed bytes meant reading most of the repository. Reading it that way turned up a real bug: the order matcher inverted a conversion. It is fixed here and pinned by an oracle ported from the contracts, and the rest of that code is gone.
Users get an app that converts CKB to iCKB and back, shows them one date for everything they have converting, and collects what is ready with the next transaction they sign. Integrators get
@ickb/sdk: one entry point, one dependency, plain sampled state and a transaction they can complete, sign and send. Operators get three processes that run one turn and exit. Every choice below, with the rejected alternatives and the accepted tradeoffs, is written down indocs/, which is the authority; this description is the map.Shape
Thirteen workspaces become four:
sdk(the published library),sdk/node(the bot, the testnet stimulus generator, the mainnet rate sampler),testkit(private test helpers),interface(the app). Gone:packages/core,dao,order,utils,node-utils,testkit, andapps/bot,tester,sampler,supervisor,interface.@ickb/sdkis the only published package: one entry point, 25 exports,@ckb-ccc/coreas its single dependency, browser safe. One file per on-chain script (udt.ts,logic.ts,owned_owner.ts,dao.ts,order/), the conversion workflow inconversion/, the send path insend/, and no barrels except the index.What the SDK does
getL1AccountStatetakes one tip and reads the book, the pool and every account lock with uncached exact-lock scans, sorting the cells here. It is complete and uncapped: a big book makes the read slower, never partial.ae8a11f, plus golden vectors generated by their Rust crate. If either port drifts, the suite fails.dao.c; the 64-output limit; the one-byte header index, with withdrawal deposit headers placed first so only they count against it.The maturity estimates explain how an order gets its date.
What the actors do
One turn is one process: read state, decide, send at most one transaction, exit
0on a skip or a commit and1on any failure. The supervisor, the launcher, the tester and the generated config files are deleted. Restarting is systemd's job, the unit is the config, and the signing key stays in a0600file the unit names. It never reaches an environment value, an event or a log. The bot policy says what a turn does, in order.What the app does
One preview per settled amount, built from exact wallet state and completed as a real transaction, then refreshed and rebuilt once more right before the wallet pops up. The balance row says what is in wallet, what is converting and what is collectable, and one "Ready:" line dates everything in flight. Every transaction the user signs also collects their converted funds, including orders the market will never fill and orders sitting for thirty days. The address in the header is also the destination: point it at another wallet and it sends the CKB and iCKB there, with no amount to type. That is the way out for a key-only wallet that has no xUDT transfer of its own.
Decisions
These are the ones that shape how the code reads. The rest, with the rejected alternatives, are in
docs/.For the user:
For the integrator:
@ckb-ccc/coreis the only thing it pulls in.9000.0.0. The old stack left@ickb/sdkon npm at1000.0.82, so a sane number could not publish. This one sorts above everything and takes the tag without a fight.For the operator:
no_gainmeans the book offers nothing worth its fees at any size;unfunded_gainmeans it does but the balances cannot pay. A watcher can tell a dull market from an empty wallet.For the repository:
pnpm checkis the audit, the full lint and the interface build. Coverage is 100% on both source trees, and a dead-member lint sits beside knip because knip sees exports, not members.