Skip to content

feat(coin): new coin package#415

Open
Hanssen0 wants to merge 3 commits into
ckb-devrel:next-majorfrom
Hanssen0:feat/coin
Open

feat(coin): new coin package#415
Hanssen0 wants to merge 3 commits into
ckb-devrel:next-majorfrom
Hanssen0:feat/coin

Conversation

@Hanssen0

Copy link
Copy Markdown
Member

@changeset-bot

changeset-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1b32769

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@ckb-ccc/shell Minor
@ckb-ccc/coin Minor
@ckb-ccc/ccc Patch
ckb-ccc Patch
@ckb-ccc/connector Patch
@ckb-ccc/connector-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Jun 30, 2026

Copy link
Copy Markdown

Deploy Preview for apiccc ready!

Name Link
🔨 Latest commit 1b32769
🔍 Latest deploy log https://app.netlify.com/projects/apiccc/deploys/6a55dd7f7ce0400008cd2084
😎 Deploy Preview https://deploy-preview-415--apiccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 85 (🔴 down 2 from production)
Accessibility: 100 (no change from production)
Best Practices: 100 (no change from production)
SEO: 95 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jun 30, 2026

Copy link
Copy Markdown

Deploy Preview for appccc ready!

Name Link
🔨 Latest commit 1b32769
🔍 Latest deploy log https://app.netlify.com/projects/appccc/deploys/6a55dd7f1d4703000871a551
😎 Deploy Preview https://deploy-preview-415--appccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 68 (🔴 down 17 from production)
Accessibility: 89 (🟢 up 1 from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jun 30, 2026

Copy link
Copy Markdown

Deploy Preview for liveccc ready!

Name Link
🔨 Latest commit 1b32769
🔍 Latest deploy log https://app.netlify.com/projects/liveccc/deploys/6a55dd7f9d8bf400081eda18
😎 Deploy Preview https://deploy-preview-415--liveccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 38 (no change from production)
Accessibility: 88 (no change from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jun 30, 2026

Copy link
Copy Markdown

Deploy Preview for docsccc ready!

Name Link
🔨 Latest commit 1b32769
🔍 Latest deploy log https://app.netlify.com/projects/docsccc/deploys/6a55dd7f3e90380008fe43bd
😎 Deploy Preview https://deploy-preview-415--docsccc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 80 (🔴 down 8 from production)
Accessibility: 95 (no change from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 75 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new @ckb-ccc/coin workspace package that provides a generic fungible-token (Coin) helper built on @ckb-ccc/core, and wires it into the monorepo’s testing/docs tooling and the @ckb-ccc/shell re-export surface.

Changes:

  • Added new packages/coin package (Coin implementation, tests, build/lint/test/tooling config, docs).
  • Updated root Vitest + TypeDoc configs to include packages/coin.
  • Updated @ckb-ccc/shell to depend on and re-export @ckb-ccc/coin; updated lockfile accordingly.

Reviewed changes

Copilot reviewed 21 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
vitest.config.mts Adds packages/coin to the monorepo Vitest projects list.
typedoc.config.mjs Adds packages/coin to TypeDoc workspace entry points.
pnpm-lock.yaml Adds the packages/coin importer and updates resolved dependency snapshots.
packages/shell/src/barrel.ts Re-exports the coin namespace from @ckb-ccc/coin.
packages/shell/package.json Adds @ckb-ccc/coin as a workspace dependency.
packages/coin/vitest.config.ts Adds package-local Vitest configuration for tests and coverage.
packages/coin/typedoc.json Adds package-local TypeDoc configuration for API generation.
packages/coin/tsdown.config.mts Adds tsdown build configuration for ESM+CJS outputs and copy-basedirs.
packages/coin/tsconfig.json Adds TypeScript build configuration for the coin package.
packages/coin/src/index.ts Exposes barrel exports and a coin namespace export.
packages/coin/src/coin/index.ts Implements the Coin class (balance/info helpers and transaction completion helpers).
packages/coin/src/coin/index.test.ts Adds Vitest coverage for Coin behaviors (inputs completion, change handling, balance/info helpers).
packages/coin/src/coin/error.ts Adds ErrorCoinInsufficient error type for insufficient Coin balance conditions.
packages/coin/src/coin/coinInfo.ts Adds CoinInfo aggregation helper used by the Coin implementation.
packages/coin/src/barrel.ts Re-exports the coin module surface from src/coin.
packages/coin/README.md Adds package documentation and usage examples.
packages/coin/prettier.config.cjs Adds package-local Prettier configuration.
packages/coin/package.json Defines the new @ckb-ccc/coin package metadata, exports, and scripts.
packages/coin/misc/basedirs/dist/package.json Ensures emitted ESM dist is treated as "type": "module".
packages/coin/misc/basedirs/dist.commonjs/package.json Ensures emitted CJS dist is treated as "type": "commonjs".
packages/coin/eslint.config.mjs Adds package-local ESLint + typescript-eslint configuration.
packages/coin/.prettierignore Adds package-local Prettier ignore rules.
packages/coin/.npmignore Adds package-local npm publish ignore rules.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/coin/src/coin/index.ts Outdated
Comment thread packages/coin/typedoc.json
@Hanssen0 Hanssen0 force-pushed the feat/coin branch 5 times, most recently from 9481017 to 982850a Compare June 30, 2026 08:04
@Hanssen0 Hanssen0 requested a review from Copilot June 30, 2026 08:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 23 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread packages/coin/src/coin/index.ts Outdated
Comment thread packages/coin/src/coin/index.ts Outdated
Comment thread packages/coin/src/coin/index.ts Outdated
@Hanssen0 Hanssen0 force-pushed the feat/coin branch 6 times, most recently from 0c73462 to 497132a Compare July 4, 2026 07:55
@Hanssen0 Hanssen0 changed the base branch from dev to next-major July 4, 2026 07:55
@Hanssen0 Hanssen0 requested a review from Copilot July 4, 2026 09:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 26 changed files in this pull request and generated 6 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread packages/coin/README.md
Comment thread packages/coin/README.md
Comment thread packages/coin/README.md
Comment thread packages/coin/src/coin/coin.ts
Comment thread packages/coin/src/coin/coin.ts
Comment thread packages/coin/src/coin/coin.ts
@Hanssen0 Hanssen0 force-pushed the feat/coin branch 3 times, most recently from b32dcb5 to 7c07413 Compare July 4, 2026 09:46
@Hanssen0 Hanssen0 force-pushed the feat/coin branch 2 times, most recently from ecb7009 to c7d8463 Compare July 4, 2026 11:16
@Hanssen0 Hanssen0 requested a review from Copilot July 4, 2026 11:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 26 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread packages/coin/package.json
@Hanssen0 Hanssen0 marked this pull request as ready for review July 4, 2026 13:26
@Hanssen0 Hanssen0 requested a review from Copilot July 13, 2026 17:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 30 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread packages/coin/src/xUdt/args.ts
@Hanssen0

Copy link
Copy Markdown
Member Author

/canary

@github-actions

Copy link
Copy Markdown
Contributor

❌ Canary version deployment failed. View workflow run

@Hanssen0

Copy link
Copy Markdown
Member Author

/canary

@github-actions

Copy link
Copy Markdown
Contributor

❌ Canary version deployment failed. View workflow run

@Hanssen0

Copy link
Copy Markdown
Member Author

/canary

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Canary version published successfully! View workflow run

The following packages have been published to npm:

  • @ckb-ccc/ccc@0.0.0-canary-20260713182150
  • ckb-ccc@0.0.0-canary-20260713182150
  • @ckb-ccc/co-build@0.0.0-canary-20260713182150
  • @ckb-ccc/coin@0.0.0-canary-20260713182150
  • @ckb-ccc/connector@0.0.0-canary-20260713182150
  • @ckb-ccc/connector-react@0.0.0-canary-20260713182150
  • @ckb-ccc/core@0.0.0-canary-20260713182150
  • @ckb-ccc/did-ckb@0.0.0-canary-20260713182150
  • @ckb-ccc/eip6963@0.0.0-canary-20260713182150
  • @ckb-ccc/joy-id@0.0.0-canary-20260713182150
  • @ckb-ccc/lumos-patches@0.0.0-canary-20260713182150
  • @ckb-ccc/nip07@0.0.0-canary-20260713182150
  • @ckb-ccc/okx@0.0.0-canary-20260713182150
  • @ckb-ccc/rei@0.0.0-canary-20260713182150
  • @ckb-ccc/shell@0.0.0-canary-20260713182150
  • @ckb-ccc/spore@0.0.0-canary-20260713182150
  • @ckb-ccc/ssri@0.0.0-canary-20260713182150
  • @ckb-ccc/type-id@0.0.0-canary-20260713182150
  • @ckb-ccc/udt@0.0.0-canary-20260713182150
  • @ckb-ccc/uni-sat@0.0.0-canary-20260713182150
  • @ckb-ccc/utxo-global@0.0.0-canary-20260713182150
  • @ckb-ccc/xverse@0.0.0-canary-20260713182150

@Hanssen0

Copy link
Copy Markdown
Member Author

/canary

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Canary version published successfully! View workflow run

The following packages have been published to npm:

  • @ckb-ccc/ccc@0.0.0-canary-20260713204024
  • ckb-ccc@0.0.0-canary-20260713204024
  • @ckb-ccc/co-build@0.0.0-canary-20260713204024
  • @ckb-ccc/coin@0.0.0-canary-20260713204024
  • @ckb-ccc/connector@0.0.0-canary-20260713204024
  • @ckb-ccc/connector-react@0.0.0-canary-20260713204024
  • @ckb-ccc/core@0.0.0-canary-20260713204024
  • @ckb-ccc/did-ckb@0.0.0-canary-20260713204024
  • @ckb-ccc/eip6963@0.0.0-canary-20260713204024
  • @ckb-ccc/joy-id@0.0.0-canary-20260713204024
  • @ckb-ccc/lumos-patches@0.0.0-canary-20260713204024
  • @ckb-ccc/nip07@0.0.0-canary-20260713204024
  • @ckb-ccc/okx@0.0.0-canary-20260713204024
  • @ckb-ccc/rei@0.0.0-canary-20260713204024
  • @ckb-ccc/shell@0.0.0-canary-20260713204024
  • @ckb-ccc/spore@0.0.0-canary-20260713204024
  • @ckb-ccc/ssri@0.0.0-canary-20260713204024
  • @ckb-ccc/type-id@0.0.0-canary-20260713204024
  • @ckb-ccc/udt@0.0.0-canary-20260713204024
  • @ckb-ccc/uni-sat@0.0.0-canary-20260713204024
  • @ckb-ccc/utxo-global@0.0.0-canary-20260713204024
  • @ckb-ccc/xverse@0.0.0-canary-20260713204024

@Hanssen0

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a new Coin package for creating, completing, transferring, minting, and burning fungible-token transactions.
    • Added xUDT support, including owner-mode flags, argument handling, balance queries, and transaction helpers.
    • Added Coin action support for minting, burning, and transfers.
    • Exposed Coin functionality through the Shell package.
  • Documentation
    • Added comprehensive Coin and xUDT usage documentation and API reference coverage.
  • Bug Fixes
    • Added validation and clear errors for invalid coin data and insufficient balances.

Walkthrough

This change introduces the @ckb-ccc/coin package with Coin transaction utilities, CoBuild actions, xUDT support, tests, build tooling, documentation, and public exports through @ckb-ccc/shell.

Changes

Coin package

Layer / File(s) Summary
Package foundation
packages/coin/*, typedoc.config.mjs, vitest.config.mts, packages/shell/package.json
Adds package metadata, ESM/CommonJS builds, TypeScript, linting, formatting, testing, publishing configuration, and workspace dependency wiring.
Coin contracts and public exports
packages/coin/src/coBuild.ts, packages/coin/src/coin/*, packages/coin/src/index.ts, packages/coin/src/barrel.ts
Defines Coin action entities, CoinInfo, insufficient-coin errors, and public barrel exports.
Coin discovery and transaction completion
packages/coin/src/coin/coin.ts, packages/coin/src/coin/coin.test.ts
Implements Coin balance inspection, input selection, change completion, transfers, minting, burning, capacity handling, and CoBuild integration with comprehensive tests.
xUDT arguments and CoinXUdt
packages/coin/src/xUdt/*
Adds owner-mode argument codecs, normalized xUDT construction, default known-script resolution, and xUDT-specific tests.
Documentation and shell exposure
packages/coin/README.md, packages/shell/src/barrel.ts
Documents Coin and CoinXUdt usage and re-exports the coin namespace from the shell package.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Coin
  participant Client
  participant Transaction
  participant CoBuild
  Caller->>Coin: create transfer or complete transaction
  Coin->>Client: query matching cells
  Client-->>Coin: return Coin cells and cell dependencies
  Coin->>Transaction: add inputs and change output
  Coin->>CoBuild: record transfer, mint, or burn action
  CoBuild-->>Coin: return action witness index
  Coin-->>Caller: return completed transaction
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a new coin package.
Description check ✅ Passed The description matches the required template and includes the contributing-guidelines acknowledgement.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/coin/vitest.config.ts

Parsing error: "parserOptions.project" has been provided for @typescript-eslint/parser.
The file was not found in any of the provided project(s): vitest.config.ts


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/coin/README.md`:
- Around line 123-128: Update the “Change to a specific address” README example
so it is self-contained by defining tx, or explicitly label the snippet as
continuing from the preceding Send example and add that clarification in the
code block. Preserve the existing signer.getRecommendedAddressObj and
coin.completeChangeToLock calls.

In `@packages/coin/src/coin/coin.test.ts`:
- Around line 1538-1555: Update the test around completeInputsByAmount so it
passes the negative amountTweak value of -100 when invoking the method. Preserve
the existing transaction setup and addedCount expectation, ensuring the test
specifically verifies that the negative tweak cancels the output requirement.

In `@packages/coin/src/coin/coinInfo.ts`:
- Around line 49-52: Update CoinInfo.from so that when infoLike is already a
CoinInfo, it returns infoLike.clone() rather than the original reference.
Preserve the existing conversion behavior for other CoinInfoLike inputs and
maintain the documented guarantee that from always returns a new instance.

In `@packages/coin/src/xUdt/args.ts`:
- Around line 42-60: Update CoinXUdtArgsCodec and CoinXUdtArgs so xUDT extension
payloads are preserved during decode and re-encode, modeling extensionData
explicitly if supported; otherwise validate flags and encoded lengths and reject
unsupported extension data instead of silently emitting only the owner hash and
four flag bytes.

In `@packages/coin/src/xUdt/coinXUdt.ts`:
- Around line 99-115: Update the CoinXUdt constructor’s argument handling around
CoinXUdtArgs.fromBytes so extended bytes from options.script.args are preserved
when rebuilding the script, rather than always replacing them with
args.toBytes(). Keep canonical serialization for options.xUdtArgs while
retaining the original script argument bytes for the script-derived path, and
add a regression test using the extended arguments covered in xUdt.test.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b179b27b-6b93-41c2-8a16-7fa1df39ce82

📥 Commits

Reviewing files that changed from the base of the PR and between 8076f62 and 1b32769.

⛔ Files ignored due to path filters (2)
  • packages/coin/misc/basedirs/dist/package.json is excluded by !**/dist/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (28)
  • .changeset/early-oranges-lick.md
  • packages/coin/.npmignore
  • packages/coin/.prettierignore
  • packages/coin/README.md
  • packages/coin/eslint.config.mjs
  • packages/coin/misc/basedirs/dist.commonjs/package.json
  • packages/coin/package.json
  • packages/coin/prettier.config.cjs
  • packages/coin/src/barrel.ts
  • packages/coin/src/coBuild.ts
  • packages/coin/src/coin/coin.test.ts
  • packages/coin/src/coin/coin.ts
  • packages/coin/src/coin/coinInfo.ts
  • packages/coin/src/coin/error.ts
  • packages/coin/src/coin/index.ts
  • packages/coin/src/index.ts
  • packages/coin/src/xUdt/args.ts
  • packages/coin/src/xUdt/coinXUdt.ts
  • packages/coin/src/xUdt/index.ts
  • packages/coin/src/xUdt/xUdt.test.ts
  • packages/coin/tsconfig.json
  • packages/coin/tsdown.config.mts
  • packages/coin/typedoc.json
  • packages/coin/vitest.config.ts
  • packages/shell/package.json
  • packages/shell/src/barrel.ts
  • typedoc.config.mjs
  • vitest.config.mts

Comment thread packages/coin/README.md
Comment on lines +123 to +128
### Change to a specific address

```ts
const { script: changeLock } = await signer.getRecommendedAddressObj();
const { tx: completedTx } = await coin.completeChangeToLock(tx, changeLock);
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the change-address example self-contained or explicitly mark it as a continuation.

This snippet uses tx without defining it in the same example. Readers who copy only this section will encounter an undefined identifier; either include the transaction construction or label the section as continuing from the previous example.

Suggested clarification
-### Change to a specific address
+### Change to a specific address (using the `tx` from the Send example above)

 ```ts
+// `tx` is created in the Send example above.
 const { script: changeLock } = await signer.getRecommendedAddressObj();
 const { tx: completedTx } = await coin.completeChangeToLock(tx, changeLock);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Change to a specific address
```ts
const { script: changeLock } = await signer.getRecommendedAddressObj();
const { tx: completedTx } = await coin.completeChangeToLock(tx, changeLock);
```
### Change to a specific address (using the `tx` from the Send example above)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/coin/README.md` around lines 123 - 128, Update the “Change to a
specific address” README example so it is self-contained by defining tx, or
explicitly label the snippet as continuing from the preceding Send example and
add that clarification in the code block. Preserve the existing
signer.getRecommendedAddressObj and coin.completeChangeToLock calls.

Comment on lines +1538 to +1555
it("should add no inputs when amountTweak exactly cancels the output requirement", async () => {
// Output needs 100, but amountTweak is -100 (negative tweak zeroing requirement)
const tx = ccc.Transaction.from({
inputs: [
{
previousOutput: {
txHash: `0x${"c".repeat(63)}0`,
index: 0,
},
},
],
outputs: [{ lock, type }],
outputsData: [ccc.numLeToBytes(100, 16)],
});

// Already have 1 input (amount 100) matching output exactly → no more needed
const { addedCount } = await coin.completeInputsByAmount(tx);
expect(addedCount).toBe(0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Actually exercise the negative amountTweak.

The existing input already balances the output, and the call omits -100, so this test passes without covering its stated edge case.

Proposed fix
 const tx = ccc.Transaction.from({
-  inputs: [
-    {
-      previousOutput: {
-        txHash: `0x${"c".repeat(63)}0`,
-        index: 0,
-      },
-    },
-  ],
   outputs: [{ lock, type }],
   outputsData: [ccc.numLeToBytes(100, 16)],
 });

-const { addedCount } = await coin.completeInputsByAmount(tx);
+const { addedCount } = await coin.completeInputsByAmount(tx, -100);
 expect(addedCount).toBe(0);
+expect(tx.inputs).toHaveLength(0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("should add no inputs when amountTweak exactly cancels the output requirement", async () => {
// Output needs 100, but amountTweak is -100 (negative tweak zeroing requirement)
const tx = ccc.Transaction.from({
inputs: [
{
previousOutput: {
txHash: `0x${"c".repeat(63)}0`,
index: 0,
},
},
],
outputs: [{ lock, type }],
outputsData: [ccc.numLeToBytes(100, 16)],
});
// Already have 1 input (amount 100) matching output exactly → no more needed
const { addedCount } = await coin.completeInputsByAmount(tx);
expect(addedCount).toBe(0);
it("should add no inputs when amountTweak exactly cancels the output requirement", async () => {
// Output needs 100, but amountTweak is -100 (negative tweak zeroing requirement)
const tx = ccc.Transaction.from({
outputs: [{ lock, type }],
outputsData: [ccc.numLeToBytes(100, 16)],
});
// Already have 1 input (amount 100) matching output exactly → no more needed
const { addedCount } = await coin.completeInputsByAmount(tx, -100);
expect(addedCount).toBe(0);
expect(tx.inputs).toHaveLength(0);
🧰 Tools
🪛 ESLint

[error] 1540-1551: Unsafe assignment of an error typed value.

(@typescript-eslint/no-unsafe-assignment)


[error] 1540-1540: Unsafe call of a type that could not be resolved.

(@typescript-eslint/no-unsafe-call)


[error] 1549-1549: Unsafe assignment of an error typed value.

(@typescript-eslint/no-unsafe-assignment)


[error] 1549-1549: Unsafe assignment of an error typed value.

(@typescript-eslint/no-unsafe-assignment)


[error] 1550-1550: Unsafe call of a type that could not be resolved.

(@typescript-eslint/no-unsafe-call)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/coin/src/coin/coin.test.ts` around lines 1538 - 1555, Update the
test around completeInputsByAmount so it passes the negative amountTweak value
of -100 when invoking the method. Preserve the existing transaction setup and
addedCount expectation, ensuring the test specifically verifies that the
negative tweak cancels the output requirement.

Comment on lines +49 to +52
static from(infoLike?: CoinInfoLike) {
if (infoLike instanceof CoinInfo) {
return infoLike;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Avoid aliasing the input from CoinInfo.from.

Line 50 returns an existing CoinInfo by reference, despite the documentation promising a new instance. CoinInfo.from(existing).addAssign(...) can therefore mutate the caller’s object. Return infoLike.clone() or explicitly document and test the identity-preserving behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/coin/src/coin/coinInfo.ts` around lines 49 - 52, Update
CoinInfo.from so that when infoLike is already a CoinInfo, it returns
infoLike.clone() rather than the original reference. Preserve the existing
conversion behavior for other CoinInfoLike inputs and maintain the documented
guarantee that from always returns a new instance.

Comment on lines +42 to +60
/**
* Optional xUDT flags encoded as a little-endian uint32.
*
* Defaults to `0`. Use {@link CoinXUdtFlags} for the standard owner-mode
* flags. Lower bits are accepted and preserved without local interpretation.
*/
flags?: number | null;
};

const CoinXUdtArgsCodec = ccc.Codec.from({
encode(value) {
const args = CoinXUdtArgs.from(value);
const ownerScriptHash = ccc.bytesFrom(args.ownerScriptHash);

if (args.flags === 0) {
return ownerScriptHash;
}

return ccc.bytesConcat(ownerScriptHash, ccc.numLeToBytes(args.flags, 4));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve xUDT extension data or reject unsupported flags. CoinXUdtArgsCodec only round-trips the owner hash and 4 flag bytes, so xUDT args that need extension payloads will be changed on re-encode. Either model extensionData explicitly or fail on unsupported flags/lengths.

🧰 Tools
🪛 ESLint

[error] 51-51: Unsafe call of a type that could not be resolved.

(@typescript-eslint/no-unsafe-call)


[error] 53-53: Unsafe argument of type any assigned to a parameter of type CoinXUdtArgsLike.

(@typescript-eslint/no-unsafe-argument)


[error] 54-54: Unsafe assignment of an error typed value.

(@typescript-eslint/no-unsafe-assignment)


[error] 54-54: Unsafe call of a type that could not be resolved.

(@typescript-eslint/no-unsafe-call)


[error] 57-57: Unsafe return of a value of type error.

(@typescript-eslint/no-unsafe-return)


[error] 60-60: Unsafe return of a value of type error.

(@typescript-eslint/no-unsafe-return)


[error] 60-60: Unsafe call of a type that could not be resolved.

(@typescript-eslint/no-unsafe-call)


[error] 60-60: Unsafe call of a type that could not be resolved.

(@typescript-eslint/no-unsafe-call)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/coin/src/xUdt/args.ts` around lines 42 - 60, Update
CoinXUdtArgsCodec and CoinXUdtArgs so xUDT extension payloads are preserved
during decode and re-encode, modeling extensionData explicitly if supported;
otherwise validate flags and encoded lengths and reject unsupported extension
data instead of silently emitting only the owner hash and four flag bytes.

Comment on lines +99 to +115
let args;
if (options.xUdtArgs != null) {
args = CoinXUdtArgs.from(options.xUdtArgs);
} else if (options.script?.args != null) {
args = CoinXUdtArgs.fromBytes(options.script.args);
} else {
throw new Error(
"Either xUdtArgs or script.args must be provided for CoinXUdt",
);
}

super({
...options,
script: {
...(options.script ?? {}),
args: args.toBytes(),
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve existing script.args extension bytes.

fromBytes accepts extended args, but args.toBytes() strips those bytes. This changes the type script identity, causing balance discovery and transactions to target a different xUDT.

Proposed fix
   constructor(options: CoinXUdtOptions) {
     let args;
+    let scriptArgs;
     if (options.xUdtArgs != null) {
       args = CoinXUdtArgs.from(options.xUdtArgs);
+      scriptArgs = args.toBytes();
     } else if (options.script?.args != null) {
       args = CoinXUdtArgs.fromBytes(options.script.args);
+      scriptArgs = options.script.args;
     } else {
       throw new Error(
         "Either xUdtArgs or script.args must be provided for CoinXUdt",
       );
     }

     super({
       ...options,
       script: {
         ...(options.script ?? {}),
-        args: args.toBytes(),
+        args: scriptArgs,
       },

Add a constructor regression test using the extended args already covered in packages/coin/src/xUdt/xUdt.test.ts Lines 63-68.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let args;
if (options.xUdtArgs != null) {
args = CoinXUdtArgs.from(options.xUdtArgs);
} else if (options.script?.args != null) {
args = CoinXUdtArgs.fromBytes(options.script.args);
} else {
throw new Error(
"Either xUdtArgs or script.args must be provided for CoinXUdt",
);
}
super({
...options,
script: {
...(options.script ?? {}),
args: args.toBytes(),
},
let args;
let scriptArgs;
if (options.xUdtArgs != null) {
args = CoinXUdtArgs.from(options.xUdtArgs);
scriptArgs = args.toBytes();
} else if (options.script?.args != null) {
args = CoinXUdtArgs.fromBytes(options.script.args);
scriptArgs = options.script.args;
} else {
throw new Error(
"Either xUdtArgs or script.args must be provided for CoinXUdt",
);
}
super({
...options,
script: {
...(options.script ?? {}),
args: scriptArgs,
},
🧰 Tools
🪛 ESLint

[error] 103-103: Unsafe assignment of an error typed value.

(@typescript-eslint/no-unsafe-assignment)


[error] 103-103: Unsafe call of a type that could not be resolved.

(@typescript-eslint/no-unsafe-call)


[error] 112-115: Unsafe assignment of an any value.

(@typescript-eslint/no-unsafe-assignment)


[error] 114-114: Unsafe assignment of an error typed value.

(@typescript-eslint/no-unsafe-assignment)


[error] 114-114: Unsafe call of a type that could not be resolved.

(@typescript-eslint/no-unsafe-call)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/coin/src/xUdt/coinXUdt.ts` around lines 99 - 115, Update the
CoinXUdt constructor’s argument handling around CoinXUdtArgs.fromBytes so
extended bytes from options.script.args are preserved when rebuilding the
script, rather than always replacing them with args.toBytes(). Keep canonical
serialization for options.xUdtArgs while retaining the original script argument
bytes for the script-derived path, and add a regression test using the extended
arguments covered in xUdt.test.ts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants