[CN/EN] LeapFi 早期预览:一个非托管 RGB++ 资产管理 DApp 的开发笔记 | LeapFi Early Preview: Notes from Building a Non-Custodial RGB++ Asset Management DApp

English Version


LeapFi 早期预览:一个非托管 RGB++ 资产管理 DApp 的开发笔记

各位开发者好,我最近做了一个 RGB++ 资产管理工具:LeapFi

预览地址:https://leapfi-preview.netlify.app/

LeapFi 想解决的问题很直接:让用户可以在一个非托管应用里查看和管理自己的 RGB++ 资产,包括 UDT 和 Spore,并完成常见操作:

  • Leap to BTC

  • Transfer on BTC

  • Leap to CKB

  • BTC / CKB 两侧资产查看

  • 长交易流程的状态追踪与恢复

这还不是一个正式版发布,更像是一次早期预览。我想借这个帖子分享一下开发过程中的一些心得,也想听听社区对 RGB++ 应用开发体验的看法。

这里先说明一下我对 “DApp” 这个词的使用边界:LeapFi 并不是一个完全去中心化的应用,它仍然依赖 btc-assets-api 这样的中心化基础设施来做资产查询、BTC 信息、SPV 和交易状态推进。但用户私钥始终保留在本地钱包里,btc-assets-api 不能替用户签名,资产状态最终也由 BTC / CKB 链上交易决定。所以我更愿意把 LeapFi 称为一个 non-custodial RGB++ DApp,而不是 fully decentralized app。

为什么做 LeapFi?

RGB++ 的协议设计很有吸引力:通过 Bitcoin UTXO 和 CKB Cell 的同构绑定,让资产既能保留 Bitcoin 的所有权语义,又能获得 CKB 的可编程能力。

但从应用层看,目前 RGB++ 生态里还缺少一个比较直观的资产管理入口。开发者可以通过 SDK 和 API 构造交易、查询资产,但普通用户很难在一个地方看清楚自己的 RGB++ UDT / Spore 分别在 BTC 还是 CKB,也很难顺手完成 Leap、Transfer、状态追踪这些操作。

LeapFi 最初就是想补这个空缺:先做一个足够简单的 RGB++ 资产管理界面,让用户可以看到资产、发起操作,也让开发者能从一个真实 DApp 里观察 RGB++ 工作流的复杂度。

但真正做成面向用户的 DApp 后,我发现最难的地方不只是“构造交易”,而是让用户理解并顺利走完一条比较长的交易链路。

比如一次 Transfer on BTC,背后可能包括:

  1. 构造 CKB partial transaction

  2. 构造 BTC PSBT

  3. 签名并广播 BTC 交易

  4. 等待 BTC 区块确认

  5. 获取 SPV proof

  6. 签名并广播 CKB 交易

  7. 等待 CKB 交易确认

对协议来说,这些步骤都有必要。但对普通用户来说,他只是点了一个按钮。

所以 LeapFi 里做了 transaction pipeline,把每个步骤拆开显示:当前在构造 PSBT,还是在等 BTC 确认,还是在推进 CKB 交易。这样至少用户不会觉得页面“卡住了”。

开发心得:长流程、恢复与基础设施边界

做 LeapFi 的过程中,最明显的感受是:RGB++ DApp 的复杂度不只来自交易构造本身,还来自长流程状态管理、交易恢复、以及前端和 btc-assets-api 之间的职责划分。

长交易流程里的 checkpoint

RGB++ 交易流程中,有些中间结果必须保存。

尤其是 BTC 交易签名并广播之后,应用需要保存当时用于 commitment 的 CKB partial transaction。因为如果页面刷新后重新构造,UTXO 选择或交易结构可能已经变了,最后就可能和 BTC 交易里的 commitment 对不上。

所以 LeapFi 目前做了 checkpoint:

  • BTC 广播后保存 btcTxId

  • 保存 seal output index 或 receiver 信息

  • 保存序列化后的 CKB partial transaction

  • 页面刷新后尽量从 checkpoint 恢复,而不是重新猜一遍流程

这部分做下来,我感觉 RGB++ SDK 未来可以考虑提供更标准的 transaction session / resume API。

比如类似这样的 workflow helper:

await rgbpp.workflow.transferOnBtc(params, {
  onStep,
  persistence,
  waitStrategy,
});

这里的重点不是把协议细节黑盒化,而是让 SDK 内置一套可恢复的状态机。

onStep 负责把结构化进度抛给 UI。DApp 不需要自己定义一堆状态名,比如 building PSBT、waiting BTC confirmation、generating SPV、broadcasting CKB。SDK 可以直接告诉应用当前走到哪一步。

persistence 负责保存恢复交易必须的数据,比如 operation、network、BTC txid、CKB partial transaction、commitment、last completed step。浏览器应用可以存在 IndexedDB 或 localStorage,更成熟的应用也可以存在自己的后端。

waitStrategy 则负责等待策略:前端轮询、后端 job、SSE、webhook,都可以放在这个层面扩展。

这样每个 DApp 就不用各自实现一套 checkpoint 规则,也不用反复判断哪些步骤可以重试、哪些步骤必须恢复原始交易、哪些步骤需要重新请求钱包签名。

btc-assets-api 是否适合承担更多 BTC 交易编排?

目前 btc-assets-api 已经在 RGB++ 工作流里承担了很多 BTC 广播之后的事情,比如等待 BTC 确认、SPV、job status 等。进一步的问题是:BTC 交易的构建和广播,是否也可以更多交给 btc-assets-api?

我觉得这个方向是可以讨论的,而且对用户体验可能是好事。但边界要非常清楚。

比较合理的流程可能是:

  1. 前端把用户意图提交给 btc-assets-api:资产、receiver、fee rate、change address 等

  2. btc-assets-api 返回 unsigned PSBT、committed CKB transaction data、job/session id

  3. 前端或 SDK 校验 PSBT 是否符合用户意图

  4. 用户钱包签名 PSBT

  5. 前端把 signed PSBT 或 raw BTC tx 提交给 btc-assets-api

  6. btc-assets-api 广播 BTC 交易,并继续推进后续 SPV / CKB job

也就是说,btc-assets-api 可以构建 unsigned PSBT,也可以广播用户已经签好的 BTC 交易,但不应该替用户签名,也不应该让用户盲签一个无法验证的 PSBT。

这里最大的安全问题是:普通 BTC 钱包通常只能展示 BTC 输入输出,不理解“这笔 BTC 交易背后绑定的是哪个 RGB++ 资产”。如果用户只是看到几个 sats、一个 OP_RETURN,然后直接签名,风险会比较高。

所以如果 btc-assets-api 参与 BTC 交易构建,SDK 或前端必须做 PSBT 语义校验:

  • receiver BTC address 是否正确

  • RGB++ asset input 是否正是用户选择的资产

  • CKB partial transaction 的 commitment 是否和 BTC transaction 匹配

  • change address 是否属于用户

  • fee 是否没有超过用户设置的上限

  • 是否存在多余 input / output

  • 网络是否正确

  • sighash 是否符合预期

更理想的是 btc-assets-api 返回一个结构化的 prepare response:


{
  jobId, 
  psbt,
  operation,
  assetSummary,
  receivers,
  fee,
  commitment,
  ckbPartialTxHash,
  expectedBtcOutputs,
}

然后 @ckb-ccc/rgbpp 提供类似:

rgbpp.verifyPreparedTransfer(response, userIntent);

只有校验通过后,DApp 才把 PSBT 交给钱包签名。

所以我的判断是:把 BTC 构建和广播纳入 btc-assets-api 是合理的,甚至可能是更好的 UX;但必须保留用户本地签名,并且必须有客户端语义校验。 btc-assets-api 可以成为 workflow coordinator,但不应该变成用户资产的信任方。

主网 JWT 与前端 DApp 的访问模型

btc-assets-api 是 RGB++ 应用里很关键的基础设施。它负责资产查询、BTC 信息、SPV、交易队列等能力。资源消耗型服务做权限控制和限流是合理的。

但主网 JWT 对纯前端 DApp 来说有点尴尬。

Token 不能放在前端代码里,普通用户也不方便自己申请 JWT。现在比较现实的做法是应用自己搭一个后端 proxy,把 token 放在服务端,由 proxy 转发请求。

LeapFi 目前也是这个思路。

这个方案能跑,但会带来一些额外成本:

  • 前端 Demo 也需要后端

  • App 方要维护 proxy

  • app-level token 很难区分具体用户

  • 不同资源成本的接口被放进同一种认证模型里

我觉得可以考虑分层:

  • 低频资产查询开放匿名访问或低限流访问

  • 重资源接口继续需要认证

  • 支持钱包签名换短期 token

  • token 可以按地址、scope、endpoint、过期时间限制

  • verified app token 继续保留,但不是唯一入口

这不是说 JWT 不应该存在,而是现在的模型更适合“有后端的应用”,对开放 Web DApp 还可以再友好一点。

想听听大家的想法

LeapFi 还在早期,我更想把它当成一个真实应用实验场:看看 RGB++ 从 SDK、API 到用户体验,中间还有哪些地方可以打磨。

我特别想讨论几个问题:

  1. @ckb-ccc/rgbpp 是否应该提供更高层的 workflow / session API,把 checkpoint、resume、step tracking 这些逻辑标准化?

  2. btc-assets-api 的职责边界应该在哪里?它是否适合从 BTC 广播后的 job 处理,进一步扩展到 unsigned PSBT 构建和 signed tx 广播?

  3. 如果 PSBT 由 API 构建,客户端和 SDK 需要做哪些语义校验,才能避免用户盲签?

  4. 主网 API 的认证和限流,怎样对纯前端 DApp 更友好,同时又能控制资源消耗?

  5. 作为一个早期 RGB++ 资产管理 DApp,LeapFi 接下来最应该补齐哪些功能或体验?

欢迎体验 LeapFi preview,也欢迎在下面直接拍砖。


6 Likes

中文版


LeapFi Early Preview: Notes from Building a Non-Custodial RGB++ Asset Management DApp

Hey devs,

I’ve been building an RGB++ asset management tool called LeapFi.

Preview: https://leapfi-preview.netlify.app/

LeapFi is meant to be a simple non-custodial app for managing RGB++ assets across BTC and CKB. The current preview focuses on:

  • Leap to BTC

  • Transfer on BTC

  • Leap to CKB

  • UDT and Spore asset views

  • Transaction progress tracking and recovery

This is not a final release yet. It is more of an early preview, plus a few notes from actually building with @ckb-ccc/rgbpp and btc-assets-api.

One clarification on the word “DApp”: LeapFi is not fully decentralized. It still relies on centralized infrastructure such as btc-assets-api for asset queries, BTC data, SPV-related work, and transaction status coordination. But user keys stay in local wallets, btc-assets-api cannot sign for users, and final asset state is determined by BTC / CKB on-chain transactions. So I would describe LeapFi as a non-custodial RGB++ DApp, not a fully decentralized app.

Why LeapFi?

RGB++ is exciting because it binds Bitcoin UTXOs with CKB Cells. Users keep Bitcoin-style ownership while getting CKB programmability.

At the application layer, though, the RGB++ ecosystem still lacks a simple asset management entry point. Developers can build transactions and query assets through SDKs and APIs, but regular users do not yet have an obvious place to see where their RGB++ UDTs and Spores live, or to complete Leap, Transfer, and transaction tracking flows in one interface.

LeapFi started as an attempt to fill that gap: first by building a simple RGB++ asset management interface, and then by using a real DApp to understand where the RGB++ workflow still feels complex.

But once this becomes a real DApp, the hard part is not just building a transaction. The hard part is helping users survive a long cross-chain workflow.

A Transfer on BTC flow may involve:

  1. building a CKB partial transaction

  2. building a BTC PSBT

  3. signing and broadcasting the BTC transaction

  4. waiting for BTC confirmation

  5. fetching SPV proof

  6. signing and broadcasting the CKB transaction

  7. waiting for CKB transaction confirmation

For the protocol, this is reasonable. For users, they clicked one button.

So LeapFi uses a transaction pipeline UI. It shows which stage the transaction is in, what has completed, and where it failed if something goes wrong.

Development Notes: Long Workflows, Recovery, and Infrastructure Boundaries

While building LeapFi, the biggest takeaway is that RGB++ DApp complexity does not only come from transaction construction. It also comes from long-running workflow state, recovery, and the boundary between the frontend and btc-assets-api.

Checkpoints in Long Transaction Flows

One important lesson: after the BTC transaction is broadcast, some intermediate data must be preserved.

In particular, the exact CKB partial transaction committed into the BTC transaction should be saved. If the page refreshes and the app rebuilds it from scratch, UTXO selection or transaction structure may change, and the commitment may no longer match.

LeapFi currently saves checkpoints after critical steps and tries to resume from them after refresh.

This makes me think the SDK could eventually provide a standard transaction session / resume API.

For example:

await rgbpp.workflow.transferOnBtc(params, {
  onStep,
  persistence,
  waitStrategy,
});

The idea is not to hide the protocol. The idea is to provide a recoverable workflow state machine.

onStep would emit structured progress events for the UI.

persistence would define how to save required recovery data, such as operation, network, BTC txid, CKB partial transaction, commitment, and last completed step.

waitStrategy would define how the app waits: frontend polling, backend jobs, SSE, webhook, or other mechanisms.

With this shape, every DApp would not need to rediscover the same checkpoint and resume rules.

Should btc-assets-api Handle More BTC Transaction Orchestration?

btc-assets-api already handles a lot of the workflow after BTC broadcast, including confirmation, SPV-related work, and job status. A further question is whether BTC transaction construction and broadcasting can also move into btc-assets-api.

I think this is worth discussing, and it may lead to better UX. But the security boundary must stay clear.

A reasonable flow could be:

  1. frontend submits user intent to btc-assets-api

  2. btc-assets-api returns an unsigned PSBT, committed CKB transaction data, and a job/session id

  3. frontend or SDK verifies that the PSBT matches the user intent

  4. user signs the PSBT with their wallet

  5. frontend submits the signed PSBT or raw BTC tx back to btc-assets-api

  6. btc-assets-api broadcasts the BTC tx and continues the SPV / CKB workflow

In other words: btc-assets-api can build unsigned PSBTs and broadcast already signed BTC transactions. It should not sign for the user, and users should not be asked to blindly sign unverifiable PSBTs.

The main risk is that regular BTC wallets usually do not understand RGB++ semantics. They may show BTC inputs, outputs, and OP_RETURN data, but they do not know which RGB++ asset is being transferred.

So if btc-assets-api builds the PSBT, the SDK or frontend must verify:

  • receiver BTC address

  • selected RGB++ asset input

  • CKB partial transaction commitment

  • change address

  • fee limit

  • unexpected inputs or outputs

  • network

  • sighash mode

Ideally, btc-assets-api returns a structured prepare response:


{
  jobId,
  psbt,
  operation,
  assetSummary,
  receivers,
  fee,
  commitment,
  ckbPartialTxHash,
  expectedBtcOutputs,
}

Then @ckb-ccc/rgbpp could provide something like:

rgbpp.verifyPreparedTransfer(response, userIntent);

Only after verification should the DApp pass the PSBT to the wallet.

My current view: moving BTC construction and broadcasting into btc-assets-api is reasonable, and may be better UX, as long as signing stays local and the client verifies the PSBT semantics. btc-assets-api can become a workflow coordinator, but it should not become a trusted custodian.

Mainnet JWT and the Frontend DApp Access Model

btc-assets-api is important infrastructure. Auth and rate limiting make sense because SPV and transaction queue services are not free to run.

But mainnet JWT is awkward for frontend-first DApps.

A token cannot safely live in frontend code. Regular users also cannot easily obtain one. In practice, apps need a backend proxy that stores the token server-side.

LeapFi currently uses this proxy approach.

It works, but it adds friction:

  • frontend demos need a backend

  • app developers must operate proxy infrastructure

  • app-level tokens do not map cleanly to individual users

  • endpoints with different resource costs share the same auth model

Maybe the API could support tiers:

  • low-rate anonymous access for simple read-only queries

  • authenticated access for heavier endpoints

  • wallet-signed challenge to obtain short-lived scoped tokens

  • verified app tokens for production integrations

The question is not whether auth is needed. It is what auth model best fits open Web DApps.

Open questions

I’d love to hear what others think:

  1. Should @ckb-ccc/rgbpp provide a higher-level workflow / session API that standardizes checkpointing, resume logic, and step tracking?

  2. Where should the btc-assets-api boundary be? Should it move beyond post-BTC-broadcast jobs into unsigned PSBT construction and signed tx broadcasting?

  3. If PSBTs are built by the API, what semantic checks should the client and SDK perform to avoid blind signing?

  4. How can mainnet API auth and rate limiting become friendlier to frontend-first DApps while still controlling resource usage?

  5. As an early RGB++ asset management DApp, what should LeapFi prioritize next?

LeapFi is still early. Please try the preview, break things, and tell me what feels confusing.

6 Likes

Bro, been a while, just read your LeapFi breakdown. Phenomenal frontend execution. Managing those 7-step transaction states on the client side is a nightmare, and you’re spot on about the UX breaking if a user drops off.

I’ve been cooking in the shadows on a backend-driven coordinator that handles the long-flow state tracking completely off-chain so the frontend never loses the session. I’ve actually been cooking in the shadows on a backend orchestration engine that handles that exact long-flow state tracking completely off-chain. But we took it further :writing_hand: I already have the pipeline pushing BTC to CKB, and CKB to EVM.Let’s hop on a casual call to sync up,your interface with this backend engine would be absolute lethal.

1 Like

Would you mind telling me: The backend-driven a remote server, or is it using the browser’s features? Currently, I am encountering similar problems when working on fiber-js.

1 Like

Hey @joii2020 , I would love to share what I can, it is a 100% dedicated remote server (backend infrastructure).

​Relying on the browser’s features (like localStorage or IndexedDB) for long-flow state recovery is too fragile. If the user clears their cache, switches devices, or the browser kills the background tab to save memory, the transaction state drops and assets get stuck in limbo.

​We completely decoupled the state-machine from the frontend. The frontend just captures the user’s intent and initial signature, and then passes the payload to our bare-metal coordinator. The backend server maintains the session off-chain, listens for the BTC block confirmations, and orchestrates the progression automatically. If the user drops off the frontend, the backend engine doesn’t care, it finishes the job.