一念Jeff LiWriting · zhipeng.li中文

A Heart-Stopping Moment in Crypto

加密货币世界中的惊魂一刻

My friend Jj sent 11 BTC into a cross-chain bridge. The xBTC was clearly minted, yet it sat stuck in an intermediate contract and never reached his wallet. Support blamed RBF, the backend threw one error after another, and nearly two days went by with no progress. In the end, he dug the depositKey out of the logs, called finalizeDeposit by hand on Etherscan, and completed the step the automated process had missed.

这是我朋友几天前的亲身经历。

2025 年 11 月下旬,Jj 做了一次跨链操作。

他打开某跨链项目的官方前端,生成了一个比特币充值地址。从自己的钱包里,往这个地址转出了 11 BTC,目标是在以太坊上收到对应的 11 xBTC。 之前他已经用小额BTC试了一次,一切顺利,所以这次他一出手就是价值百万美金的币。

比特币那边很顺利,交易很快确认了。 照理说,过一段时间之后,Jj 的以太坊地址里就应该出现 11 枚 xBTC。

但这一次,钱包里一直是 0。

Jj 打开以太坊浏览器,检查自己的地址,没有任何 xBTC 到账记录。他又去查项目相关合约的链上数据,发现了一件非常微妙的事情: • 链上日志显示:11 枚 xBTC 已经被合约 mint 出来了; • 但这些 xBTC 并没有发到他的以太坊地址,而是留在了一个中间合约地址里——一个专门用于存放存款中间状态的 Depositor 合约。

这意味着,资产不在他手里,也没丢,卡在系统内部。

⸻

发现这个情况后,Jj 到项目的 Discord 里开了工单,请求技术支持。客服很快回应,开始了解情况。

Jj 把比特币交易哈希、以太坊地址、时间点都发了过去,并说明自己在链上看到:xBTC 已经铸造,但被锁在中间合约地址中,没有发送给他。

客服给出的第一个解释,是把问题归因到 RBF(Replace-By-Fee) 上: 他们认为,Jj 这笔 11 BTC 在最终确认时使用了 RBF,打乱了后台用来匹配“比特币存款 → 目标地址”的内部逻辑,导致自动化流程无法把这笔存款正确归属到 Jj 身上,所以后续自动发放 xBTC 的那一步没有执行。

与此同时,Jj 还遇到了一些看得出后台比较混乱的情况: • 他尝试登录项目的 dashboard 查询进度,页面一度加载不出来; • 客服先是提到可能是 Cloudflare 的问题,随后又承认,有一个内部 bot 把 Jj 从相关频道里误踢了出去; • 为了绕过问题,客服又给了他一个“旧版 dashboard”链接,让他试试在那里完成操作,但旧版页面出现了链名不匹配的报错,根本用不上。

在接下来的沟通中,客服一度判断: “你的 xBTC 好像还没有被 mint。”

但此时 Jj 已经通过浏览器确认: xBTC 的 mint 事件已经发生,只是目标地址不是他的钱包地址,而是那个 Depositor 合约。

Jj 把这个事实又冷静地重申了一遍: 比特币已经确认; 合约已经 mint; 问题在于:最后一步发到他地址的流程没有跑完。

对方回复大多是“需要工程师再看一下”“我们在内部讨论解决办法”等,没有给出明确的处置路径。时间一小时一小时地过去,资产仍然停留在中间状态。

⸻

在等待过程中,Jj 决定不再只依赖对方的进展,而是自己着手从技术上想办法。

他整理了手上能拿到的所有资料: 官方前端之前下载的 JSON 文件、错误日志、相关截图、比特币和以太坊的交易哈希。然后,他开始对项目的开源代码和合约接口逐项对照。

在查看合约的 ABI 和 Etherscan 页面时,他注意到,在合约的 “Write as Proxy” 选项卡下,有一个函数名字非常关键——finalizeDeposit。

从代码和注释来看,这个函数的作用,就是在自动流程没有完成时,提供一个手动完成存款流程的入口: 给定正确的参数,就可以让合约把已经 mint 出来的 xBTC,从中间合约转发到真正的目标地址。

要调用这个函数,需要一个参数:depositKey。 这个值是系统在内部用来标记每一次存款的“主键”。在前端正常工作时,用户根本不需要知道它的存在。而在前端失灵的情况下,只有从日志或底层数据里把它翻出来,才能使用这条路径。

Jj 翻看之前导出的 JSON 和报错信息,在一堆长数字和十六进制字符串里,终于找到了与自己这笔 11 BTC 存款对应的那一串 depositKey。

确认了这一点之后,他打开 Etherscan:

  1. 进入相关合约页面;
  2. 切到 “Write as Proxy”;
  3. 选择 finalizeDeposit 函数;
  4. 在表单中将 payableAmount 设置为 0;
  5. 在 depositKey 一栏填入刚刚找到的那串长整型数字;
  6. 用自己的以太坊地址发起这笔交易,并在钱包中签名确认。

交易打包之后,Jj 再次刷新浏览器。

原本停在 Depositor 合约地址中的那 11 枚 xBTC,不再出现在中间地址,而是出现在了他自己的以太坊地址名下。

这笔在链上“中途停留”的资产,终于完成了最后一步。

⸻

问题解决之后,Jj 回到 Discord,把整个过程简要地同步给客服: 他说明自己通过链上数据确认了当前合约状态,找到 depositKey,并调用了合约中的 finalizeDeposit 函数,完成了本该自动进行却中途卡住的那一步。

客服对此表示惊讶,并表示工程师之前并没有想到直接采用这条路径来处理问题。

这起事件,从 11 BTC 打入存款地址开始,到 xBTC 最终出现在Jj的钱包里,中间隔了将近两天时间,自动化流程没有完成,最终是由用户本人在链上亲自补上了那一步。

Originally published on the WeChat account 网安志异, 2025-11-27 · original

Comments

    No sign-in needed; comments appear right away.