Skip to main content

Reactive Library

Overview

Reactive Library provides abstract contracts and interfaces for building Reactive Contracts. The library includes components for subscriptions, callbacks, payments, and system contract interaction.

Install the library in your Foundry project:

forge install Reactive-Network/reactive-lib

Abstract Contracts

AbstractCallback

AbstractCallback extends AbstractPayer.sol and provides callback authorization for Reactive Contracts.

The contract initializes:

  • rvm_id — authorized ReactVM identifier
  • vendor — callback proxy address

The rvmIdOnly modifier restricts functions to the authorized ReactVM.

modifier rvmIdOnly(address _rvm_id) {
require(rvm_id == address(0) || rvm_id == _rvm_id, 'Authorized RVM ID only');
_;
}

The constructor sets the deploying ReactVM as the authorized rvm_id and registers the callback proxy as an authorized payment sender.

constructor(address _callback_sender) {
rvm_id = msg.sender;
vendor = IPayable(payable(_callback_sender));
addAuthorizedSender(_callback_sender);
}

AbstractPausableReactive

AbstractPausableReactive extends AbstractReactive.sol and provides pausable event subscriptions.

Subscriptions are defined using the Subscription struct, which specifies chain ID, contract address, and event topics.

The contract provides:

  • pause() — unsubscribes all pausable subscriptions
  • resume() — restores subscriptions

Access is restricted to the contract owner.

The constructor sets the deployer as the owner.

constructor() {
owner = msg.sender;
}

The pause() function unsubscribes all subscriptions returned by getPausableSubscriptions():

function pause() external rnOnly onlyOwner {
require(!paused, 'Already paused');
Subscription[] memory subscriptions = getPausableSubscriptions();
for (uint256 ix = 0; ix != subscriptions.length; ++ix) {
service.unsubscribe(
subscriptions[ix].chain_id,
subscriptions[ix]._contract,
subscriptions[ix].topic_0,
subscriptions[ix].topic_1,
subscriptions[ix].topic_2,
subscriptions[ix].topic_3
);
}
paused = true;
}

The resume() function restores the same subscriptions:

function resume() external rnOnly onlyOwner {
require(paused, 'Not paused');
Subscription[] memory subscriptions = getPausableSubscriptions();
for (uint256 ix = 0; ix != subscriptions.length; ++ix) {
service.subscribe(
subscriptions[ix].chain_id,
subscriptions[ix]._contract,
subscriptions[ix].topic_0,
subscriptions[ix].topic_1,
subscriptions[ix].topic_2,
subscriptions[ix].topic_3
);
}
paused = false;
}

AbstractPayer

AbstractPayer provides payment and debt-settlement functionality for Reactive Contracts.

Features include:

  • Authorized payment senders
  • Vendor debt settlement
  • Direct contract funding

The authorizedSenderOnly modifier restricts payment initiation to authorized senders.

modifier authorizedSenderOnly() {
require(senders[msg.sender], 'Authorized sender only');
_;
}

The contract provides:

  • pay() — transfers funds to the authorized sender
  • coverDebt() — settles outstanding vendor debt
function pay(uint256 amount) external authorizedSenderOnly {
_pay(payable(msg.sender), amount);
}

function coverDebt() external {
uint256 amount = vendor.debt(address(this));
_pay(payable(vendor), amount);
}

function _pay(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, 'Insufficient funds');
if (amount > 0) {
(bool success,) = payable(recipient).call{value: amount}(new bytes(0));
require(success, 'Transfer failed');
}
}

Authorized senders are managed with:

function addAuthorizedSender(address sender) internal {
senders[sender] = true;
}

function removeAuthorizedSender(address sender) internal {
senders[sender] = false;
}

The contract accepts direct transfers:

receive() virtual external payable {
}

AbstractReactive

AbstractReactive is the base contract for Reactive Contracts. It extends AbstractPayer.sol and implements IReactive.sol, providing access to the Reactive Network system contract and subscription service.

The contract defines two execution modes:

  • vmOnly — ReactVM execution
  • rnOnly — Reactive Network execution

These modes ensure functions run in the appropriate environment.

The constructor initializes the system contract as both the payment vendor and subscription service, and authorizes it for payment operations.

constructor() {
vendor = service = SERVICE_ADDR;
addAuthorizedSender(address(SERVICE_ADDR));
detectVm();
}

Execution mode is determined automatically using detectVm(), which checks whether the system contract is deployed.

function detectVm() internal {
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(0x0000000000000000000000000000000000fffFfF) }
vm = size == 0;
}

Interfaces

IPayable

IPayable defines payment and debt-query functionality for Reactive Contracts.

  • receive() — accepts direct payments
  • debt() — returns the outstanding debt of a contract
interface IPayable {
receive() external payable;

function debt(address _contract) external view returns (uint256);
}

IPayer

IPayer defines a minimal interface for initiating payments and receiving funds.

  • pay() — initiates a payment
  • receive() — accepts direct transfers
interface IPayer {
function pay(uint256 amount) external;

receive() external payable;
}

IReactive

IReactive defines the core interface for Reactive Contracts. It extends IPayer.sol and provides event notifications and the execution entry point.

The LogRecord struct contains event data delivered to the contract.

struct LogRecord {
uint256 chain_id;
address _contract;
uint256 topic_0;
uint256 topic_1;
uint256 topic_2;
uint256 topic_3;
bytes data;
uint256 block_number;
uint256 op_code;
uint256 block_hash;
uint256 tx_hash;
uint256 log_index;
}

The Callback event is emitted when a Reactive Contract triggers a callback transaction.

event Callback(
uint256 indexed chain_id,
address indexed _contract,
uint64 indexed gas_limit,
bytes payload
);

The react() function processes event notifications.

function react(LogRecord calldata log) external;

ISubscriptionService

ISubscriptionService defines functions for managing event subscriptions. It extends IPayable.sol and allows Reactive Contracts to subscribe to or unsubscribe from event logs.

The subscribe() function registers a subscription with the specified event criteria.

function subscribe(
uint256 chain_id,
address _contract,
uint256 topic_0,
uint256 topic_1,
uint256 topic_2,
uint256 topic_3
) external;

The unsubscribe() function removes a subscription matching the specified criteria.

function unsubscribe(
uint256 chain_id,
address _contract,
uint256 topic_0,
uint256 topic_1,
uint256 topic_2,
uint256 topic_3
) external;

ISystemContract

ISystemContract combines the functionality of IPayable.sol and ISubscriptionService.sol. It represents the Reactive Network system contract interface used for payments and subscription management.

import './IPayable.sol';
import './ISubscriptionService.sol';

interface ISystemContract is IPayable, ISubscriptionService {
}

System Contract

Reactive Network operations are handled by three core contracts:

System Contract:

  • Handles payments for Reactive Contracts
  • Manages contract access control (whitelist/blacklist)
  • Emits cron events for periodic triggers

Callback Proxy:

  • Delivers callback transactions to destination contracts
  • Manages deposits, reserves, and debts
  • Restricts callbacks to authorized Reactive Contracts
  • Calculates callback gas costs and kickbacks

AbstractSubscriptionService:

  • Manages event subscriptions
  • Supports filtering by chain, contract, and topics
  • Supports wildcard matching via REACTIVE_IGNORE
  • Emits subscription update events

CRON Functionality

The SystemContract provides a cron mechanism for time-based automation by emitting events at fixed block intervals. Reactive Contracts can subscribe to these events to implement scheduled execution without polling or external automation.

Only authorized validator root addresses can call cron(). Each call to cron() emits one or more Cron events depending on the divisibility of the current block number. Larger intervals produce less frequent events.

Each Cron event contains a single parameter:

  • number — the current block number
EventIntervalApproximate TimeTopic 0
Cron1Every block~7 seconds0xf02d6ea5c22a71cffe930a4523fcb4f129be6c804db50e4202fb4e0b07ccb514
Cron10Every 10 blocks~1 minute0x04463f7c1651e6b9774d7f85c85bb94654e3c46ca79b0c16fb16d4183307b687
Cron100Every 100 blocks~12 minutes0xb49937fb8970e19fd46d48f7e3fb00d659deac0347f79cd7cb542f0fc1503c70
Cron1000Every 1000 blocks~2 hours0xe20b31294d84c3661ddc8f423abb9c70310d0cf172aa2714ead78029b325e3f4
Cron10000Every 10,000 blocks~28 hours0xd214e1d84db704ed42d37f538ea9bf71e44ba28bc1cc088b2f5deca654677a56