From e1f95754343027c1c1fa9b8a33ff16bad0f0da9a Mon Sep 17 00:00:00 2001 From: phroi <90913182+phroi@users.noreply.github.com> Date: Tue, 5 May 2026 23:56:04 +0000 Subject: [PATCH 1/5] refactor(tester): migrate to CCC and drop faucet app --- apps/faucet/.npmignore | 1 - apps/faucet/README.md | 46 --- apps/faucet/package.json | 58 --- apps/faucet/src/index.ts | 3 - apps/faucet/src/main.ts | 107 ------ apps/faucet/tsconfig.json | 11 - apps/faucet/vitest.config.mts | 10 - apps/interface/index.html | 9 +- apps/tester/README.md | 58 +-- apps/tester/package.json | 11 +- apps/tester/src/index.ts | 613 +++++++++++++------------------- forks/config.json | 29 +- packages/core/package.json | 2 +- packages/dao/package.json | 2 +- packages/order/package.json | 2 +- packages/sdk/package.json | 2 +- packages/utils/package.json | 2 +- pnpm-lock.yaml | 284 +-------------- pnpm-workspace.yaml | 27 +- scripts/check-ccc-overrides.mjs | 6 +- scripts/forks-ccc.mjs | 4 +- 21 files changed, 352 insertions(+), 935 deletions(-) delete mode 100644 apps/faucet/.npmignore delete mode 100644 apps/faucet/README.md delete mode 100644 apps/faucet/package.json delete mode 100644 apps/faucet/src/index.ts delete mode 100644 apps/faucet/src/main.ts delete mode 100644 apps/faucet/tsconfig.json delete mode 100644 apps/faucet/vitest.config.mts diff --git a/apps/faucet/.npmignore b/apps/faucet/.npmignore deleted file mode 100644 index 6396b25..0000000 --- a/apps/faucet/.npmignore +++ /dev/null @@ -1 +0,0 @@ -**/*{_,.}{test,spec}.* \ No newline at end of file diff --git a/apps/faucet/README.md b/apps/faucet/README.md deleted file mode 100644 index 6e5e651..0000000 --- a/apps/faucet/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# iCKB Faucet - -iCKB faucet an utility to help requesting testnet faucet funds and convert them directly to iCKB. - -## Run the limit order fulfillment faucet on testnet - -1. Download this repo in a folder of your choice: - -```bash -git clone https://github.com/ickb/stack.git -``` - -2. Enter into the repo folder: - -```bash -cd stack/apps/faucet -``` - -3. Install dependencies: - -```bash -pnpm install -``` - -4. Build project: - -```bash -pnpm build -``` - -5. Start the faucet utility: - -```bash -export ADDRESS=ckt-your-testnet-address; -pnpm start; -``` - -Or - -```bash -ADDRESS=ckt-your-testnet-address pnpm start; -``` - -## Licensing - -This source code, crafted with care by [Phroi](https://phroi.com/), is freely available on [GitHub](https://github.com/ickb/stack) and it is released under the [MIT License](../../LICENSE). diff --git a/apps/faucet/package.json b/apps/faucet/package.json deleted file mode 100644 index 85c3679..0000000 --- a/apps/faucet/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "@ickb/faucet", - "version": "1001.0.0", - "description": "iCKB faucet built on top of CCC", - "keywords": [ - "ickb", - "ccc", - "ckb", - "blockchain" - ], - "author": "phroi", - "license": "MIT", - "homepage": "https://ickb.org", - "repository": { - "type": "git", - "url": "https://github.com/ickb/stack" - }, - "bugs": { - "url": "https://github.com/ickb/stack/issues" - }, - "sideEffects": false, - "type": "module", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" - } - }, - "scripts": { - "test": "vitest", - "test:ci": "vitest run", - "build": "tsc", - "lint": "eslint ./src", - "clean": "rm -fr dist", - "clean:deep": "rm -fr dist node_modules", - "start": "node dist/index.js" - }, - "files": [ - "dist", - "src" - ], - "publishConfig": { - "access": "public", - "provenance": true - }, - "devDependencies": { - "@types/node": "catalog:" - }, - "dependencies": { - "@ckb-ccc/core": "catalog:", - "@ickb/core": "workspace:*", - "@ickb/dao": "workspace:*", - "@ickb/order": "workspace:*", - "@ickb/utils": "workspace:*" - } -} diff --git a/apps/faucet/src/index.ts b/apps/faucet/src/index.ts deleted file mode 100644 index 3c18894..0000000 --- a/apps/faucet/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { main } from "./main.js"; - -await main(); diff --git a/apps/faucet/src/main.ts b/apps/faucet/src/main.ts deleted file mode 100644 index 52ee3d9..0000000 --- a/apps/faucet/src/main.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { ccc } from "@ckb-ccc/core"; -import { sum, unique } from "@ickb/utils"; -import { getRandomValues } from "crypto"; -import { exit } from "process"; - -export async function main(): Promise { - const { ADDRESS } = process.env; - if (!ADDRESS) { - console.error("Empty env ADDRESS"); - exit(1); - } - - console.log("Your testnet account:"); - console.log(ADDRESS); - console.log(); - const client = new ccc.ClientPublicTestnet(); - const realAccount = await ccc.Address.fromString(ADDRESS, client); - - console.log("Generating temporary key:"); - const key = ccc.hexFrom(getRandomValues(new Uint8Array(32))); - console.log(key); - console.log(); - - const dummyAccount = new ccc.SignerCkbPrivateKey(client, key); - const dummyAddress = await dummyAccount.getRecommendedAddressObj(); - console.log("Use this dummy testnet account for requesting Faucet funds:"); - console.log(dummyAddress.toString()); - console.log(); - - for (;;) { - await new Promise((r) => { - setTimeout(r, 120000); - }); - console.log(); - - const executionLog: { - startTime?: string; - balance?: { - CKB?: string; - }; - error?: string | object; - txHash?: string; - elapsedSeconds?: number; - } = {}; - const startTime = new Date(); - executionLog.startTime = startTime.toLocaleString(); - try { - const capacities: ccc.Cell[] = []; - for (const lock of unique([dummyAddress.script])) { - for await (const cell of client.findCellsOnChain( - { - script: lock, - scriptType: "lock", - filter: { - scriptLenRange: [0n, 1n], - outputDataLenRange: [0n, 1n], - }, - scriptSearchMode: "exact", - withData: true, - }, - "asc", - 400, - )) { - if ( - cell.cellOutput.type !== undefined || - !cell.cellOutput.lock.eq(lock) - ) { - continue; - } - capacities.push(cell); - } - } - - if (capacities.length === 0) { - console.log("No faucet funds to transfer, shutting down..."); - exit(0); - } - - const ckbBalance = sum( - 0n, - ...capacities.map((c) => c.cellOutput.capacity), - ); - - executionLog.balance = { - CKB: ccc.fixedPointToString(ckbBalance), - }; - - const tx = ccc.Transaction.default(); - for (const cell of capacities) { - tx.addInput(cell); - } - await tx.completeFeeChangeToLock(dummyAccount, realAccount.script); - executionLog.txHash = await dummyAccount.sendTransaction(tx); - } catch (e) { - if (e instanceof Object && "stack" in e) { - /* eslint-disable-next-line @typescript-eslint/no-misused-spread */ - executionLog.error = { ...e, stack: e.stack ?? "" }; - } else { - executionLog.error = e ?? "Empty Error"; - } - } - executionLog.elapsedSeconds = Math.round( - (new Date().getTime() - startTime.getTime()) / 1000, - ); - console.log(JSON.stringify(executionLog, undefined, " ")); - } -} diff --git a/apps/faucet/tsconfig.json b/apps/faucet/tsconfig.json deleted file mode 100644 index 9f29614..0000000 --- a/apps/faucet/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "noEmit": false, - "rootDir": "src", - "outDir": "dist", - "sourceRoot": "../src", - "types": ["node"] - }, - "include": ["src"], -} diff --git a/apps/faucet/vitest.config.mts b/apps/faucet/vitest.config.mts deleted file mode 100644 index dc6a587..0000000 --- a/apps/faucet/vitest.config.mts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - include: ["src/**/*.test.ts"], - coverage: { - include: ["src/**/*.ts"], - }, - }, -}); diff --git a/apps/interface/index.html b/apps/interface/index.html index a825a3f..e7acc4e 100644 --- a/apps/interface/index.html +++ b/apps/interface/index.html @@ -146,13 +146,8 @@

Feel free to give feedback for how you would like to see it improved. - Also visit the - Nervos Testnet Faucet - if you need some free testnet CKB to test with. + If you need testnet CKB to try the app, fund the account before + starting the flow.

What is NervosDAO? diff --git a/apps/tester/README.md b/apps/tester/README.md index 4a1773b..63e9813 100644 --- a/apps/tester/README.md +++ b/apps/tester/README.md @@ -1,51 +1,53 @@ # iCKB Tester -## Run the simulation of iCKB limit order creation on testnet +The tester is now CCC-native. It cancels the tester's own active orders, then places randomized iCKB limit orders against the live testnet exchange ratio using the shared `@ickb/sdk`, `@ickb/core`, and `@ickb/order` packages. -1. Download this repo in a folder of your choice: +## Environment -```bash -git clone https://github.com/ickb/stack.git +Required variables: + +```text +CHAIN=testnet +TESTER_PRIVATE_KEY=0x... +TESTER_SLEEP_INTERVAL=10 ``` -2. Enter into the repo folder: +Optional variable: -```bash -cd stack/apps/tester +```text +RPC_URL=http://127.0.0.1:8114/ ``` -3. Install dependencies: +Current network support: + +- `CHAIN=testnet` +- `CHAIN=mainnet` + +## Run ```bash pnpm install +pnpm --filter ./apps/tester build +mkdir -p apps/tester/env/testnet +$EDITOR apps/tester/env/testnet/.env +export CHAIN=testnet +pnpm --filter ./apps/tester start ``` -4. Build project: +Or from `apps/tester`: ```bash +pnpm install pnpm build +mkdir -p env/testnet +$EDITOR env/testnet/.env +export CHAIN=testnet +pnpm run start ``` -5. Inside `apps/tester` define a `env/testnet/.env` file, for example: - -``` -CHAIN=testnet -TESTER_PRIVATE_KEY=0x-YOUR-SECP256K1-BLAKE160-PRIVATE-KEY -TESTER_SLEEP_INTERVAL=10 -``` - -Optionally the property `RPC_URL` can also be specified: +`CHAIN` selects `env/${CHAIN}/.env`, which must contain the remaining runtime variables such as `TESTER_PRIVATE_KEY` and `TESTER_SLEEP_INTERVAL`. -``` -RPC_URL=http://127.0.0.1:8114/ -``` - -6. Start simulation of user interactions: - -```bash -export CHAIN=testnet; -pnpm run start; -``` +The start script keeps the existing JSON log format and writes one log file per run. ## Licensing diff --git a/apps/tester/package.json b/apps/tester/package.json index 17a9d07..88ad8b8 100644 --- a/apps/tester/package.json +++ b/apps/tester/package.json @@ -49,12 +49,9 @@ "@types/node": "catalog:" }, "dependencies": { - "@ckb-lumos/base": "^0.23.0", - "@ckb-lumos/common-scripts": "^0.23.0", - "@ckb-lumos/config-manager": "^0.23.0", - "@ckb-lumos/hd": "^0.23.0", - "@ckb-lumos/helpers": "^0.23.0", - "@ickb/lumos-utils": "1.4.2", - "@ickb/v1-core": "1.4.2" + "@ckb-ccc/core": "catalog:", + "@ickb/core": "workspace:*", + "@ickb/order": "workspace:*", + "@ickb/sdk": "workspace:*" } } diff --git a/apps/tester/src/index.ts b/apps/tester/src/index.ts index d923594..2292c38 100644 --- a/apps/tester/src/index.ts +++ b/apps/tester/src/index.ts @@ -1,51 +1,35 @@ -import type { Cell, Transaction } from "@ckb-lumos/base"; -import { prepareSigningEntries } from "@ckb-lumos/common-scripts/lib/secp256k1_blake160.js"; -import { key } from "@ckb-lumos/hd"; -import type { TransactionSkeletonType } from "@ckb-lumos/helpers"; -import { - TransactionSkeleton, - encodeToAddress, - sealTransaction, -} from "@ckb-lumos/helpers"; -import { - CKB, - I8Cell, - I8Header, - I8Script, - addCells, - addCkbChange, - addWitnessPlaceholder, - calculateTxFee, - chainConfigFrom, - ckbDelta, - hex, - isChain, - lockExpanderFrom, - min, - simpleSifter, - txSize, - type ChainConfig, - type ConfigAdapter, -} from "@ickb/lumos-utils"; -import type { MyOrder, OrderRatio } from "@ickb/v1-core"; -import { - ICKB_SOFT_CAP_PER_DEPOSIT, - addIckbUdtChange, - addOwnedWithdrawalRequestsChange, - addReceiptDepositsChange, - addWithdrawalRequestGroups, - ckb2Ickb, - ckbSoftCapPerDeposit, - getIckbScriptConfigs, - ickb2Ckb, - ickbDelta, - ickbExchangeRatio, - ickbUdtType, - limitOrderScript, - orderMelt, - orderMint, - orderSifter, -} from "@ickb/v1-core"; +import { ccc } from "@ckb-ccc/core"; +import { ICKB_DEPOSIT_CAP, convert } from "@ickb/core"; +import { IckbSdk, getConfig, type SystemState } from "@ickb/sdk"; +import { type OrderGroup } from "@ickb/order"; + +const CKB = ccc.fixedPointFrom(1); +const CKB_RESERVE = 2000n * CKB; +const MIN_POST_TX_CKB = 1000n * CKB; +const MIN_TOTAL_CAPITAL_DIVISOR = 20n; +const TESTER_FEE = 100n; +const TESTER_FEE_BASE = 100000n; +const MAX_ELAPSED_BLOCKS = 100800n; +const FIND_CELLS_PAGE_SIZE = 400; + +interface Runtime { + chain: SupportedChain; + client: ccc.Client; + signer: ccc.SignerCkbPrivateKey; + sdk: IckbSdk; + managers: ReturnType["managers"]; + primaryLock: ccc.Script; + accountLocks: ccc.Script[]; +} + +interface TesterState { + system: SystemState; + userOrders: OrderGroup[]; + availableCkbBalance: bigint; + availableIckbBalance: bigint; +} + +type SupportedChain = "mainnet" | "testnet"; async function main(): Promise { const { CHAIN, RPC_URL, TESTER_PRIVATE_KEY, TESTER_SLEEP_INTERVAL } = @@ -53,9 +37,6 @@ async function main(): Promise { if (!CHAIN) { throw new Error("Invalid env CHAIN: Empty"); } - if (!isChain(CHAIN)) { - throw new Error("Invalid env CHAIN: " + CHAIN); - } if (!TESTER_PRIVATE_KEY) { throw new Error("Empty env TESTER_PRIVATE_KEY"); } @@ -63,108 +44,93 @@ async function main(): Promise { throw new Error("Invalid env TESTER_SLEEP_INTERVAL"); } - const chainConfig = await chainConfigFrom( - CHAIN, - RPC_URL, - true, - getIckbScriptConfigs, - ); - const { config, rpc, chain } = chainConfig; - const account = secp256k1Blake160(TESTER_PRIVATE_KEY, config); + const chain = parseChain(CHAIN); + const client = createClient(chain, RPC_URL); + const { managers, bots } = getConfig(chain); + const signer = new ccc.SignerCkbPrivateKey(client, TESTER_PRIVATE_KEY); + const primaryLock = (await signer.getRecommendedAddressObj()).script; + const runtime: Runtime = { + chain, + client, + signer, + sdk: new IckbSdk( + managers.ownedOwner, + managers.logic, + managers.order, + bots, + ), + managers, + primaryLock, + accountLocks: dedupeScripts( + (await signer.getAddressObjs()).map(({ script }) => script), + ), + }; const sleepInterval = Number(TESTER_SLEEP_INTERVAL) * 1000; for (;;) { - await new Promise((r) => { - setTimeout(r, 2 * Math.random() * sleepInterval); - }); - console.log(); + await sleep(2 * Math.random() * sleepInterval); - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - const executionLog: Record = {}; + const executionLog: Record = {}; const startTime = new Date(); executionLog.startTime = startTime.toLocaleString(); - try { - const { capacities, udts, myOrders } = await siftCells( - account, - chainConfig, - ); - const tipHeader = I8Header.from(await rpc.getTipHeader()); - const feeRate = await rpc.getFeeRate(61n); - - const maxElapsedBlocks = chain === "devnet" ? 500n : 100800n; //One week wait on testnet - // Wait for new orders to be matched - - // console.log(JSON.stringify(myOrders, replacer, " ")); - - if ( - myOrders.some( - (o) => - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - BigInt(o.cell.blockNumber!) + maxElapsedBlocks >= - BigInt(tipHeader.number) && o.info.isMatchable, - ) - ) { + try { + const state = await readTesterState(runtime); + if (await hasFreshMatchableOrders(runtime, state.userOrders, state.system.tip)) { continue; } - // Calculate balances and baseTx - const baseTx = base({ - capacities, - myOrders, - udts, - receipts: [], - wrGroups: [], - }); - const ckbBalance = ckbDelta(baseTx, config); - const ickbUdtBalance = ickbDelta(baseTx, config); + const depositAmount = convert(false, ICKB_DEPOSIT_CAP, state.system.tip); + const totalEquivalentCkb = + state.availableCkbBalance + + convert(false, state.availableIckbBalance, state.system.tip); executionLog.balance = { CKB: { - total: fmtCkb(ckbBalance), - available: fmtCkb(ckbBalance), + total: fmtCkb(state.availableCkbBalance), + available: fmtCkb(state.availableCkbBalance), unavailable: fmtCkb(0n), }, ICKB: { - total: fmtCkb(ickbUdtBalance), - available: fmtCkb(ickbUdtBalance), + total: fmtCkb(state.availableIckbBalance), + available: fmtCkb(state.availableIckbBalance), unavailable: fmtCkb(0n), }, totalEquivalent: { - CKB: fmtCkb(ckbBalance + ickb2Ckb(ickbUdtBalance, tipHeader)), - ICKB: fmtCkb(ckb2Ickb(ckbBalance, tipHeader) + ickbUdtBalance), + CKB: fmtCkb(totalEquivalentCkb), + ICKB: fmtCkb( + convert(true, state.availableCkbBalance, state.system.tip) + + state.availableIckbBalance, + ), }, }; - executionLog.ratio = ickbExchangeRatio(tipHeader); + executionLog.ratio = state.system.exchangeRatio; let r = Math.random(); - const ickbEquivalentBalance = Number(ckb2Ickb(ckbBalance, tipHeader)); - const ickbBalance = Number(ickbUdtBalance); + const ickbEquivalentBalance = Number( + convert(true, state.availableCkbBalance, state.system.tip), + ); + const ickbBalance = Number(state.availableIckbBalance); const isCkb2Udt = Math.round((ickbEquivalentBalance + ickbBalance) * r) <= - ickbEquivalentBalance; - // const isCkb2Udt = false; - // const isCkb2Udt = true; + ickbEquivalentBalance; r = Math.random(); const ckbAmount = isCkb2Udt ? min( - BigInt(Math.round(r * Number(ckbSoftCapPerDeposit(tipHeader)))), - ckbBalance - 2000n * CKB, + BigInt(Math.round(r * Number(depositAmount))), + state.availableCkbBalance - CKB_RESERVE, ) : 0n; const udtAmount = isCkb2Udt ? 0n : min( - BigInt(Math.round(r * Number(ICKB_SOFT_CAP_PER_DEPOSIT))), - ickbUdtBalance, + BigInt(Math.round(r * Number(ICKB_DEPOSIT_CAP))), + state.availableIckbBalance, ); - if (ckbAmount <= 0n && udtAmount <= 0) { - if ( - ckbBalance + ickb2Ckb(ickbUdtBalance, tipHeader) < - ckbSoftCapPerDeposit(tipHeader) / 20n // ~ 5000 CKB - ) { + if (ckbAmount <= 0n && udtAmount <= 0n) { + if (totalEquivalentCkb < depositAmount / MIN_TOTAL_CAPITAL_DIVISOR) { executionLog.error = "Not enough funds to continue testing, shutting down..."; console.log(JSON.stringify(executionLog, replacer, " ")); @@ -173,81 +139,45 @@ async function main(): Promise { continue; } - const { ckbMultiplier, udtMultiplier } = ickbExchangeRatio(tipHeader); - const ratio: OrderRatio = { - ckbMultiplier, - // Pay 0.1% fee to bot - udtMultiplier: - udtMultiplier + (isCkb2Udt ? 1n : -1n) * (udtMultiplier / 1000n), - }; - - const { tx, freeCkb, freeIckbUdt } = addChange( - orderMint( - baseTx, - account.lockScript, - config, - isCkb2Udt ? ckbAmount : undefined, - isCkb2Udt ? undefined : udtAmount, - isCkb2Udt ? ratio : undefined, - isCkb2Udt ? undefined : ratio, - ), - feeRate, - account, - chainConfig, - ); - - if (freeIckbUdt < 0n) { - throw new Error("Negative iCKB after the tx"); + const amounts = isCkb2Udt + ? { ckbValue: ckbAmount, udtValue: 0n } + : { ckbValue: 0n, udtValue: udtAmount }; + const estimate = IckbSdk.estimate(isCkb2Udt, amounts, state.system, { + fee: TESTER_FEE, + feeBase: TESTER_FEE_BASE, + }); + if (estimate.convertedAmount <= 0n) { + continue; } - if (isCkb2Udt) { - if (freeCkb < 1000n * CKB) { - throw new Error("Not enough CKB, less than 1000 CKB after the tx"); - } - } else { - if (freeCkb < 0n) { - throw new Error("Not enough CKB to execute the transaction"); - } + + if (isCkb2Udt && state.availableCkbBalance - ckbAmount < MIN_POST_TX_CKB) { + throw new Error("Not enough CKB, less than 1000 CKB after the tx"); } + const tx = await buildTransaction(runtime, state, amounts, estimate.info); + executionLog.actions = { newOrder: isCkb2Udt ? { giveCkb: fmtCkb(ckbAmount), - takeIckb: fmtCkb( - (ckbAmount * ratio.ckbMultiplier) / ratio.udtMultiplier, - ), - fee: fmtCkb( - ckbAmount - - ickb2Ckb( - (ckbAmount * ratio.ckbMultiplier) / ratio.udtMultiplier, - tipHeader, - ), - ), + takeIckb: fmtCkb(estimate.convertedAmount), + fee: fmtCkb(estimate.ckbFee), } : { giveIckb: fmtCkb(udtAmount), - takeCkb: fmtCkb( - (udtAmount * ratio.udtMultiplier) / ratio.ckbMultiplier, - ), - fee: fmtCkb( - ickb2Ckb(udtAmount, tipHeader) - - (udtAmount * ratio.udtMultiplier) / ratio.ckbMultiplier, - ), + takeCkb: fmtCkb(estimate.convertedAmount), + fee: fmtCkb(estimate.ckbFee), }, - cancelledOrders: myOrders.filter((o) => o.info.isMatchable).length, + cancelledOrders: state.userOrders.filter((group) => group.order.isMatchable()) + .length, }; executionLog.txFee = { - fee: fmtCkb(ckbDelta(tx, config)), - feeRate, + fee: fmtCkb(await tx.getFee(runtime.client)), + feeRate: state.system.feeRate, }; - executionLog.txHash = await rpc.sendTransaction(account.signer(tx)); + executionLog.txHash = await runtime.signer.sendTransaction(tx); } catch (e) { - if (e instanceof Object && "stack" in e) { - /* eslint-disable-next-line @typescript-eslint/no-misused-spread */ - executionLog.error = { ...e, stack: e.stack ?? "" }; - } else { - executionLog.error = e ?? "Empty Error"; - } + executionLog.error = errorToLog(e); } executionLog.ElapsedSeconds = Math.round( (new Date().getTime() - startTime.getTime()) / 1000, @@ -256,216 +186,177 @@ async function main(): Promise { } } -function fmtCkb(b: bigint): number { - return Number(b) / Number(CKB); -} +async function readTesterState(runtime: Runtime): Promise { + const { system, user } = await runtime.sdk.getL1State( + runtime.client, + runtime.accountLocks, + ); + const accountCells = await collectAccountCells(runtime.client, runtime.accountLocks); + const capacityCells = accountCells.filter( + (cell) => cell.cellOutput.type === undefined && cell.outputData === "0x", + ); + const udtCells = accountCells.filter((cell) => runtime.managers.ickbUdt.isUdt(cell)); + const walletUdtInfo = await runtime.managers.ickbUdt.infoFrom( + runtime.client, + udtCells, + ); -function replacer(_: unknown, value: unknown): unknown { - return typeof value === "bigint" ? Number(value) : value; + return { + system, + userOrders: user.orders, + availableCkbBalance: + sumValues(capacityCells, (cell) => cell.cellOutput.capacity) + + walletUdtInfo.capacity + + sumValues(user.orders, (group) => group.ckbValue), + availableIckbBalance: + walletUdtInfo.balance + sumValues(user.orders, (group) => group.udtValue), + }; } -function addChange( - tx: TransactionSkeletonType, - feeRate: bigint, - account: ReturnType, - chainConfig: ChainConfig, -): { - tx: TransactionSkeletonType; - freeCkb: bigint; - freeIckbUdt: bigint; -} { - const { lockScript: accountLock, preSigner: addPlaceholders } = account; - const { config } = chainConfig; - let freeCkb, freeIckbUdt; - tx = addReceiptDepositsChange(tx, accountLock, config); - tx = addOwnedWithdrawalRequestsChange(tx, accountLock, config); - ({ tx, freeIckbUdt } = addIckbUdtChange(tx, accountLock, config)); - ({ tx, freeCkb } = addCkbChange( - tx, - accountLock, - (txWithDummyChange: TransactionSkeletonType) => - calculateTxFee(txSize(addPlaceholders(txWithDummyChange)), feeRate), - config, - )); - - return { tx, freeCkb, freeIckbUdt }; -} +async function collectAccountCells( + client: ccc.Client, + locks: ccc.Script[], +): Promise { + const cells: ccc.Cell[] = []; + + for (const lock of locks) { + for await (const cell of client.findCellsOnChain( + { + script: lock, + scriptType: "lock", + scriptSearchMode: "exact", + withData: true, + }, + "asc", + FIND_CELLS_PAGE_SIZE, + )) { + cells.push(cell); + } + } -function base({ - capacities = [], - udts = [], - receipts = [], - wrGroups = [], - myOrders = [], -}: { - capacities?: I8Cell[]; - udts?: I8Cell[]; - receipts?: I8Cell[]; - wrGroups?: Readonly<{ - ownedWithdrawalRequest: I8Cell; - owner: I8Cell; - }>[]; - myOrders?: MyOrder[]; -}): TransactionSkeletonType { - let tx = TransactionSkeleton(); - tx = addCells(tx, "append", [capacities, udts, receipts].flat(), []); - tx = addWithdrawalRequestGroups(tx, wrGroups); - tx = orderMelt(tx, myOrders); - return tx; + return cells; } -async function siftCells( - account: ReturnType, - chainConfig: ChainConfig, -): Promise<{ - capacities: I8Cell[]; - udts: I8Cell[]; - myOrders: MyOrder[]; -}> { - const { rpc, config } = chainConfig; - const mixedCells = ( - await Promise.all( - [account.lockScript, limitOrderScript(config)].map((lock) => - rpc.getCellsByLock(lock, "desc", "max"), - ), - ) - ).flat(); - - const { expander } = account; - - // Prefetch txs outputs - const wantedTxsOutputs = new Set(); - const deferredGetTxsOutputs = (txHash: string): never[] => { - wantedTxsOutputs.add(txHash); - return []; - }; - orderSifter(mixedCells, expander, deferredGetTxsOutputs, config); - const txsOutputsPromise = getTxsOutputs(wantedTxsOutputs, chainConfig); - - // Sift capacities and udts - const { - notSimples, - capacities, - types: udts, - } = simpleSifter(mixedCells, ickbUdtType(config), account.expander); - - // Await for txsOutputs - const txsOutputs = await txsOutputsPromise; - - // Sift through Orders - const { myOrders } = orderSifter( - notSimples, - expander, - (txHash) => txsOutputs.get(txHash) ?? [], - config, - ); +async function hasFreshMatchableOrders( + runtime: Runtime, + orders: OrderGroup[], + tip: ccc.ClientBlockHeader, +): Promise { + for (const group of orders) { + if (!group.order.isMatchable()) { + continue; + } - return { capacities, udts, myOrders }; -} + const txWithHeader = await runtime.client.getTransactionWithHeader( + group.order.cell.outPoint.txHash, + ); + if (!txWithHeader?.header) { + throw new Error("Header not found for txHash"); + } -async function getTxsOutputs( - txHashes: Set, - chainConfig: ChainConfig, -): Promise>> { - const { rpc } = chainConfig; - - const result = new Map(); - const batch = rpc.createBatchRequest(); - for (const txHash of txHashes) { - const outputs = _knownTxsOutputs.get(txHash); - if (outputs !== undefined) { - result.set(txHash, outputs); - continue; + if (txWithHeader.header.number + MAX_ELAPSED_BLOCKS >= tip.number) { + return true; } - batch.add("getTransaction", txHash); } - if (batch.length === 0) { - return _knownTxsOutputs; + return false; +} + +async function buildTransaction( + runtime: Runtime, + state: TesterState, + amounts: { ckbValue: bigint; udtValue: bigint }, + info: Parameters[2], +): Promise { + let tx = ccc.Transaction.default(); + + if (state.userOrders.length > 0) { + tx = runtime.sdk.collect(tx, state.userOrders); } - for (const tx of (await batch.exec()).map( - ({ transaction: tx }: { transaction: Transaction }) => tx, - )) { - const txHash = tx.hash; - if (!txHash) { - throw new Error("Empty tx hash"); - } - result.set( - txHash, - Object.freeze( - tx.outputs.map(({ lock, type, capacity }, index) => - Object.freeze({ - cellOutput: Object.freeze({ - lock: Object.freeze(lock), - type: Object.freeze(type), - capacity: Object.freeze(capacity), - }), - data: Object.freeze(tx.outputsData[index] ?? "0x"), - outPoint: Object.freeze({ - txHash: txHash, - index: hex(index), - }), - } as Cell), - ), - ), + tx = await runtime.sdk.request(tx, runtime.primaryLock, info, amounts); + tx = await runtime.managers.ickbUdt.completeBy(tx, runtime.signer); + await tx.completeFeeBy(runtime.signer, state.system.feeRate); + + if (await ccc.isDaoOutputLimitExceeded(tx, runtime.client)) { + throw new Error( + `NervosDAO transaction has ${String(tx.outputs.length)} output cells, exceeding the limit of 64`, ); } - const frozenResult = Object.freeze(result); - _knownTxsOutputs = frozenResult; - return frozenResult; + return tx; } -let _knownTxsOutputs = Object.freeze(new Map>()); - -function secp256k1Blake160( - privateKey: string, - config: ConfigAdapter, -): { - publicKey: string; - lockScript: Readonly; - address: string; - expander: (c: Cell) => I8Script | undefined; - preSigner: (tx: TransactionSkeletonType) => TransactionSkeletonType; - signer: (tx: TransactionSkeletonType) => Transaction; -} { - const publicKey = key.privateToPublic(privateKey); - - const lockScript = I8Script.from({ - /* eslint-disable-next-line @typescript-eslint/no-misused-spread */ - ...config.defaultScript("SECP256K1_BLAKE160"), - args: key.publicKeyToBlake160(publicKey), - }); +function createClient(chain: SupportedChain, rpcUrl: string | undefined): ccc.Client { + const config = rpcUrl ? { url: rpcUrl } : undefined; + return chain === "mainnet" + ? new ccc.ClientPublicMainnet(config) + : new ccc.ClientPublicTestnet(config); +} - const address = encodeToAddress(lockScript, { config }); +function parseChain(chain: string): SupportedChain { + if (chain === "mainnet" || chain === "testnet") { + return chain; + } - const expander = lockExpanderFrom(lockScript); + throw new Error("Invalid env CHAIN: " + chain); +} - function preSigner(tx: TransactionSkeletonType): TransactionSkeletonType { - return addWitnessPlaceholder(tx, lockScript); - } +function dedupeScripts(scripts: ccc.Script[]): ccc.Script[] { + const seen = new Set(); + const unique: ccc.Script[] = []; - function signer(tx: TransactionSkeletonType): Transaction { - tx = preSigner(tx); - tx = prepareSigningEntries(tx, { config }); - const message = tx.get("signingEntries").get(0)?.message; - if (!message) { - throw new Error("Empty message to sign"); + for (const script of scripts) { + const key = script.toHex(); + if (seen.has(key)) { + continue; } - const sig = key.signRecoverable(message, privateKey); + seen.add(key); + unique.push(script); + } - return sealTransaction(tx, [sig]); + return unique; +} + +function sumValues(items: readonly T[], project: (item: T) => bigint): bigint { + let total = 0n; + for (const item of items) { + total += project(item); } + return total; +} - return { - publicKey, - lockScript, - address, - expander, - preSigner, - signer, - }; +function fmtCkb(balance: bigint): number { + return Number(balance) / Number(CKB); +} + +function replacer(_: unknown, value: unknown): unknown { + return typeof value === "bigint" ? Number(value) : value; +} + +function errorToLog(error: unknown): unknown { + if (error instanceof Object && "stack" in error) { + return { + name: "name" in error ? error.name : undefined, + message: + "message" in error && typeof error.message === "string" + ? error.message + : "Unknown error", + stack: error.stack ?? "", + }; + } + + return error ?? "Empty Error"; +} + +function min(left: bigint, right: bigint): bigint { + return left < right ? left : right; +} + +function sleep(ms: number): Promise { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); } await main(); diff --git a/forks/config.json b/forks/config.json index 9adebb2..3c77e6b 100644 --- a/forks/config.json +++ b/forks/config.json @@ -12,15 +12,26 @@ ], "workspace": { "include": [ - "packages/*" - ], - "exclude": [ - "packages/demo", - "packages/docs", - "packages/examples", - "packages/faucet", - "packages/playground", - "packages/tests" + "packages/ccc", + "packages/ckb-ccc", + "packages/connector", + "packages/connector-react", + "packages/core", + "packages/did-ckb", + "packages/eip6963", + "packages/joy-id", + "packages/lumos-patches", + "packages/nip07", + "packages/okx", + "packages/rei", + "packages/shell", + "packages/spore", + "packages/ssri", + "packages/type-id", + "packages/udt", + "packages/uni-sat", + "packages/utxo-global", + "packages/xverse" ] } }, diff --git a/packages/core/package.json b/packages/core/package.json index 2b66e1b..c82951e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,7 +25,7 @@ "exports": { ".": { "types": "./src/index.ts", - "import": "./src/index.ts" + "import": "./dist/index.js" } }, "scripts": { diff --git a/packages/dao/package.json b/packages/dao/package.json index df17f69..8252080 100644 --- a/packages/dao/package.json +++ b/packages/dao/package.json @@ -25,7 +25,7 @@ "exports": { ".": { "types": "./src/index.ts", - "import": "./src/index.ts" + "import": "./dist/index.js" } }, "scripts": { diff --git a/packages/order/package.json b/packages/order/package.json index 82cfd9f..99c971a 100644 --- a/packages/order/package.json +++ b/packages/order/package.json @@ -25,7 +25,7 @@ "exports": { ".": { "types": "./src/index.ts", - "import": "./src/index.ts" + "import": "./dist/index.js" } }, "scripts": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index e645e84..f468590 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -25,7 +25,7 @@ "exports": { ".": { "types": "./src/index.ts", - "import": "./src/index.ts" + "import": "./dist/index.js" } }, "scripts": { diff --git a/packages/utils/package.json b/packages/utils/package.json index dd1974f..880448f 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -25,7 +25,7 @@ "exports": { ".": { "types": "./src/index.ts", - "import": "./src/index.ts" + "import": "./dist/index.js" } }, "scripts": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80de986..3a40a9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,28 +66,6 @@ importers: specifier: 'catalog:' version: 24.12.2 - apps/faucet: - dependencies: - '@ckb-ccc/core': - specifier: workspace:* - version: link:../../forks/ccc/repo/packages/core - '@ickb/core': - specifier: workspace:* - version: link:../../packages/core - '@ickb/dao': - specifier: workspace:* - version: link:../../packages/dao - '@ickb/order': - specifier: workspace:* - version: link:../../packages/order - '@ickb/utils': - specifier: workspace:* - version: link:../../packages/utils - devDependencies: - '@types/node': - specifier: 'catalog:' - version: 24.12.2 - apps/interface: dependencies: '@ckb-ccc/ccc': @@ -167,27 +145,18 @@ importers: apps/tester: dependencies: - '@ckb-lumos/base': - specifier: ^0.23.0 - version: 0.23.0 - '@ckb-lumos/common-scripts': - specifier: ^0.23.0 - version: 0.23.0 - '@ckb-lumos/config-manager': - specifier: ^0.23.0 - version: 0.23.0 - '@ckb-lumos/hd': - specifier: ^0.23.0 - version: 0.23.0 - '@ckb-lumos/helpers': - specifier: ^0.23.0 - version: 0.23.0 - '@ickb/lumos-utils': - specifier: 1.4.2 - version: 1.4.2 - '@ickb/v1-core': - specifier: 1.4.2 - version: 1.4.2 + '@ckb-ccc/core': + specifier: workspace:* + version: link:../../forks/ccc/repo/packages/core + '@ickb/core': + specifier: workspace:* + version: link:../../packages/core + '@ickb/order': + specifier: workspace:* + version: link:../../packages/order + '@ickb/sdk': + specifier: workspace:* + version: link:../../packages/sdk devDependencies: '@types/node': specifier: 'catalog:' @@ -1304,46 +1273,22 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@ckb-lumos/base@0.23.0': - resolution: {integrity: sha512-8aLFsUyWIK0rT7GQlYFuXyiG5lQ2bLRK2GvUsxv5G7I3nJ1UyxjwvVOdtlsR/cwhzOam3ujwqASqBIayBL6GLA==} - engines: {node: '>=12.0.0'} - '@ckb-lumos/base@0.24.0-next.2': resolution: {integrity: sha512-VCXYpykJ+OhjbcCp3imwt9rk3Ie2T8jyCjSXExkoyKY+QT+EI856p37KBbBdO4r7gguVtADnJS+WLIZoNm5Bvw==} engines: {node: '>=12.0.0'} - '@ckb-lumos/bi@0.23.0': - resolution: {integrity: sha512-KAy+lyVpL+Al4XD+c9tHrA9DSpHkMusyXtTS81aNZi5MyL6F9jrVmFcqLorhfyfl8Fsv2sEjMe5Neo2Y+w/RJQ==} - engines: {node: '>=12.0.0'} - '@ckb-lumos/bi@0.24.0-next.2': resolution: {integrity: sha512-KGRv2M0EsZw08qIjauew43VBoG8X6IPiPrW7Bn5nnrqWh0i+JE8ZC4fsS6kGmAhUMw3OcTzinU7cQ318CjaC3Q==} engines: {node: '>=12.0.0'} - '@ckb-lumos/ckb-indexer@0.23.0': - resolution: {integrity: sha512-yLODLJzvtz4M6W6OJR4iRbBTUGrKReV2dhVePPjbH/HRkRY6f6J6cEM9+qM2I3QABmaCXeAM3hWvnWU9hjalQQ==} - engines: {node: '>=12.0.0'} - - '@ckb-lumos/codec@0.23.0': - resolution: {integrity: sha512-FwYooXnsFDjlHHnlFnCTB1UbBzV72I0VjkRpeauFk5nQ4+/75xl28ywK3J14M+0aHTnYU9msXUTRDAGqC0CaNQ==} - engines: {node: '>=12.0.0'} - '@ckb-lumos/codec@0.24.0-next.2': resolution: {integrity: sha512-yPrAwJyLJw4jf2GMMcJHRE2Z/I9ZD0e7VzzHA0rwTNaaXHpjZWdR5ffBqHGjNAobIrIlJlv9FVDLZTukO1RohA==} engines: {node: '>=12.0.0'} - '@ckb-lumos/common-scripts@0.23.0': - resolution: {integrity: sha512-Dwic0Al94afdGNu+TGAMmZiU5OVF/zvXbzhCvNmkFS25t8BxPdFjGEc0MlWBI4ZSEoGRrC0O+BOxjzfl5VxSYg==} - engines: {node: '>=12.0.0'} - '@ckb-lumos/common-scripts@0.24.0-next.2': resolution: {integrity: sha512-wPaAHYWoxOuYLkkfRImtTxaElfMuL5mYA/L5nSCTCkMCncvt3loMjbYlBZY6IBMMclwobTfMPej5Bg5cUpHadA==} engines: {node: '>=12.0.0'} - '@ckb-lumos/config-manager@0.23.0': - resolution: {integrity: sha512-MvNyzGIJTmIpEf5WJB3TkE4icZyZ2HZhFIfJB2SXDRAC84E02jxENPelCnqRbM1rlFHnxjh/5a/oCi5LcXefag==} - engines: {node: '>=12.0.0'} - '@ckb-lumos/config-manager@0.24.0-next.2': resolution: {integrity: sha512-/HL0fjFXFnTaJNRyZqHk6/MiioDWeB6bVcm6MO97E6tJVBxTJjIe/cvRjqohEAO3SRst9X81vh2WIB7lCIBjfA==} engines: {node: '>=12.0.0'} @@ -1352,34 +1297,14 @@ packages: resolution: {integrity: sha512-wJ1gE5ThZX15q45wRLX0oUoZ/qyE3jIIBnM7wuOWjAVEdp7LVRgNDoYHd7pFCYDH2j6rJwLNhJW41o1f7Aqqlg==} engines: {node: '>=18.0.0'} - '@ckb-lumos/hd@0.23.0': - resolution: {integrity: sha512-z7EsR/GeX54hq4ukqwW3nrqLCsYrTWIFAjZLR1Ao8xycqQp0IBjCWZLLjRrZY6krbUQpVOoiKo3NBLpPW36LXg==} - engines: {node: '>=12.0.0'} - - '@ckb-lumos/helpers@0.23.0': - resolution: {integrity: sha512-yfD28vSn1BBk8BA+/ivL7pF3rMsx4OPQ+UUJjsQiR1zGdkNR3zhJOecgICeddJGYDTBBDVgwHcuyoekLxQzmGg==} - engines: {node: '>=12.0.0'} - '@ckb-lumos/helpers@0.24.0-next.2': resolution: {integrity: sha512-yHn1Ty1lyOTRKyEKGzH88eBE4iio7iDpoyejsWVZVk6Eetkv1XS/zNA2J5w7KVhjOzbNK7GUUU9v/3kzYWpG0A==} engines: {node: '>=12.0.0'} - '@ckb-lumos/light-client@0.23.0': - resolution: {integrity: sha512-O+dbfubDjl0iODiQ8Q+RVJLfuXYNCN6c0am8xEv4vvazLwfw1y/vn6fG/pFu9Mc1GahZ58y9o6fkUgs8ujH3Mg==} - engines: {node: '>=12.0.0'} - - '@ckb-lumos/rpc@0.23.0': - resolution: {integrity: sha512-NEY1Wb2cNMYdHwcZYtd8XZ3CP6WGPd25hcsudoDAFlAt9vjHsPlNiwSS7tcZCZfg1XiJy3taViVgG8pFemgpbA==} - engines: {node: '>=12.0.0'} - '@ckb-lumos/rpc@0.24.0-next.2': resolution: {integrity: sha512-C/Vjvbh+GStxA/9vcj6oI1apZQRoydIvc8T1DR5Dh3F9K1q8NUSnJ43bLNOcwusdMWDkRNNS0jyoKirtLJy5Hg==} engines: {node: '>=12.0.0'} - '@ckb-lumos/toolkit@0.23.0': - resolution: {integrity: sha512-7LTsUFfoNCBWJLgh+V/QFnemjGw+y4mmLeQvubwYuJqIPIhIpwKUuKRzkvVG8snA8xVQSfjSSQOs5m3mKp66Kg==} - engines: {node: '>=12.0.0'} - '@ckb-lumos/toolkit@0.24.0-next.2': resolution: {integrity: sha512-faqOZpj0H21vsqfQXfzRRQUEgF3vZ9i3PcqyF7hNbrNeR6VUcIzJL8QskhjFhBusxAvKf56QbX0/T06/PAgbfg==} engines: {node: '>=12.0.0'} @@ -1607,14 +1532,6 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@ickb/lumos-utils@1.4.2': - resolution: {integrity: sha512-sN9wZ5U9sUhK/hvLkRH7TpTgbweszLKNSOSJ6Ber9EKVqyQ0eD7qnJlGzDudmHt1Z/uEPlWBvD0cYOi61sK8Dg==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - '@ickb/v1-core@1.4.2': - resolution: {integrity: sha512-39UKHX2DnCO9fWvM2nqLnDHLzHFSNuaWmTnNfc8Tg4/XD/1iNhafKsqcQXtExcHXji5iUPtAbGWqA4H8beIlhw==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - '@ipld/dag-cbor@9.2.6': resolution: {integrity: sha512-vZGJ84Em2jCVAS7td5gc08YTVN8/s4bTQxg4pU77PAXDAR/yLYOthOvkCu01fdl1lrZwz47RdUterxdkrs3p5A==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} @@ -2427,9 +2344,6 @@ packages: bn.js@4.12.3: resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==} - bn.js@5.2.3: - resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} - brace-expansion@1.1.14: resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} @@ -2727,10 +2641,6 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} @@ -3098,10 +3008,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -3373,9 +3279,6 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - scrypt-js@3.0.1: - resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -3385,9 +3288,6 @@ packages: engines: {node: '>=10'} hasBin: true - sha3@2.1.4: - resolution: {integrity: sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -3606,11 +3506,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). - hasBin: true - valibot@1.3.1: resolution: {integrity: sha512-sfdRir/QFM0JaF22hqTroPc5xy4DimuGQVKFrzF1YfGwaS1nJot3Y8VqMdLO2Lg27fMzat2yD3pY5PbAYO39Gg==} peerDependencies: @@ -4003,19 +3898,6 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@ckb-lumos/base@0.23.0': - dependencies: - '@ckb-lumos/bi': 0.23.0 - '@ckb-lumos/codec': 0.23.0 - '@ckb-lumos/toolkit': 0.23.0 - '@types/blake2b': 2.1.3 - '@types/lodash.isequal': 4.5.8 - blake2b: 2.1.4 - js-xxhash: 1.0.4 - lodash.isequal: 4.5.0 - transitivePeerDependencies: - - react-native-b4a - '@ckb-lumos/base@0.24.0-next.2': dependencies: '@ckb-lumos/bi': 0.24.0-next.2 @@ -4028,51 +3910,14 @@ snapshots: transitivePeerDependencies: - react-native-b4a - '@ckb-lumos/bi@0.23.0': - dependencies: - jsbi: 4.3.2 - '@ckb-lumos/bi@0.24.0-next.2': dependencies: jsbi: 4.3.2 - '@ckb-lumos/ckb-indexer@0.23.0': - dependencies: - '@ckb-lumos/base': 0.23.0 - '@ckb-lumos/bi': 0.23.0 - '@ckb-lumos/codec': 0.23.0 - '@ckb-lumos/rpc': 0.23.0 - '@ckb-lumos/toolkit': 0.23.0 - cross-fetch: 3.2.0 - events: 3.3.0 - transitivePeerDependencies: - - encoding - - react-native-b4a - - '@ckb-lumos/codec@0.23.0': - dependencies: - '@ckb-lumos/bi': 0.23.0 - '@ckb-lumos/codec@0.24.0-next.2': dependencies: '@ckb-lumos/bi': 0.24.0-next.2 - '@ckb-lumos/common-scripts@0.23.0': - dependencies: - '@ckb-lumos/base': 0.23.0 - '@ckb-lumos/bi': 0.23.0 - '@ckb-lumos/codec': 0.23.0 - '@ckb-lumos/config-manager': 0.23.0 - '@ckb-lumos/helpers': 0.23.0 - '@ckb-lumos/rpc': 0.23.0 - '@ckb-lumos/toolkit': 0.23.0 - bech32: 2.0.0 - bs58: 5.0.0 - immutable: 4.3.8 - transitivePeerDependencies: - - encoding - - react-native-b4a - '@ckb-lumos/common-scripts@0.24.0-next.2': dependencies: '@ckb-lumos/base': 0.24.0-next.2 @@ -4090,18 +3935,6 @@ snapshots: - encoding - react-native-b4a - '@ckb-lumos/config-manager@0.23.0': - dependencies: - '@ckb-lumos/base': 0.23.0 - '@ckb-lumos/bi': 0.23.0 - '@ckb-lumos/codec': 0.23.0 - '@ckb-lumos/rpc': 0.23.0 - '@types/deep-freeze-strict': 1.1.2 - deep-freeze-strict: 1.1.1 - transitivePeerDependencies: - - encoding - - react-native-b4a - '@ckb-lumos/config-manager@0.24.0-next.2': dependencies: '@ckb-lumos/base': 0.24.0-next.2 @@ -4119,31 +3952,6 @@ snapshots: '@noble/ciphers': 0.5.3 '@noble/hashes': 1.8.0 - '@ckb-lumos/hd@0.23.0': - dependencies: - '@ckb-lumos/base': 0.23.0 - '@ckb-lumos/bi': 0.23.0 - bn.js: 5.2.3 - elliptic: 6.6.1 - scrypt-js: 3.0.1 - sha3: 2.1.4 - uuid: 8.3.2 - transitivePeerDependencies: - - react-native-b4a - - '@ckb-lumos/helpers@0.23.0': - dependencies: - '@ckb-lumos/base': 0.23.0 - '@ckb-lumos/bi': 0.23.0 - '@ckb-lumos/codec': 0.23.0 - '@ckb-lumos/config-manager': 0.23.0 - '@ckb-lumos/toolkit': 0.23.0 - bech32: 2.0.0 - immutable: 4.3.8 - transitivePeerDependencies: - - encoding - - react-native-b4a - '@ckb-lumos/helpers@0.24.0-next.2': dependencies: '@ckb-lumos/base': 0.24.0-next.2 @@ -4157,27 +3965,6 @@ snapshots: - encoding - react-native-b4a - '@ckb-lumos/light-client@0.23.0': - dependencies: - '@ckb-lumos/base': 0.23.0 - '@ckb-lumos/ckb-indexer': 0.23.0 - '@ckb-lumos/rpc': 0.23.0 - cross-fetch: 3.2.0 - events: 3.3.0 - transitivePeerDependencies: - - encoding - - react-native-b4a - - '@ckb-lumos/rpc@0.23.0': - dependencies: - '@ckb-lumos/base': 0.23.0 - '@ckb-lumos/bi': 0.23.0 - abort-controller: 3.0.0 - cross-fetch: 3.2.0 - transitivePeerDependencies: - - encoding - - react-native-b4a - '@ckb-lumos/rpc@0.24.0-next.2': dependencies: '@ckb-lumos/base': 0.24.0-next.2 @@ -4188,10 +3975,6 @@ snapshots: - encoding - react-native-b4a - '@ckb-lumos/toolkit@0.23.0': - dependencies: - '@ckb-lumos/bi': 0.23.0 - '@ckb-lumos/toolkit@0.24.0-next.2': dependencies: '@ckb-lumos/bi': 0.24.0-next.2 @@ -4352,35 +4135,6 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@ickb/lumos-utils@1.4.2': - dependencies: - '@ckb-lumos/base': 0.23.0 - '@ckb-lumos/ckb-indexer': 0.23.0 - '@ckb-lumos/codec': 0.23.0 - '@ckb-lumos/common-scripts': 0.23.0 - '@ckb-lumos/config-manager': 0.23.0 - '@ckb-lumos/helpers': 0.23.0 - '@ckb-lumos/light-client': 0.23.0 - '@ckb-lumos/rpc': 0.23.0 - immutable: 4.3.8 - transitivePeerDependencies: - - encoding - - react-native-b4a - - '@ickb/v1-core@1.4.2': - dependencies: - '@ckb-lumos/base': 0.23.0 - '@ckb-lumos/ckb-indexer': 0.23.0 - '@ckb-lumos/codec': 0.23.0 - '@ckb-lumos/common-scripts': 0.23.0 - '@ckb-lumos/config-manager': 0.23.0 - '@ckb-lumos/helpers': 0.23.0 - '@ckb-lumos/rpc': 0.23.0 - '@ickb/lumos-utils': 1.4.2 - transitivePeerDependencies: - - encoding - - react-native-b4a - '@ipld/dag-cbor@9.2.6': dependencies: cborg: 5.1.1 @@ -5096,8 +4850,6 @@ snapshots: bn.js@4.12.3: {} - bn.js@5.2.3: {} - brace-expansion@1.1.14: dependencies: balanced-match: 1.0.2 @@ -5442,8 +5194,6 @@ snapshots: event-target-shim@5.0.1: {} - events@3.3.0: {} - expect-type@1.3.0: {} fast-deep-equal@3.1.3: {} @@ -5764,8 +5514,6 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash.isequal@4.5.0: {} - lodash.merge@4.6.2: {} loupe@3.2.1: {} @@ -6052,16 +5800,10 @@ snapshots: scheduler@0.27.0: {} - scrypt-js@3.0.1: {} - semver@6.3.1: {} semver@7.7.4: {} - sha3@2.1.4: - dependencies: - buffer: 6.0.3 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -6250,8 +5992,6 @@ snapshots: util-deprecate@1.0.2: {} - uuid@8.3.2: {} - valibot@1.3.1(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b11e2a5..5349c74 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,13 +2,26 @@ packages: - packages/* - apps/* # Local CCC workspace when materialized via `pnpm forks:bootstrap`. - - forks/ccc/repo/packages/* - - "!forks/ccc/repo/packages/demo" - - "!forks/ccc/repo/packages/docs" - - "!forks/ccc/repo/packages/examples" - - "!forks/ccc/repo/packages/faucet" - - "!forks/ccc/repo/packages/playground" - - "!forks/ccc/repo/packages/tests" + - forks/ccc/repo/packages/ccc + - forks/ccc/repo/packages/ckb-ccc + - forks/ccc/repo/packages/connector + - forks/ccc/repo/packages/connector-react + - forks/ccc/repo/packages/core + - forks/ccc/repo/packages/did-ckb + - forks/ccc/repo/packages/eip6963 + - forks/ccc/repo/packages/joy-id + - forks/ccc/repo/packages/lumos-patches + - forks/ccc/repo/packages/nip07 + - forks/ccc/repo/packages/okx + - forks/ccc/repo/packages/rei + - forks/ccc/repo/packages/shell + - forks/ccc/repo/packages/spore + - forks/ccc/repo/packages/ssri + - forks/ccc/repo/packages/type-id + - forks/ccc/repo/packages/udt + - forks/ccc/repo/packages/uni-sat + - forks/ccc/repo/packages/utxo-global + - forks/ccc/repo/packages/xverse overrides: # Keep published manifests on `catalog:` while forcing the materialized diff --git a/scripts/check-ccc-overrides.mjs b/scripts/check-ccc-overrides.mjs index e3ed3fb..f3f2384 100644 --- a/scripts/check-ccc-overrides.mjs +++ b/scripts/check-ccc-overrides.mjs @@ -1,4 +1,4 @@ -import { readdirSync, readFileSync } from "node:fs"; +import { existsSync, readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -47,7 +47,9 @@ function collectDirectCccDeps(root) { const groupDir = join(root, group); for (const entry of readdirSync(groupDir, { withFileTypes: true })) { if (!entry.isDirectory()) continue; - manifests.push(join(groupDir, entry.name, "package.json")); + const manifestPath = join(groupDir, entry.name, "package.json"); + if (!existsSync(manifestPath)) continue; + manifests.push(manifestPath); } } diff --git a/scripts/forks-ccc.mjs b/scripts/forks-ccc.mjs index ed5ab63..5aed84d 100644 --- a/scripts/forks-ccc.mjs +++ b/scripts/forks-ccc.mjs @@ -76,7 +76,9 @@ function collectDirectCccDeps(root) { const groupDir = join(root, group); for (const entry of readdirSync(groupDir, { withFileTypes: true })) { if (!entry.isDirectory()) continue; - manifests.push(join(groupDir, entry.name, "package.json")); + const manifestPath = join(groupDir, entry.name, "package.json"); + if (!existsSync(manifestPath)) continue; + manifests.push(manifestPath); } } From d427f4f91bcc0f86e7637bc116c88dc6e9897204 Mon Sep 17 00:00:00 2001 From: phroi <90913182+phroi@users.noreply.github.com> Date: Wed, 6 May 2026 00:08:28 +0000 Subject: [PATCH 2/5] fix(tester): preserve bigint precision in sampling --- apps/tester/src/index.ts | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/apps/tester/src/index.ts b/apps/tester/src/index.ts index 2292c38..2d2359c 100644 --- a/apps/tester/src/index.ts +++ b/apps/tester/src/index.ts @@ -11,6 +11,7 @@ const TESTER_FEE = 100n; const TESTER_FEE_BASE = 100000n; const MAX_ELAPSED_BLOCKS = 100800n; const FIND_CELLS_PAGE_SIZE = 400; +const RANDOM_SCALE = 1000000n; interface Runtime { chain: SupportedChain; @@ -106,26 +107,25 @@ async function main(): Promise { }; executionLog.ratio = state.system.exchangeRatio; - let r = Math.random(); - const ickbEquivalentBalance = Number( - convert(true, state.availableCkbBalance, state.system.tip), + const ickbEquivalentBalance = convert( + true, + state.availableCkbBalance, + state.system.tip, ); - const ickbBalance = Number(state.availableIckbBalance); + const totalIckbBalance = ickbEquivalentBalance + state.availableIckbBalance; const isCkb2Udt = - Math.round((ickbEquivalentBalance + ickbBalance) * r) <= - ickbEquivalentBalance; + sampleRatio(totalIckbBalance) <= ickbEquivalentBalance; - r = Math.random(); const ckbAmount = isCkb2Udt ? min( - BigInt(Math.round(r * Number(depositAmount))), + sampleRatio(depositAmount), state.availableCkbBalance - CKB_RESERVE, ) : 0n; const udtAmount = isCkb2Udt ? 0n : min( - BigInt(Math.round(r * Number(ICKB_DEPOSIT_CAP))), + sampleRatio(ICKB_DEPOSIT_CAP), state.availableIckbBalance, ); @@ -353,6 +353,18 @@ function min(left: bigint, right: bigint): bigint { return left < right ? left : right; } +function sampleRatio(amount: bigint): bigint { + if (amount <= 0n) { + return 0n; + } + + return (amount * randomScaled()) / RANDOM_SCALE; +} + +function randomScaled(): bigint { + return BigInt(Math.floor(Math.random() * Number(RANDOM_SCALE))); +} + function sleep(ms: number): Promise { return new Promise((resolve) => { setTimeout(resolve, ms); From 0be1a718e99ab758530d62ab5bc49ebca67ede98 Mon Sep 17 00:00:00 2001 From: phroi <90913182+phroi@users.noreply.github.com> Date: Wed, 6 May 2026 00:17:52 +0000 Subject: [PATCH 3/5] refactor(tester): narrow cell collection queries --- apps/tester/src/index.ts | 66 +++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/apps/tester/src/index.ts b/apps/tester/src/index.ts index 2d2359c..7ce541f 100644 --- a/apps/tester/src/index.ts +++ b/apps/tester/src/index.ts @@ -187,15 +187,11 @@ async function main(): Promise { } async function readTesterState(runtime: Runtime): Promise { - const { system, user } = await runtime.sdk.getL1State( - runtime.client, - runtime.accountLocks, - ); - const accountCells = await collectAccountCells(runtime.client, runtime.accountLocks); - const capacityCells = accountCells.filter( - (cell) => cell.cellOutput.type === undefined && cell.outputData === "0x", - ); - const udtCells = accountCells.filter((cell) => runtime.managers.ickbUdt.isUdt(cell)); + const [{ system, user }, capacityCells, udtCells] = await Promise.all([ + runtime.sdk.getL1State(runtime.client, runtime.accountLocks), + collectCapacityCells(runtime.signer), + collectWalletUdtCells(runtime.signer, runtime.managers.ickbUdt), + ]); const walletUdtInfo = await runtime.managers.ickbUdt.infoFrom( runtime.client, udtCells, @@ -213,25 +209,47 @@ async function readTesterState(runtime: Runtime): Promise { }; } -async function collectAccountCells( - client: ccc.Client, - locks: ccc.Script[], +async function collectCapacityCells( + signer: ccc.SignerCkbPrivateKey, +): Promise { + const cells: ccc.Cell[] = []; + + for await (const cell of signer.findCellsOnChain( + { + scriptLenRange: [0n, 1n], + outputDataLenRange: [0n, 1n], + }, + true, + "asc", + FIND_CELLS_PAGE_SIZE, + )) { + if (cell.cellOutput.type !== undefined || cell.outputData !== "0x") { + continue; + } + + cells.push(cell); + } + + return cells; +} + +async function collectWalletUdtCells( + signer: ccc.SignerCkbPrivateKey, + ickbUdt: Runtime["managers"]["ickbUdt"], ): Promise { const cells: ccc.Cell[] = []; - for (const lock of locks) { - for await (const cell of client.findCellsOnChain( - { - script: lock, - scriptType: "lock", - scriptSearchMode: "exact", - withData: true, - }, - "asc", - FIND_CELLS_PAGE_SIZE, - )) { - cells.push(cell); + for await (const cell of signer.findCellsOnChain( + ickbUdt.filter, + true, + "asc", + FIND_CELLS_PAGE_SIZE, + )) { + if (!ickbUdt.isUdt(cell)) { + continue; } + + cells.push(cell); } return cells; From f067738ce07441dd5bdad73a542f1990ba064513 Mon Sep 17 00:00:00 2001 From: phroi <90913182+phroi@users.noreply.github.com> Date: Wed, 6 May 2026 00:33:03 +0000 Subject: [PATCH 4/5] perf(tester): cache order tx block lookups --- apps/tester/src/index.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/tester/src/index.ts b/apps/tester/src/index.ts index 7ce541f..8e92140 100644 --- a/apps/tester/src/index.ts +++ b/apps/tester/src/index.ts @@ -223,10 +223,6 @@ async function collectCapacityCells( "asc", FIND_CELLS_PAGE_SIZE, )) { - if (cell.cellOutput.type !== undefined || cell.outputData !== "0x") { - continue; - } - cells.push(cell); } @@ -260,19 +256,26 @@ async function hasFreshMatchableOrders( orders: OrderGroup[], tip: ccc.ClientBlockHeader, ): Promise { + const tx2BlockNumber = new Map(); + for (const group of orders) { if (!group.order.isMatchable()) { continue; } - const txWithHeader = await runtime.client.getTransactionWithHeader( - group.order.cell.outPoint.txHash, - ); - if (!txWithHeader?.header) { - throw new Error("Header not found for txHash"); + const txHash = group.order.cell.outPoint.txHash; + let blockNumber = tx2BlockNumber.get(txHash); + if (blockNumber === undefined) { + const tx = await runtime.client.getTransaction(txHash); + if (!tx?.blockNumber) { + throw new Error("Block number not found for order tx"); + } + + blockNumber = tx.blockNumber; + tx2BlockNumber.set(txHash, blockNumber); } - if (txWithHeader.header.number + MAX_ELAPSED_BLOCKS >= tip.number) { + if (blockNumber + MAX_ELAPSED_BLOCKS >= tip.number) { return true; } } From 7b0076473d0688a587b11fe88b7c680041e41ba5 Mon Sep 17 00:00:00 2001 From: phroi <90913182+phroi@users.noreply.github.com> Date: Wed, 6 May 2026 00:40:13 +0000 Subject: [PATCH 5/5] fix(tester): treat pending orders as fresh --- apps/tester/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tester/src/index.ts b/apps/tester/src/index.ts index 8e92140..2083582 100644 --- a/apps/tester/src/index.ts +++ b/apps/tester/src/index.ts @@ -268,7 +268,7 @@ async function hasFreshMatchableOrders( if (blockNumber === undefined) { const tx = await runtime.client.getTransaction(txHash); if (!tx?.blockNumber) { - throw new Error("Block number not found for order tx"); + return true; } blockNumber = tx.blockNumber;