Pckt: A Friendly Way to Send CKB | pckt:一种友好的发送 CKB 的方式

pckt is a red-packet style CKB app. A sender seals CKB into a single on-chain packet cell, shares a link, and recipients claim from that packet until it is exhausted or expires. If the packet expires before all claims are taken, the sender can reclaim the remainder

For now, this is a solo-built project. I have tried to keep the architecture small, inspectable, and easy to maintain without requiring a large ops or protocol team.

Problem and Solution

The problem

Sending tokens on-chain is usually transactional, but not ceremonial. Red packets are social. They are meant to be shared into a group, opened by multiple recipients, and claimed with a sense of timing, surprise, and urgency.

Most wallet transfer flows do not capture that behavior well. They support “send X to Y,” but not:

  • one sender, many recipients
  • partial sequential claiming
  • time-gated opening
  • “claim by link” behavior
  • reclaiming unclaimed balance after expiry

The solution

pckt models the packet as a single live CKB cell governed by a custom lock script. Instead of keeping packet state off-chain, every claim consumes the packet cell and produces its next version. That lets the chain remain the source of truth while the frontend and backend make the experience feel native.

Hosted solution: https://sendpckt.robaireth.dev
Repo: GitHub - RobaireTH/pckt: The friendliest way to send CKB · GitHub

The core user flow is:

  1. Sender seals a packet on-chain.

  2. Sender shares a claim link.

  3. Recipients claim against the packet cell.

  4. The packet shrinks as claims happen.

  5. If claims are exhausted, the packet terminates.

  6. If time expires first, the sender can reclaim the remainder.

Technical Approaches and Design Decisions

1. One contract, one packet cell

The main design decision is that a packet is not an account, and it is not a backend object. It is a single live cell with explicit, serialized state.

That gives a few advantages:

  • easier audit surface than a larger state machine

  • no server custody

  • claims are naturally serialized by cell consumption

  • each state transition is verifiable from chain history

2. Frontend signs the social flow, backend indexes the chain


The system is split into three parts:

  • Contract: Rust CKB lock script for claim and reclaim paths

  • Frontend: React + TypeScript app using CCC to build and sign transactions

  • Backend: Rust + Axum + SQLite indexer and UX helper

The backend is intentionally non-authoritative. It does not hold keys, does not create truth, and can be replaced. Its role is to index packet state, provide sender/claimer views, short links, and live UX data.

3. Claim links put the secret in the fragment

The claim secret lives in the URL fragment, not in backend storage. That means the browser can hold the secret client-side while the backend only sees the public short-link target. This is an important design boundary: the server helps with UX, but it should not become a custody or secret-management layer.

4. Testnet-first, with deliberate constraints

I explicitly kept this testnet-first and avoided pretending every mode is ready for production. In particular:

  • reclaim exists on-chain and is now wired into the UI
  • sender naming is currently backend-stored and convenience-oriented, not yet decentralized identity
  • the “lucky split” mode is constrained by payout math and safety concerns

CKB Ecosystem Relevance

pckt is relevant to CKB because it leans into what cells are good at:

  • explicit state

  • composable lock logic

  • sequential consumption and recreation

  • custom social/financial flows without custodial middleware

I believe it’s of benefit to the wider ecosystem as it is a good initiative to introduce people to the Nervos CKB by bringing in centuries old traditions onchain. A simple sharing of love!

Deployment Architecture

Current deployment shape:

  • Frontend: Vite app deployed separately
  • Backend: Fly.io app
  • Database: SQLite on Fly volume
  • Chain target: CKB testnet (Pudge)

The backend runs as:

  • packet indexer

  • packet/query API

  • short-link service

  • price helper

  • relay endpoint for signed transactions

The frontend is configured by environment variables with:

  • testnet RPC URL
  • active contract code hash
  • contract outpoint
  • backend URL

Security Considerations

1. Self-custody

Funds are locked on-chain. The backend never has authority to move them.

2. Non-authoritative backend

The backend is replaceable and should be treated as UX infrastructure, not a trust anchor. It’s even the contract that rejects multiple claims, not the backend.

3. Claim secret handling

The claim secret sits in the URL fragment so it is not automatically sent to servers. This avoids a major class of accidental backend custody.

4. Reorg and index correctness

The backend includes reorg handling and block-hash rollback logic because packet state derived from chain history must survive reorgs cleanly.

5. Explicitly bounded feature set

I do not want unsafe “it sort of works” behavior around payout math or reclaim timing. Where a mode looks questionable, I prefer to restrict it until it is clean.

Why I Don’t Like the Maths Behind Lucky Splits For Now

I do not dislike the idea of lucky splits. I dislike the current math and operational risk profile of lucky splits in this implementation.

The problem is: once you move from fixed shares to random shares, you need to guarantee that:

  • each claim remains above minimum live-cell capacity

  • enough capacity is reserved for future claims

  • the final claim and reclaim paths still behave safely

  • the user-facing preview matches what on-chain logic will actually allow

That is easy to get subtly wrong.

In practice, lucky mode becomes a product-security problem, not just a product-design problem. If the random lower bound is too low, you can create unclaimable outputs. If the reservation math is too optimistic, you can strand capacity. If frontend prediction diverges from contract math, users get misleading expectations.

For now, I prefer the stricter stance:

  • fixed and timed-fixed remain usable
  • lucky mode is something I would only re-enable once I am satisfied that the lower-bound math, reservation rules, and UI expectations are all aligned

I would rather disappoint users with a disabled mode than lock them into a half-correct one.

Maintenance Plan

I plan to use this Talk Nervos thread itself as part of the maintenance loop.

Specifically:

  • I will post updates there as features or fixes land
  • I will note contract redeploys and testnet config changes here
  • I will use the thread to surface known limitations honestly
  • I will treat user reports in the thread as a lightweight public changelog and maintenance channel

The near-term maintenance priorities are:

  1. stabilize sender/claimer UX

  2. keep reclaim flow reliable and understandable

  3. tighten profile/name semantics

  4. revisit lucky split math only when it is safe

  5. continue testnet usage before thinking seriously about mainnet

Closing

pckt is an attempt to build something small, social, and unmistakably native to CKB’s cell model. It is not trying to be a generic wallet feature (Though it can be). It is trying to make a familiar ritual feel right on-chain.

If that resonates, I’d love feedback moat importantly on whether the identity layer should remain simple or move toward a more formal DID-based model later.


ZH


pckt 是一个类似红包的 CKB 应用。发送者将 CKB 封装成一个链上数据包,分享一个链接,接收者可以从该数据包中认领代币,直到数据包耗尽或过期。如果数据包在所有代币被认领之前过期,发送者可以重新认领剩余的代币。

目前,这是一个由我独立开发的项目。我努力保持架构的简洁、易于检查和维护,无需庞大的运维或协议团队。

问题与解决方案

问题

链上发送代币通常是交易性的,但缺乏仪式感。红包则具有社交性。它们应该被分享给一群人,由多个接收者打开,并以一种带有时机感、惊喜感和紧迫感的方式认领。

大多数钱包的转账流程都无法很好地体现这种行为。他们支持“向 Y 发送 X”,但不支持:

  • 一个发送方,多个接收方

  • 部分顺序声明

  • 时间门控开启

  • “通过链接声明”行为

  • 过期后回收未声明余额

解决方案

pckt 将数据包建模为由自定义锁定脚本控制的“单个活跃 CKB 单元”。数据包状态并非保存在链下,而是每次声明都会消耗数据包单元并生成其下一个版本。这使得链始终保持真实性,同时前端和后端提供原生体验。

托管解决方案:https://sendpckt.robaireth.dev

代码库:GitHub - RobaireTH/pckt: The friendliest way to send CKB · GitHub

核心用户流程如下:

  1. 发送方在链上密封数据包。

  2. 发送方共享声明链接。

  3. 接收方针对数据包单元进行声明。

  4. 随着声明的进行,数据包会逐渐缩小。

  5. 如果所有请求权都已用尽,则数据包终止。

  6. 如果时间先到期,发送方可以重新获取剩余的请求权。

技术方案和设计决策

1. 一个合约,一个数据包单元

主要的设计决策是:数据包不是账户,也不是后端对象。它是一个具有明确序列化状态的“单个活动单元”。

这带来了一些优势:

  • 比大型状态机更容易审计

  • 无需服务器托管

  • 声明自然地按单元消耗进行序列化

  • 每个状态转换都可以从链历史记录中验证

2. 前端对社交流进行签名,后端对链进行索引

该系统分为三个部分:

  • Contract:用于声明和回收路径的 Rust CKB 锁定脚本

  • Frontend:使用 CCC 构建和签名交易的 React + TypeScript 应用

  • Backend:Rust + Axum + SQLite 索引器和 UX 辅助工具

后端有意设计为非权威。它不持有密钥,不创造真理,并且可以被替换。它的作用是索引数据包状态,提供发送方/声明方视图、短链接和实时用户体验数据。

3. 声明链接将密钥放在片段中

声明密钥存储在 URL 片段中,而不是后端存储中。这意味着浏览器可以在客户端持有密钥,而后端只能看到公开的短链接目标。这是一个重要的设计边界:服务器有助于用户体验,但不应成为密钥保管层或密钥管理层。

4. 测试网优先,并设定了明确的限制

我特意坚持测试网优先的原则,避免假装所有模式都已准备好用于生产环境。具体而言:

  • 资金回收功能已上线,并集成到用户界面中

  • 发送者名称目前存储在后端,仅供方便使用,尚未实现去中心化身份

  • “幸运平分”模式受限于支付算法和安全问题。

CKB 生态系统相关性

pckt 与 CKB 的相关性在于它充分利用了 Cell 的优势:

  • 显式状态

  • 可组合的锁逻辑

  • 顺序消费和再利用

  • 无需托管中间件的自定义社交/金融流程

我相信这对整个生态系统都有益处,因为它是一个很好的举措,通过将几个世纪以来的传统引入链上,向人们介绍 Nervos CKB。这是一种简单的分享!

部署架构

当前部署结构:

  • 前端:独立部署的 Vite 应用

  • 后端:Fly.io 应用

  • 数据库:Fly 卷上的 SQLite 数据库

  • 目标链:CKB 测试网 (Pudge)

后端运行方式如下:

  • 数据包索引器

  • 数据包/查询 API

  • 短链接服务

  • 价格辅助工具

  • 已签名交易的中继端点

前端通过环境变量配置,包括:

  • 测试网 RPC URL

  • 活动合约代码哈希值

  • 合约输出点

  • 后端 URL

安全注意事项

1. 自保

资金锁定在链上。后端无权移动资金。

2. 非权威后端

后端是可替换的,应该被视为用户体验基础设施,而不是信任锚点。甚至拒绝多个声明的也是合约,而不是后端。

3. 声明密钥处理

声明密钥位于 URL 片段中,因此不会自动发送到服务器。这避免了一类主要的意外后端保管风险。

4. 重组和索引正确性

后端包含重组处理和区块哈希回滚逻辑,因为从链历史中派生的包状态必须在重组后保持完好。

5. 明确限定的功能集

我不希望在支付计算或回收时间方面出现不安全的“勉强能用”的行为。如果某个模式看起来有问题,我倾向于限制它,直到它变得安全为止。

为什么我目前不喜欢幸运分割背后的数学原理

我并不反对幸运分割的想法。我不喜欢当前实现中“幸运拆分”的数学原理和操作风险

问题在于:一旦从固定份额改为随机份额,就需要保证:

  • 每次认领都保持在最低活跃单元容量之上

  • 为未来的认领预留足够的容量

  • 最终认领和赎回路径仍然安全

  • 用户预览与链上逻辑实际允许的情况相符

这一点很容易出现细微的错误。

实际上,“幸运模式”会演变成产品安全问题,而不仅仅是产品设计问题。如果随机下限设置得太低,可能会导致无法认领的输出结果。如果预留计算过于乐观,则可能导致资源闲置。如果前端预测与合同计算存在偏差,则会产生误导性的预期。

目前,我倾向于采取更严格的立场:

  • 固定模式和限时固定模式仍然可用

  • 只有在我确信下限计算、预留规则和用户界面预期都一致后,我才会重新启用“幸运模式”。

我宁愿禁用该模式让用户失望,也不愿让他们使用一个半真半假的模式。

维护计划

我计划将此 Talk Nervos 讨论帖本身作为维护流程的一部分。

具体来说:

  • 我会在此发布新功能或修复的更新

  • 我会在此记录合约重新部署和测试网配置变更

  • 我会在此帖中如实说明已知的限制

  • 我会将帖子中的用户报告视为一个轻量级的公开变更日志和维护渠道

近期维护重点如下:

  1. 稳定发送方/领取方的用户体验

  2. 保持回收流程的可靠性和易懂性

  3. 完善个人资料/名称语义

  4. 仅在安全的情况下重新审视幸运拆分算法

  5. 在认真考虑主网之前,继续使用测试网

结束语

pckt 旨在构建一个小型、社交化且与 CKB 的 cell 模型完美契合的原生应用。它并非旨在成为一个通用的钱包功能(尽管它也可以做到)。它的目标是让熟悉的仪式感在链上自然流畅地呈现。

如果这引起了您的共鸣,我非常希望收到反馈,尤其希望了解身份层是否应该保持简单,还是应该在未来转向更正式的基于 DID 的模型。

9 Likes

Hi @RobaireTH, I’m not trying to put your project down here, but just wanted to bring up that something very similar (in concept at least) has already been developed a couple of years ago by a member here @yixiu.ckbfans.bit.

https://joygift.cc/

2 Likes

Thanks @Yeti for brinnging this up., and, yeah, I don’t taje ut as putting the project down.

I checked out JoyGift and could see there’s overlap across concepts (wrapping of gift, splits and reclaim policies). JoyGift is JoyID Oriented/native while what I’m exploring with pckt is a more contract-centric and self-custodial model where the packet itself is treated as an on-chain state object, claims/reclaims are explicit chain transitions, and the backend is intentionally non-authoritative.

While I acknowledge this, I will like to not see it as ‘the same project already exists’ but as a real use case builders in the ecosystem found worth exploring on CKB.

Once again, I appreciate the pointer. I will look more closely at JoyGift and learn from their implementation. Seems they got the lucky splits right while I’m still sitting at math. @yixiu.ckbfans.bit ,kudos :flexed_biceps:

3 Likes

Hi mate, yeah, I’m happy someone is going to carry this concept forward, I like it.

I used Joygift a lot when the Joyid Telegram was really active, mostly with people outside the CKB ecosystem so it was a great way to introduce new users to CKB by gifting them tokens and the gamified ‘race’ to claim the gifts made it fun!

Also, because of CKB’s storage requirements, I think this sort of thing could be used in some interesting marketing ways to entice users outside of CKB to take part in things like token airdrops and DOB mints where they also need small amounts of CKB to store these assets.

2 Likes

Yes, introducing CKB with pckt would be a great addition to the Nervos ecosystem whereas most of my CKB gifts through Joy had their limitations (for personal reasons), but pckt appears to have a more solid approach and the clean UI finishes off a nice polish.

3 Likes