Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0450138
feat: add fiber-sdk module
ashuralyk Mar 31, 2025
fb2e6bf
chore: add framework of fiber json rpc client
ashuralyk Apr 2, 2025
3c3d62f
feat:add fiber method and test
sfsf332 Apr 2, 2025
60f99a6
feat:add channel,info test code
sfsf332 Apr 8, 2025
71b857f
feat:fiber sdk demo init
sfsf332 Apr 9, 2025
49d9a3a
feat:fiber sdk &demo v0.1
sfsf332 Apr 9, 2025
a86f10d
feat:add readme
sfsf332 Apr 9, 2025
98bfe0e
feat:fiber sdk test page
sfsf332 Apr 16, 2025
42ceba3
chore: code format and remove chinesecomments
ashuralyk Apr 16, 2025
461cfb2
fix:pritter error
sfsf332 Apr 16, 2025
d5f5c3b
chore: create changeset
ashuralyk Apr 19, 2025
ca785b6
chore: update lock file
ashuralyk Apr 19, 2025
e2e2886
fix:
sfsf332 May 21, 2025
cb99c39
fix: 修复通道关闭时的参数类型错误
sfsf332 May 21, 2025
6f38082
chore: align old interfaces with latest documentation
ashuralyk Feb 4, 2026
6fbabe3
feat: slightly refactor code structures, leaving examples to complete
ashuralyk Feb 5, 2026
034e02b
feat: update to v0.6.1
ashuralyk Feb 11, 2026
f129e1c
feat: introduce fiber-js
ashuralyk Feb 22, 2026
b54c623
feat: reconstruct fiber test cases
ashuralyk Feb 22, 2026
bfa457e
feat: temporary pass all fiber test cases
ashuralyk Feb 22, 2026
7297bfc
feat: start two different fiber nodes for test
ashuralyk Feb 22, 2026
2f528e6
feat: add failure scenario test cases
ashuralyk Feb 23, 2026
97ae3f0
feat: accomplish a basic usable version of fiber-sdk
ashuralyk Feb 23, 2026
52be5ca
chore: beatify code visual effect
ashuralyk Feb 23, 2026
34c1a20
feat: clone all types from fiber-js because name case style
ashuralyk Feb 23, 2026
222da27
feat: make code more adapt to ccc-style
ashuralyk Feb 23, 2026
98e9809
chore: simplize fiber test preparation code
ashuralyk Feb 23, 2026
33732b5
feat: add node info api
ashuralyk Feb 24, 2026
33e712f
feat: add fiber peer feature
ashuralyk Feb 24, 2026
2aa8c28
chore: erase useless
ashuralyk Feb 24, 2026
67ef668
chore: inject mini-game for fiber
ashuralyk Mar 4, 2026
a5c1680
feat: seperate fiber config
ashuralyk Mar 9, 2026
ed16d40
chore: pass fiber test cases even in case of failed responses
ashuralyk Mar 10, 2026
3d3a35d
feat: move fiber-wasm runtime under test cases
ashuralyk Mar 19, 2026
4e0c3d8
chore: adjust to kill gemini review reported items
ashuralyk Mar 22, 2026
815f7ed
chore: solve worker-idb-wrapper warning and update README.md
ashuralyk Mar 22, 2026
e0f89ac
chore: supply changeset file
ashuralyk Mar 24, 2026
8ac953e
chore: no playable presentation
ashuralyk Mar 26, 2026
847fadb
chore: update pnpm-workspace
ashuralyk Mar 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/blue-aliens-ring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@ckb-ccc/fiber": patch
---

Abstract channel, invoice and payment methods of Fiber

6 changes: 6 additions & 0 deletions .changeset/polite-pianos-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@ckb-ccc/fiber": major
---

Initial fiber-sdk module

5 changes: 5 additions & 0 deletions .changeset/unlucky-tools-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ckb-ccc/fiber": patch
---

Wrap fiber RPCs as fiber-sdk into CCC, with playable presentation
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ packages/*/dist*
.env*

*.DS_Store

.tmp-fiber-js
.pnpm-store
21 changes: 21 additions & 0 deletions packages/fiber/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
node_modules/
misc/

*test.js
*test.ts
*test.d.ts
*test.d.ts.map
*spec.js
*spec.ts
*spec.d.ts
*spec.d.ts.map

tsconfig.json
tsconfig.*.json
eslint.config.mjs
.prettierrc
.prettierignore

tsconfig.tsbuildinfo
tsconfig.*.tsbuildinfo
.github/
12 changes: 12 additions & 0 deletions packages/fiber/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules/

dist/
dist.commonjs/

.npmignore
.prettierrc
tsconfig.json
eslint.config.mjs

tsconfig.tsbuildinfo
.github/
5 changes: 5 additions & 0 deletions packages/fiber/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"singleQuote": false,
"trailingComma": "all",
"plugins": ["prettier-plugin-organize-imports"]
}
Empty file added packages/fiber/CHANGELOG.md
Empty file.
209 changes: 209 additions & 0 deletions packages/fiber/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
# Fiber SDK

A TypeScript/JavaScript SDK for building on [Fiber](https://github.com/nervosnetwork/fiber)—the Nervos payment channel network. One client, one config, and typed methods for channels, invoices, payments, node info, and peers. Built for the [CCC](https://github.com/ckb-devrel/ccc) stack with camelCase APIs and full type exports.

---

## Why use it

- **Single entry point** — `FiberSDK` wraps Fiber JSON-RPC so you don’t deal with raw RPC or snake_case.
- **TypeScript-first** — Params and results are typed; import `Channel`, `PaymentResult`, `NewInvoiceParamsLike`, etc. from the package.
- **Minimal surface** — Channels, invoices, payments, plus node info and peer management.
- **Fiber-native** — Talks to a running Fiber node over HTTP; no extra runtimes. Use it in Node or the browser.

---

## Install

```bash
npm install @ckb-ccc/fiber
```

---

## Quick start

Create a client and point it at your Fiber node:

```ts
import { FiberSDK } from "@ckb-ccc/fiber";

const sdk = new FiberSDK({
endpoint: "http://127.0.0.1:8227",
timeout: 5000, // optional, milliseconds
});
```

Then use the same `sdk` for channels, invoices, and payments:

```ts
const channels = await sdk.listChannels();
const { invoiceAddress, invoice } = await sdk.newInvoice({
amount: "0x5f5e100", // 100000000 decimal
currency: "Fibt",
paymentPreimage: "0x" + "01".repeat(32), // 32 bytes; each byte is 0x01 = 1 decimal (example preimage)
description: "Coffee",
expiry: "0xe10", // 3600 decimal
finalExpiryDelta: "0x9283C0", // 9601984 decimal
});
Comment on lines +41 to +48
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The code examples use magic hex strings for values like amount, expiry, and finalExpiryDelta. This makes the code harder to understand. It would be more readable if you either used decimal numbers with comments or defined constants with descriptive names to represent these values.

const payment = await sdk.sendPayment({ invoice: invoiceAddress });
```

That’s the core loop: create an invoice (always pass `paymentPreimage`; optional `paymentHash` is available on `NewInvoiceParamsLike` for hold-style setups), share `invoiceAddress`, and the payer calls `sendPayment` with it.

---

## SDK at a glance

| Domain | What it does |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **Channel** | List channels, open and accept, shut down or abandon, update channel options (`sdk.channel.updateChannel`). |
| **Invoice** | Create, parse, get or cancel by payment hash, settle with preimage (`sdk.invoice.settleInvoice`). |
| **Payment** | Send a payment, get status by hash, build a router, send with a pre-built router. |
| **Info** | Node metadata via `getNodeInfo()` (`node_info` RPC). |
| **Peer** | `connectPeer`, `disconnectPeer`, `listPeers`. |

Most calls exist both as **top-level methods** on `FiberSDK` and on **domain objects** `sdk.channel`, `sdk.invoice`, `sdk.payment`, `sdk.info`, and `sdk.peer`. Use the domain objects when a method is only there (e.g. `acceptChannel`, `buildRouter`, `settleInvoice`, `updateChannel`). Types are exported so you can annotate your code with `Channel`, `PaymentResult`, `NewInvoiceParamsLike`, `SettleInvoiceParamsLike`, and so on.

---

## Node info and peers

**Node**

```ts
const info = await sdk.getNodeInfo();
// info.version, info.nodeId, info.addresses, ...
```

**Peers**

```ts
await sdk.connectPeer({ address: "/ip4/127.0.0.1/tcp/8228", save: true });
const peers = await sdk.listPeers();
await sdk.disconnectPeer({ peerId: "<peer id>" });
```

---

## Channels

Channels are the links between your node and others. You list them, open new ones toward a peer, and either shut them down cleanly (when they’re open) or abandon them (e.g. when an open never completed).

**List and open**

```ts
const channels = await sdk.listChannels();
// Optional filter: sdk.listChannels({ includeClosed: false })

const temporaryChannelId = await sdk.openChannel({
peerId: "<peer multiaddr or id>",
fundingAmount: "0xba43b7400", // 50000000000 decimal
public: true,
});
```

The counterparty accepts with `sdk.channel.acceptChannel({ temporaryChannelId, fundingAmount, ... })` and gets back the final `channelId`. You use that `channelId` later to shut down or update the channel.

**Shut down, abandon, or update**

```ts
await sdk.shutdownChannel({
channelId: readyChannelId,
feeRate: "0x3FC", // 1020 decimal
force: false,
});

await sdk.abandonChannel({ channelId: temporaryChannelId });

await sdk.channel.updateChannel({
channelId: readyChannelId,
enabled: true,
});
```

Use `shutdownChannel` for an established channel you want to close; use `abandonChannel` for a channel that never reached “ready” (e.g. a stuck open). `updateChannel` is only on `sdk.channel` (not duplicated as a top-level `FiberSDK` method).

---

## Invoices

Invoices represent a request to be paid: amount, currency, **payment preimage** (or optional `paymentHash` for hold invoices), optional description and expiry. You create one, share the **invoice address**, and the payer pays to that address.

**Create and share**

```ts
const { invoiceAddress, invoice } = await sdk.newInvoice({
amount: "0x5f5e100", // 100000000 decimal
currency: "Fibt",
paymentPreimage: "0x" + "01".repeat(32), // 32 bytes; each byte is 0x01 = 1 decimal (example preimage)
description: "Order #42",
expiry: "0xe10", // 3600 decimal
finalExpiryDelta: "0x9283C0", // 9601984 decimal
});
// Send invoiceAddress to the payer (e.g. QR or link).
// Keep invoice.data.paymentHash to look up or cancel later.
```

See `NewInvoiceParamsLike` in `src/types/invoice.ts` for optional `paymentHash`, `allowMpp`, UDT script, and other fields.

**Parse, get, cancel**

```ts
const parsed = await sdk.parseInvoice({ invoice: invoiceAddress });
const info = await sdk.getInvoice(invoice.data.paymentHash);
await sdk.cancelInvoice(invoice.data.paymentHash);
```

`parseInvoice` on `FiberSDK` takes `{ invoice: string }` and returns a `CkbInvoice` (the API layer returns `{ invoice }` as `ParseInvoiceResult`).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The explanation of how parseInvoice works on the API layer is an implementation detail that might be confusing for an SDK user. To improve clarity, I suggest removing the parenthetical part and focusing only on the behavior of the FiberSDK method.

Suggested change
`parseInvoice` on `FiberSDK` takes `{ invoice: string }` and returns a `CkbInvoice` (the API layer returns `{ invoice }` as `ParseInvoiceResult`).
`parseInvoice` on `FiberSDK` takes `{ invoice: string }` and returns a `CkbInvoice`.


Settling an invoice with a preimage is done via `sdk.invoice.settleInvoice({ paymentHash, paymentPreimage })` (typed as `SettleInvoiceParamsLike`).

---

## Payments

Payments are “send money to this invoice.” You pass the invoice address (or a pre-built router via `sdk.payment`), and the node figures out routing and state updates.

**Send and track**

```ts
const result = await sdk.sendPayment({
invoice: invoiceAddress,
});
// result.paymentHash, result.status, result.fee, ...

const status = await sdk.getPayment(result.paymentHash);
```

If you prefer to build the route yourself, use `sdk.payment.buildRouter(...)` and `sdk.payment.sendPaymentWithRouter(...)`. Params and results are typed (`SendPaymentCommandParamsLike`, `PaymentResult`, etc.).

---

## Basic usage flows

**Flow 1: Receive payment**
Create an invoice with `newInvoice` (include `paymentPreimage` or `paymentHash` per your flow), expose `invoiceAddress` to the payer. They call `sendPayment({ invoice: invoiceAddress })`. You can poll or use `getInvoice(paymentHash)` / `getPayment(paymentHash)` as needed.

**Flow 2: Send payment**
Obtain an invoice string (e.g. from the receiver). Optionally `parseInvoice({ invoice })` to inspect it. Call `sendPayment({ invoice })`. Use `getPayment(paymentHash)` to check status and fee.

**Flow 3: Channel lifecycle**
Open with `openChannel`; the other side calls `acceptChannel`. Once ready, use `listChannels` to see state and balances. To close, use `shutdownChannel`; if the channel never became ready, use `abandonChannel`. Adjust live settings with `sdk.channel.updateChannel` when supported.

---

## TypeScript and exports

- All APIs use **camelCase** (e.g. `paymentHash`, `invoiceAddress`). `FiberClient` converts params with `camelToSnake` and responses with `snakeToCamel` (see `utils.ts`). Bigint and number values in RPC params are serialized to hex strings.
- Import **types** from the package: `Channel`, `PaymentResult`, `NewInvoiceParamsLike`, `FiberClient`, etc.
- Besides `FiberSDK`, the package exports `ChannelApi`, `InvoiceApi`, `PaymentApi`, `InfoApi`, `PeerApi`, and `FiberClient` for custom wiring, plus helpers from `utils.ts` (`camelToSnake`, `snakeToCamel`, `toHex`).

For full type definitions and optional parameters, use your editor’s IntelliSense or browse `packages/fiber/src/types/`.

---

## Learn more

- [Fiber](https://github.com/nervosnetwork/fiber) — Node implementation and RPC.
- [CCC](https://github.com/ckb-devrel/ccc) — CKBer’s Codebase / Common Chains Connector.
47 changes: 47 additions & 0 deletions packages/fiber/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// @ts-check

import eslint from "@eslint/js";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
import tseslint from "typescript-eslint";

import { dirname } from "path";
import { fileURLToPath } from "url";

export default [
...tseslint.config({
files: ["**/*.ts"],
extends: [
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
],
rules: {
"@typescript-eslint/no-unused-vars": [
"error",
{
args: "all",
argsIgnorePattern: "^_",
caughtErrors: "all",
caughtErrorsIgnorePattern: "^_",
destructuredArrayIgnorePattern: "^_",
varsIgnorePattern: "^_",
ignoreRestSiblings: true,
},
],
"@typescript-eslint/unbound-method": ["error", { ignoreStatic: true }],
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/require-await": "off",
"no-empty": "off",
"prefer-const": [
"error",
{ ignoreReadBeforeAssign: true, destructuring: "all" },
],
},
languageOptions: {
parserOptions: {
project: true,
tsconfigRootDir: dirname(fileURLToPath(import.meta.url)),
},
},
}),
eslintPluginPrettierRecommended,
];
3 changes: 3 additions & 0 deletions packages/fiber/misc/basedirs/dist.commonjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "commonjs"
}
3 changes: 3 additions & 0 deletions packages/fiber/misc/basedirs/dist/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
62 changes: 62 additions & 0 deletions packages/fiber/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"name": "@ckb-ccc/fiber",
"version": "0.1.0",
"type": "module",
"description": "CCC - CKBer's Codebase. Common Chains Connector's support for Fiber SDK",
"author": "ashuralyk <ashuralyk@live.com>",
"license": "MIT",
"private": false,
"homepage": "https://github.com/ckb-devrel/ccc",
"repository": {
"type": "git",
"url": "git://github.com/ckb-devrel/ccc.git"
},
"sideEffects": false,
"main": "dist.commonjs/index.js",
"module": "dist/index.js",
"browser": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist.commonjs/index.js",
"default": "./dist.commonjs/index.js"
}
},
"scripts": {
"build": "rimraf ./dist && rimraf ./dist.commonjs && tsc && tsc --project tsconfig.commonjs.json && copyfiles -u 2 misc/basedirs/**/* .",
"lint": "eslint ./src",
"format": "prettier --write . && eslint --fix ./src",
"test": "vitest",
"test:ci": "vitest run"
},
"devDependencies": {
"@eslint/js": "^9.34.0",
"@nervosnetwork/fiber-js": "^0.7.0",
"@noble/curves": "^1.7.0",
"@types/node": "^24.3.0",
"bs58": "^6.0.0",
"copyfiles": "^2.4.1",
"dotenv": "^17.2.1",
"eslint": "^9.34.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"fake-indexeddb": "^6.2.5",
"prettier": "^3.6.2",
"prettier-linter-helpers": "^1.0.1",
"prettier-plugin-organize-imports": "^4.2.0",
"rimraf": "^6.0.1",
"tsx": "^4.20.5",
"typescript": "^5.9.2",
"typescript-eslint": "^8.41.0",
"vitest": "^3.2.4",
"web-worker": "^1.5.0"
},
"publishConfig": {
"access": "public"
},
"dependencies": {
"@ckb-ccc/core": "workspace:*"
}
}
Loading
Loading