beautifulremi / R1 - Reactive Basic

Created Sat, 14 Mar 2026 23:06:27 +0800 Modified Mon, 23 Mar 2026 05:26:54 +0000
7834 Words

Origins & Destinations

Callback Proxy Address

在去中心化世界里,“谁在调用我”至关重要。假设你有一个目标链合约,功能是“发放奖励”。如果这个合约谁都能调用,那黑客分分钟就把钱领光了。

我们介绍 Callback Proxy ,它是目标链上一个固定的、受信任的地址。其是由 Reactive Network 官方在各个支持的目标链上预先部署好的基础设施合约。

由于 Reactive Network 是一个独立于以太坊或其他 Layer 2 的网络,当它需要向目标链(如 Arbitrum 或 Base)发送指令时,目标链上的业务合约需要一个“受信任的来源”来验证这些指令。

  • 统一性: 官方在每条链上部署一个统一的 Proxy 地址,开发者不需要自己去写复杂的跨链验证逻辑。

  • 权威性: 官方负责维护这个代理合约的安全,确保只有经过 Reactive Network 共识验证的消息才能通过这个代理发出来。

Reactive Network 并不直接去敲你合约的门,而是先把指令传给这个 Proxy,再由 Proxy 转发给你。为了确保安全,你的目标合约在收到指令时,必须执行以下逻辑:

  1. 验证发送方 (The Sender Check):

    • 检查 msg.sender。它必须等于该链对应的 Callback Proxy 地址

    • 意义: 确保这条指令不是路人甲发的,而是通过 Reactive 官方渠道送达的。

  2. 验证 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 IDCallback ProxyRPC
Abstract27410x9299472A6399Fd1027ebF067571Eb3e3D7837FC4Chainlist
Arbitrum421610x4730c58FDA9d78f60c987039aEaB7d261aAd942EChainlist
Avalanche431140x934Ea75496562D4e83E80865c33dbA600644fCDaChainlist
Base84530x0D3E76De6bC44309083cAAFdB49A088B8a250947Chainlist
BSC560xdb81A196A0dF9Ef974C9430495a09B6d535fAc48Chainlist
Ethereum10x1D5267C1bb7D8bA68964dDF3990601BDB7902D76Chainlist
HyperEVM9990x9299472A6399Fd1027ebF067571Eb3e3D7837FC4Chainlist
Linea591440x9299472A6399Fd1027ebF067571Eb3e3D7837FC4Chainlist
Plasma97450x9299472A6399Fd1027ebF067571Eb3e3D7837FC4Chainlist
Reactive15970x0000000000000000000000000000000000fffFfFhttps://mainnet-rpc.rnk.dev/
Sonic1460x9299472A6399Fd1027ebF067571Eb3e3D7837FC4Chainlist
Unichain1300x9299472A6399Fd1027ebF067571Eb3e3D7837FC4Chainlist
  • ✅ 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 合约的状态来做决定。

测试网链

ChainOriginDestinationChain IDCallback ProxyRPC
Avalanche Fuji43113Chainlist
Base Sepolia845320xa6eA49Ed671B8a4dfCDd34E36b7a75Ac79B8A5a6Chainlist
BSC Testnet97Chainlist
Ethereum Sepolia111551110xc9f36411C9897e7F959D99ffca2a0Ba7ee0D7bDAChainlist
Reactive Lasna53180070x0000000000000000000000000000000000fffFfFhttps://lasna-rpc.rnk.dev/
Polygon Amoy80002Chainlist
Unichain Sepolia13010x9299472A6399Fd1027ebF067571Eb3e3D7837FC4Chainlist

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  链 IDHyperlane Mailbox  Hyperlane 信箱RPC
Ethereum10xc005dc82818d67AF737725bD4bf75435d065D239Chainlist
BSC560x2971b9Aec44bE4eb673DF1B88cDB57b96eefe8a4Chainlist
Avalanche431140xFf06aFcaABaDDd1fb08371f9ccA15D73D51FeBD6Chainlist
Base84530xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1DChainlist
Sonic1460x3a464f746D23Ab22155710f44dB16dcA53e0775EChainlist
Reactive15970x3a464f746D23Ab22155710f44dB16dcA53e0775Ehttps://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) 这种全自动引擎里。这种环境下,“逻辑的微小差异”可能导致自动化脚本产生不可预知的后果。

  1. 自动化审计: RVM 的节点需要确切知道每一行代码是如何工作的。Sourcify 提供的 IPFS 存储让节点可以随时调取源码进行校验。

  2. 透明度: 因为 RVM 是“幕后”运行的,只有通过 Sourcify 这种“连空格都不能错”的验证方式,才能给社区提供最高级别的信任。

  3. 开发者友好: 现在的开发工具(如 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 部署)。

  1. 性能解耦: 如果多个合约在同一个 RVM,它们必须竞争同一个执行序列。分开部署可以让它们在物理上实现真正的并行处理。

  2. 安全隔离: 防止一个合约的漏洞或逻辑错误影响到同账户下的其他自动化逻辑。

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

一切的起点是左侧的 Origin Chain(如以太坊)。

  1. New Block (新区块): 源链产生一个新区块。

  2. 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 (停止运行): 完成逻辑处理后,释放执行资源。

执行完逻辑后,数据流向两个方向:

  1. Transaction Receipt (交易收据): RVM 将执行结果反馈给 Reactive Network,用于状态更新(RVM State)。

  2. 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:直接充值 + 手动结账

  1. 转账: 使用 cast send 直接往合约地址转入 REACT。

  2. 平账: 调用合约的 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 --value 0.1ether --rpc-url $DEST_RPC --private-key $KEY
    
  • 平账:

    cast send $CALLBACK_ADDR "coverDebt()" --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的原因很重要:

  1. 权限问题

在以太坊或 Reactive Network 这种 EVM 兼容链上,原生代币(REACT 或 ETH)没有 approve 机制

  • 你的 Balance 是你的私人领域: 如果资金只是躺在你的合约地址(Balance)里,除了你的合约代码主动调用 calltransfer 发送出去,任何外部合约(包括系统合约)都无权强行把这笔钱从你账户里划走。

  • 安全防线: 如果系统合约能直接扣你 Balance 里的钱,那就意味着系统合约拥有全网所有资产的“万能钥匙”。为了去中心化的安全,系统被设计成只能动用你主动授权(即转账过去)的那部分钱。

  1. 性能问题

RN 每天要处理海量的事件流。当它决定是否运行你的逻辑时,它必须在几毫秒内知道:“这家伙有钱付账吗?”

  • 查余额太慢: 如果系统每次都要去查几千个不同合约地址的 balance,这涉及到大量的磁盘 I/O 操作(访问账户状态树),速度非常慢。

  • 查“内部账本”极快: 当你把钱转到系统合约(Reserves)里时,系统合约只会在它自己的内部维护一张像 mapping(address => uint256) public reserves 这样的简单表格。

  • 逻辑: RN 只需扫一眼这张表就能判断你的状态(Active/Inactive),而不需要跨合约去查你的私人余额。

  1. 结算逻辑

由于 Reactive 是“先执行、后结算”的,系统合约需要扮演一个“押金中心”的角色。

  1. 锁定准备金: 当你把钱存入系统合约,这部分钱就变成了准备金(Reserves)

  2. 原子扣款: RVM 执行完产生债务(Debt)后,系统可以直接在内部账本上执行简单的减法: $$NewReserves = OldReserves - Debt$$

  3. 如果扣 Balance: 万一 RN 刚干完活准备扣你 Balance,结果你正好在同一毫秒把 Balance 里的钱全部转走了,那系统就白干活了。