Origins & Destinations
Callback Proxy Address
在去中心化世界里,“谁在调用我”至关重要。假设你有一个目标链合约,功能是“发放奖励”。如果这个合约谁都能调用,那黑客分分钟就把钱领光了。
我们介绍 Callback Proxy ,它是目标链上一个固定的、受信任的地址。其是由 Reactive Network 官方在各个支持的目标链上预先部署好的基础设施合约。
由于 Reactive Network 是一个独立于以太坊或其他 Layer 2 的网络,当它需要向目标链(如 Arbitrum 或 Base)发送指令时,目标链上的业务合约需要一个“受信任的来源”来验证这些指令。
- 统一性: 官方在每条链上部署一个统一的 Proxy 地址,开发者不需要自己去写复杂的跨链验证逻辑。
- 权威性: 官方负责维护这个代理合约的安全,确保只有经过 Reactive Network 共识验证的消息才能通过这个代理发出来。
Reactive Network 并不直接去敲你合约的门,而是先把指令传给这个 Proxy,再由 Proxy 转发给你。为了确保安全,你的目标合约在收到指令时,必须执行以下逻辑:
- 验证发送方 (The Sender Check):
- 检查
msg.sender。它必须等于该链对应的 Callback Proxy 地址。 - 意义: 确保这条指令不是路人甲发的,而是通过 Reactive 官方渠道送达的。
- 检查
- 验证 RVM ID (The RVM ID Match):
- Reactive 虚拟机会为每个 Reactive Contract (RC) 生成一个唯一的 ID(RVM ID)。
- 意义: 即使指令来自官方 Proxy,你也得确认这个指令是你的那个 RC 发出的。防止别人写的 RC 合约恶意调用你的目标合约。
在编写目标链的智能合约时,你的代码通常会包含这样一个修饰器或判断逻辑:
// 伪代码示例
address constant CALLBACK_PROXY = 0x9299...; // 从官方列表查到的对应链地址
bytes32 constant MY_RC_ID = 0xabc123...; // 你部署的 RC 合约 ID
function onReactiveCall(bytes32 rvmId, bytes calldata data) external {
// 1. 身份检查:必须来自代理合约
require(msg.sender == CALLBACK_PROXY, "Only Proxy allowed");
// 2. 归属检查:必须是我的 RC 发出的逻辑
require(rvmId == MY_RC_ID, "Unauthorized RVM ID");
// 3. 执行真正的逻辑...
}
Hyperlane
我们介绍 Hyperlane,他在这里扮演的是 Transport Layer(传输层)。
- 原生模式 (Native): 如果目标链(如 Arbitrum, BSC)已经部署了 Reactive 的 Callback Proxy,那么 Reactive Network 会通过自己的节点网络直接把数据同步过去。
- Hyperlane 模式: 某些新链或尚未完全集成的链,Reactive 的原生通信还没打通。这时,Reactive 会把加密后的回调指令“打包”交给 Hyperlane。
- Hyperlane 负责把包裹从 A 链搬到 B 链。
- 到达 B 链后,再解包交给当地的代理合约。
如果 Reactive 是顺丰快递,Hyperlane 就是它在没有直达网点时外包的“跨国货运”。对于开发者来说,这保证了全链的覆盖能力。
主网链和测试网链
在这里需要注意,主网链和测试网链的环境是严格隔离的。如果你的源事件发生在以太坊主网,你的回调动作也必须落在另一个主网(如 Arbitrum 或 BSC)。如果你在 Sepolia 测试网测试,回调也只能发往测试网。
这主要是为了防止开发测试时的“垃圾指令”意外触发真实主网的资产变动,同时也因为主网和测试网的 Gas 费结算逻辑完全不同。
主网链
| Chain 链 | Origin 源链 | Destination 目的地 | Chain ID | Callback Proxy | RPC |
|---|---|---|---|---|---|
| Abstract | ✅ | ✅ | 2741 | 0x9299472A6399Fd1027ebF067571Eb3e3D7837FC4 |
Chainlist |
| Arbitrum | ✅ | ✅ | 42161 | 0x4730c58FDA9d78f60c987039aEaB7d261aAd942E |
Chainlist |
| Avalanche | ✅ | ✅ | 43114 | 0x934Ea75496562D4e83E80865c33dbA600644fCDa |
Chainlist |
| Base | ✅ | ✅ | 8453 | 0x0D3E76De6bC44309083cAAFdB49A088B8a250947 |
Chainlist |
| BSC | ✅ | ✅ | 56 | 0xdb81A196A0dF9Ef974C9430495a09B6d535fAc48 |
Chainlist |
| Ethereum | ✅ | ✅ | 1 | 0x1D5267C1bb7D8bA68964dDF3990601BDB7902D76 |
Chainlist |
| HyperEVM | ✅ | ✅ | 999 | 0x9299472A6399Fd1027ebF067571Eb3e3D7837FC4 |
Chainlist |
| Linea | ✅ | ✅ | 59144 | 0x9299472A6399Fd1027ebF067571Eb3e3D7837FC4 |
Chainlist |
| Plasma | ✅ | ✅ | 9745 | 0x9299472A6399Fd1027ebF067571Eb3e3D7837FC4 |
Chainlist |
| Reactive | ✅ | ✅ | 1597 | 0x0000000000000000000000000000000000fffFfF |
https://mainnet-rpc.rnk.dev/ |
| Sonic | ✅ | ✅ | 146 | 0x9299472A6399Fd1027ebF067571Eb3e3D7837FC4 |
Chainlist |
| Unichain | ✅ | ✅ | 130 | 0x9299472A6399Fd1027ebF067571Eb3e3D7837FC4 |
Chainlist |
- ✅ Origin: 表示 Reactive Network 能够实时监控(监听)这条链上的事件。
- ✅ Destination: 表示这条链已经部署了官方的 Callback Proxy,可以直接接收来自 Reactive Network 的指令。
在这个主网列表中,所有列出的链(如 Ethereum, Arbitrum, Base, BSC 等)都是双向 ✅,意味着它们是 “Reactive 全功能支持区”。
像 Abstract, HyperEVM, Linea, Plasma, Sonic, Unichain 的 callback proxy 用的都是同一个地址:0x9299472A6399Fd1027ebF067571Eb3e3D7837FC4。
同时,Reactive 也是一条链。
- 这意味着 Reactive Contract 也可以监听 Reactive 链本身的事件。
- 它的 Proxy 地址是一个特殊的
0x...ffffFfF。 - 这通常用于更高阶的“元操作”,比如一个 Reactive 合约根据另一个 Reactive 合约的状态来做决定。
测试网链
| Chain | Origin | Destination | Chain ID | Callback Proxy | RPC |
|---|---|---|---|---|---|
| Avalanche Fuji | ✅ | ➖ | 43113 | ➖ | Chainlist |
| Base Sepolia | ✅ | ✅ | 84532 | 0xa6eA49Ed671B8a4dfCDd34E36b7a75Ac79B8A5a6 |
Chainlist |
| BSC Testnet | ✅ | ➖ | 97 | ➖ | Chainlist |
| Ethereum Sepolia | ✅ | ✅ | 11155111 | 0xc9f36411C9897e7F959D99ffca2a0Ba7ee0D7bDA |
Chainlist |
| Reactive Lasna | ✅ | ✅ | 5318007 | 0x0000000000000000000000000000000000fffFfF |
https://lasna-rpc.rnk.dev/ |
| Polygon Amoy | ✅ | ➖ | 80002 | ➖ | Chainlist |
| Unichain Sepolia | ✅ | ✅ | 1301 | 0x9299472A6399Fd1027ebF067571Eb3e3D7837FC4 |
Chainlist |
Avalanche Fuji、BSC Testnet 和 Polygon Amoy 是只有 Origin,没有 Destination的半透明链。这意味着可以监听这些链上的事件(比如监听 Fuji 上的存款)。但 Reactive Network 不能直接通过官方 Callback Proxy 回调这些链。如果非要往这些链发回调,就需要用到前面提到的 Hyperlane 作为传输层。
Reactive的测试网是Reactive Lasna,它自己也有 Callback Proxy,意味着你可以在 Reactive 链内部实现逻辑闭环,或者作为逻辑的中转站。
Hyperlane
通常情况下,Reactive 会通过自己的节点直接把指令发给目标链上的 Callback Proxy。但引入 Hyperlane 有三个核心理由:
- 填补空白: 某些链(比如你之前看到的测试网 Fuji 或 Amoy)没有官方 Proxy,但它们支持 Hyperlane。这时 Hyperlane 就是唯一的跨链桥梁。
- 路由灵活性: Hyperlane 允许你自定义消息的传输路径或安全模型。
- 系统集成: 如果你的项目本身就是基于 Hyperlane 构建的(比如你已经用了它的收件箱机制),那么直接用 Hyperlane 传输回调会更自然。
在 Hyperlane 的体系里,最重要的合约是 Mailbox。
- 作用: 它是跨链消息的“进出口”。所有的消息必须先扔进源链的 Mailbox,然后由 Hyperlane 的验证者搬运,最后从目标链的 Mailbox 吐出来。
- 对比:
- Native 模式: Reactive -> Callback Proxy -> 合约。
- Hyperlane 模式: Reactive -> Hyperlane Mailbox -> 跨链传输 -> 目标链 Mailbox -> 合约。
| Chain 链 | Chain ID 链 ID | Hyperlane Mailbox Hyperlane 信箱 | RPC |
|---|---|---|---|
| Ethereum | 1 | 0xc005dc82818d67AF737725bD4bf75435d065D239 |
Chainlist |
| BSC | 56 | 0x2971b9Aec44bE4eb673DF1B88cDB57b96eefe8a4 |
Chainlist |
| Avalanche | 43114 | 0xFf06aFcaABaDDd1fb08371f9ccA15D73D51FeBD6 |
Chainlist |
| Base | 8453 | 0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D |
Chainlist |
| Sonic | 146 | 0x3a464f746D23Ab22155710f44dB16dcA53e0775E |
Chainlist |
| Reactive | 1597 | 0x3a464f746D23Ab22155710f44dB16dcA53e0775E |
https://mainnet-rpc.rnk.dev/ |
在使用Hyperlane的时候,Reactive Contract (RC) 依然在 ReactVM 里运行,它依然通过 subscribe 监听事件,依然通过逻辑判断触发动作。但是回调函数不再调用 emit_callback(发送给 Proxy),而是通过特定的 Hyperlane 指令发送给目标链的 Mailbox。
Reactive Contract
双重环境
当执行部署命令后,同样的字节码会同时出现在两个地方:
Reactive Network (RN)
- 性质: 这是一个公共的、可交互的区块链(类似于以太坊)。
- 谁在使用: 你(EOA 钱包)、其他用户、或者前端网页。
- 它的作用:
- 充值:给合约充入 REACT 代币作为后续运行的 Gas。
- 配置: 调用合约函数来设置“我要监听哪条链”、“监听哪个地址”。
- 状态查询: 存储一些非自动化的元数据。
ReactVM (RM)
- 性质: 这是一个私有的、隔离的执行环境。
- 谁在使用: 只有 Reactive 的底层节点。
- 它的作用:
- 监听: 它像一个雷达,不间断地扫描你订阅的那些链(如以太坊、Base)的日志。
- 执行逻辑: 一旦扫到匹配的日志,它立刻在内部运行你的 Solidity 逻辑。
- 下达指令: 根据逻辑结果,决定是否发送跨链回调。
状态隔离
虽然在两个环境中共用一套代码长得一样,但变量的状态不共享。因为 RVM 的运行速度极快,如果它每次处理事件都要去 RNK 链上同步一下最新的状态,那跨链自动化的延迟就会变得不可接受。
因此使用运行时检查的 vm() 函数让代码确定自己存在的环境,配合函数标志,做到在不同环境运行的效果。
验证
Etherscan 是目前大多数人最熟悉的验证方式。它是一个由私有公司(Etherscan 团队)运营的中心化服务。
- 工作模式: 你把代码上传到 Etherscan 的服务器,他们在自己的后端进行编译,如果编译出来的字节码和链上的一样,就给声明验证了合约。
- 优点: 普及率极高,用户体验好。大多数区块链浏览器(如 Polygonscan, Basescan)都集成了这套系统。
- 缺点:
- 中心化: 如果 Etherscan 哪天倒闭了或服务器宕机,这些验证过的源代码可能会丢失。
- 验证门槛: 它对“元数据”的要求没那么严,只要逻辑对得上,即便编译器的小设置有点出入,通常也能通过。
Reactive使用的是Sourcify 验证,其是一项更极客、更去中心化的开源服务。它不仅仅关注“代码长得像”,它追求的是“完全匹配”。即源代码、编译器版本、设置、甚至连代码里的注释和空格都必须和部署时一模一样。
- 工作模式: 它不仅验证代码,还强制要求验证 Metadata (JSON 文件)。验证后的源码和元数据会被存储在 IPFS(去中心化存储网络)上。
- 优点:
- 永不丢失: 数据在 IPFS 上,谁也删不掉。
- 高确定性: 它是目前最严谨的验证标准。
这种选择的原因是Reactive 的合约运行在 ReactVM (RM) 这种全自动引擎里。这种环境下,“逻辑的微小差异”可能导致自动化脚本产生不可预知的后果。
- 自动化审计: RVM 的节点需要确切知道每一行代码是如何工作的。Sourcify 提供的 IPFS 存储让节点可以随时调取源码进行校验。
- 透明度: 因为 RVM 是“幕后”运行的,只有通过 Sourcify 这种“连空格都不能错”的验证方式,才能给社区提供最高级别的信任。
- 开发者友好: 现在的开发工具(如 Foundry)已经深度集成了 Sourcify,一行命令就能搞定,比去网页上粘贴代码要专业得多。
部署后手动验证
当已经成功部署了合约(拿到了地址),但忘记在部署时验证,或者由于网络抖动导致自动验证失败时,使用此命令。
forge verify-contract \
--verifier sourcify \
--chain-id $CHAIN_ID \
$CONTRACT_ADDR $CONTRACT_NAME
--verifier sourcify:必须明确指定。默认情况下 Foundry 可能会尝试寻找 Etherscan,但在 Reactive 上我们需要 Sourcify。
需要替换的是:
$CHAIN_ID→1597(Reactive 主网)或5318007(Lasna 测试网)$CONTRACT_ADDR→ 已部署的合约地址$CONTRACT_NAME→ 合约名称(例如MyContract),格式通常是src/FileName.sol:ClassName。
部署时自动验证
forge create \
--rpc-url $REACTIVE_RPC_URL \
--private-key $REACTIVE_PRIVATE_KEY \
--value 0.01ether \ # 关键点:给合约“打钱”
--verify \
--verifier sourcify \
src/MyContract.sol:MyContract \
--constructor-args $ARG_1 $ARG_2
--value 0.01ether: 在 Reactive 开发中,这步非常重要。Reactive 合约(RC)是自动运行的,它需要消耗 REACT 代币来支付订阅费和跨链回调费。部署时预存一点钱,能保证合约部署后立即就能“转起来”。--constructor-args: 如果你的构造函数需要参数(比如指定监听哪个地址),这些参数要依次排在后面。
验证后可以在 Reactscan 进行网页验证。
ReactVM
My ReactVM
在 Reactive Network 中,ReactVM 并不是每部署一个合约就生成一个全新的,它的划分逻辑是基于部署者(Deployer)的钱包地址(EOA)。
如果你用地址 0x123... 部署了合约 A 和合约 B,这两个合约会运行在同一个 ReactVM 实例中。因为它们在同一个 RVM 房间里,合约 A 可以像调用普通以太坊合约一样,直接读取合约 B 的公共变量,或者直接通过内部调用(Internal Call)交互。这种共享意味着如果你不小心,合约 A 的逻辑可能会意外修改合约 B 在 RVM 里的状态。
虽然支持共享,但官方文档建议跨 ReactVM 分离合约(即使用不同的 EOA 部署)。
- 性能解耦: 如果多个合约在同一个 RVM,它们必须竞争同一个执行序列。分开部署可以让它们在物理上实现真正的并行处理。
- 安全隔离: 防止一个合约的漏洞或逻辑错误影响到同账户下的其他自动化逻辑。
Calling subscribe()
ReactVM 是一个执行层(数据消费者),它只负责处理已经送上门的日志。RVM 环境被设计为只读外部数据并输出结果。因此我们假设一个场景,想在代码中实现:“如果监听到 A 事件,就自动订阅 B 链的事件”。
这里需要注意的是在 ReactVM 内部,subscribe() 和 unsubscribe() 是无效的。因此必须通过你的钱包,在 Reactive Network (RNK) 链上直接调用合约的 subscribe() 函数。
亦或者,我们可以使用 callback。
- 合约 A 处理完逻辑,向目标链发一个回调。
- 如果需要,这个回调可以触发目标链上的某个事件,再由合约 B 在 RNK 链上重新捕获。
执行层状态
状态独立性
ReactVM(执行层)并不是一个巨大的共享数据库,而是碎片化的:
- 私有性: 每个 ReactVM 维护自己的状态(State)。这意味着合约 A 在 RVM 里的变量修改,默认情况下不会影响到合约 B,除非它们是由同一个钱包部署的。
- 整体性: 整个 Reactive Network 的全局状态,实际上是成千上万个独立运行的 ReactVM 状态的总和。
抗分叉机制
由于 RVM 监听的是外部链(如以太坊),而外部链可能会发生回滚,ReactVM 必须应对这种情况:
- 区块关联: RVM 的每一个区块都“背着”源链的 Block Number 和 Hash。
- 自动回溯: 如果以太坊主网发生了 2 个区块的回滚,Reactive Network 能够识别到哈希的变化,并自动将对应的 ReactVM 状态也回滚到正确的时间点。这保证了自动化的确定性。
生命周期
![[Pasted image 20260315163329.png]]
一切的起点是左侧的 Origin Chain(如以太坊)。
- New Block (新区块): 源链产生一个新区块。
- Catch Block (捕获区块): Reactive Network 的节点第一时间“捕获”这个区块。
然后中间第一个大框的内容,可以理解为“数据预处理”。
- Iterate Transactions (遍历交易): 扫描区块里的每一笔交易。
- Extract Transaction Logs (提取日志): Reactive 只关心事件日志(Logs/Events),而不是交易本身。
- Find transactions at System SC (查找系统合约关联): 检查这些日志是否匹配任何已有的订阅(Subscriptions)。
- Prepare & Publish to RVM (准备并发布给 RVM): 一旦匹配成功,系统将数据打包,送往对应的 ReactVM。
中间第二个大框是RVM环境。
- ReactVM Exists? (RVM 是否存在): 如果是第一次运行或被释放了,执行 Setup ReactVM(初始化环境)。
- 如果已在运行,直接进入 Run ReactVM。
- Execute Transaction (执行交易): 这里运行的是发布者写的 Solidity 逻辑。它读取日志内容,根据设定的
if/else条件进行计算。 - Stop ReactVM (停止运行): 完成逻辑处理后,释放执行资源。
执行完逻辑后,数据流向两个方向:
- Transaction Receipt (交易收据): RVM 将执行结果反馈给 Reactive Network,用于状态更新(RVM State)。
- Prepare for Destination Chain (准备目标链交易): 如果你的逻辑触发了
emit_callback,Reactive Network 会封装一个发送往目标链的交易。
最后一步发生在右侧:
- Transaction at Mem. Pool (内存池中的交易): 最终生成的交易被推送到目标链(如 Arbitrum 或 Base)的内存池,等待打包执行。此时,Callback Proxy 就会发挥作用,验证并完成最终的合约调用。
Economy
RVM 交易
异步扣费
ReactVM (RVM) 的执行和扣费是异步的。 在以太坊上,你没钱发不出交易;但在 RVM 里,它是先帮你干活,然后再找你结账。这也是 Reactive Network 与传统 EVM 链最大的财务区别:
- 无实时气价: 当 RVM 监听到事件并开始跑你的逻辑时,它并不携带
gas price。 - 延后扣费: 费用是在执行完成后的后续区块(通常是下一个)中,根据该区块的
BaseFee追溯计算的。 - 黑盒审计: 因为扣费是在区块层面汇总的,你在 Reactscan 浏览器上只能看到合约总共欠了多少钱,而很难查到某一次具体的 RVM 执行到底花了多少钱。
计算公式: $$fee = BaseFee \cdot GasUsed$$
- Max Gas Limit: 单次执行上限是 900,000 单位。如果你的逻辑太复杂(比如死循环),超过这个数就会强行停止。
合约状态
既然是“先干活后结账”,就必然存在“欠债”的情况。
- Active (活跃): 合约账户里有足够的 REACT,或者欠款在可控范围内,机器人正常工作。
- Inactive (失效): 这是一个危险信号。这意味着你的合约欠费了。此时,RVM 会停止处理该合约的所有订阅事件。
- 恢复方法: 必须结算欠款,状态才会切回
active。
付费
可以把 Reactive 合约想象成一个预付费手机号。
方案 A:直接充值 + 手动结账
- 转账: 使用
cast send直接往合约地址转入 REACT。 - 平账: 调用合约的
coverDebt()函数。这步操作会告诉系统已经有费用了,请求系统结清欠款。
方案 B:系统合约代扣 (推荐)
通过 System Contract(地址:0x...fffFfF)调用 depositTo()。
- 优点: 这是一个“一键式”操作。系统收到钱后,会自动触发平账逻辑,把该合约的欠款结清。
这里需要注意的是这些都是RVM环境里的扣费,而不是RN上的,两者环境不同,结账方式也有很大差别:
- Reactive Network (RNK) 交易: 遵循标准 EVM 模型。你部署合约、修改配置时,就像在以太坊上一样,发送交易时就要付 Gas。
- ReactVM (RVM) 交易: 遵循后结算模型。它是响应式的,由事件触发,费用自动计入债务。
跨链回调(Callback)
定价
跨链操作涉及两个网络的资源,因此费用不是简单的 Gas 消耗。公式如下:
$$p_{callback} = p_{base} \cdot C \cdot (g_{callback} + K)$$
- $p_{base}$ (基础气价): 当前区块的 Gas 价格。
- $C$ (网络系数): 这是一个调节杠杆。由于目标链(如以太坊主网)的 Gas 成本可能远高于 Reactive 网络,这个系数确保收取的 REACT 代币足以兑换成目标链的 Gas。
- $g_{callback}$ (回调 Gas 消耗): 你的代码在目标链上实际跑了多少路。
- $K$ (固定附加费): 协议运行所需的固定开销(如节点审计、跨链消息头处理)。
重要门槛: 每次回调最低必须申请 100,000 Gas。如果你的设置低于这个数,Reactive 网络会直接忽略你的请求,因为它认为这连最基础的安全审计都跑不完。
支付模型
回调的支付逻辑和 RVM 是一样的:先执行,后结算。
- 欠债 (Debt): 回调完成后,产生的费用会记在合约的账上。
- 黑名单 (Blocklist): 如果合约账户余额不足以抵扣这笔费用,合约会被标记为“欠费”,并立即被列入黑名单。
- 一旦进入黑名单,合约的 RVM 逻辑会停摆,也不会再发出任何新的回调,直到你把账补齐。
付费
如果你没有写自动支付逻辑,或者合约已经进入黑名单,你需要执行这两步:
平账:
cast send $CALLBACK_ADDR "coverDebt()" --rpc-url $DEST_RPC --private-key $KEY
充值:
cast send $CALLBACK_ADDR --value 0.1ether --rpc-url $DEST_RPC --private-key $KEY
其次也可以使用系统代理一键充值:
这相当于“直接往信用账户存钱”,存入的同时会自动平掉欠款:
cast send $CALLBACK_PROXY_ADDR "depositTo(address)" $CALLBACK_ADDR --value 0.1ether --rpc-url $DEST_RPC
自动化结算
手动调用 coverDebt() 既麻烦又容易出错。官方提供了一套自动支付模板。通过继承 AbstractPayer,你的合约可以在产生债务的瞬间“自动划款”:
// 引入 Reactive 官方库
import "./abstracts/AbstractPayer.sol";
// 你的目标链业务合约,继承 AbstractPayer
contract MyDestinationContract is AbstractPayer {
// 构造函数需要传入 Callback Proxy 地址
constructor(address _callbackProxy) AbstractPayer(_callbackProxy) {}
// 这是回调的核心函数
function onReactiveCall(bytes32 rvmId, bytes calldata payload) external {
// 1. 验证调用者必须是官方代理(AbstractPayer 已包含部分校验逻辑)
require(msg.sender == callback_proxy, "Only proxy allowed");
// 2. 执行你的业务逻辑
// ... 比如:根据指令给用户发奖金
}
// 关键:实现自动支付逻辑
// 当回调执行完产生债务时,Proxy 会尝试调用这个 pay 函数
function pay() external override payable {
// 校验:只有官方 Proxy 才能扣钱
require(msg.sender == callback_proxy, "Unauthorized payer");
// AbstractPayer 内部通常已经写好了:
// 检查余额 -> 支付债务 -> 更新状态
_pay();
}
}
财务查询
目标链回调合约财务查询 (Callback Contract)
在目标链(如 Base, Arbitrum 等)上,你需要确保你的业务合约有足够的资金来支付跨链回调产生的费用。
余额 (Balance)
指该合约地址在目标链上持有的原生代币(如 ETH)数量。
# 查询合约在目标链上的原生代币余额
cast balance $CONTRACT_ADDR --rpc-url $DESTINATION_RPC
债务 (Debt)
指该合约通过 Callback Proxy 执行回调后产生的尚未结清的费用。
# 查询回调代理记录的该合约欠款(16进制转10进制)
cast call $CALLBACK_PROXY_ADDR "debts(address)" $CONTRACT_ADDR \
--rpc-url $DESTINATION_RPC | cast to-dec
准备金 (Reserves)
指你通过 depositTo() 预存在 Callback Proxy 内部、专门用于抵扣后续回调费用的资金。
# 查询你在回调代理中预存的储备金
cast call $CALLBACK_PROXY_ADDR "reserves(address)" $CONTRACT_ADDR \
--rpc-url $DESTINATION_RPC | cast to-dec
源链响应式合约财务查询(Reactive Contract)
在 Reactive Network 本身,你需要监控合约的 REACT 代币情况,以保证 ReactVM 的逻辑处理不会中断。
余额 (Balance)
指该合约在 Reactive 链上的 REACT 代币余额。
# 查询合约在 Reactive Network 的 REACT 余额
cast balance $CONTRACT_ADDR --rpc-url $REACTIVE_RPC
债务 (Debt)
由 System Contract 记录的、该合约在 RVM 中执行逻辑所消耗的费用。
# 查询系统合约记录的 RVM 执行欠款
cast call $SYSTEM_CONTRACT_ADDR "debts(address)" $CONTRACT_ADDR \
--rpc-url $REACTIVE_RPC | cast to-dec
准备金 (Reserves)
预存在系统合约中、用于自动抵扣 RVM 费用的资金。
# 查询你在系统合约中预存的准备金
cast call $SYSTEM_CONTRACT_ADDR "reserves(address)" $CONTRACT_ADDR \
--rpc-url $REACTIVE_RPC | cast to-dec
区分
这里需要区分balance和reserves的原因很重要:
- 权限问题
在以太坊或 Reactive Network 这种 EVM 兼容链上,原生代币(REACT 或 ETH)没有 approve 机制。
- 你的 Balance 是你的私人领域: 如果资金只是躺在你的合约地址(Balance)里,除了你的合约代码主动调用
call或transfer发送出去,任何外部合约(包括系统合约)都无权强行把这笔钱从你账户里划走。 - 安全防线: 如果系统合约能直接扣你 Balance 里的钱,那就意味着系统合约拥有全网所有资产的“万能钥匙”。为了去中心化的安全,系统被设计成只能动用你主动授权(即转账过去)的那部分钱。
- 性能问题
RN 每天要处理海量的事件流。当它决定是否运行你的逻辑时,它必须在几毫秒内知道:“这家伙有钱付账吗?”
- 查余额太慢: 如果系统每次都要去查几千个不同合约地址的
balance,这涉及到大量的磁盘 I/O 操作(访问账户状态树),速度非常慢。 - 查“内部账本”极快: 当你把钱转到系统合约(Reserves)里时,系统合约只会在它自己的内部维护一张像
mapping(address => uint256) public reserves这样的简单表格。 - 逻辑: RN 只需扫一眼这张表就能判断你的状态(Active/Inactive),而不需要跨合约去查你的私人余额。
- 结算逻辑
由于 Reactive 是“先执行、后结算”的,系统合约需要扮演一个“押金中心”的角色。
- 锁定准备金: 当你把钱存入系统合约,这部分钱就变成了准备金(Reserves)。
- 原子扣款: RVM 执行完产生债务(Debt)后,系统可以直接在内部账本上执行简单的减法: $$NewReserves = OldReserves - Debt$$
- 如果扣 Balance: 万一 RN 刚干完活准备扣你 Balance,结果你正好在同一毫秒把 Balance 里的钱全部转走了,那系统就白干活了。