
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 identifiervendor— 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 subscriptionsresume()— 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 sendercoverDebt()— 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 executionrnOnly— 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 paymentsdebt()— 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 paymentreceive()— 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:
- Handles payments for Reactive Contracts
- Manages contract access control (whitelist/blacklist)
- Emits cron events for periodic triggers
- Delivers callback transactions to destination contracts
- Manages deposits, reserves, and debts
- Restricts callbacks to authorized Reactive Contracts
- Calculates callback gas costs and kickbacks
- 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
| Event | Interval | Approximate Time | Topic 0 |
|---|---|---|---|
Cron1 | Every block | ~7 seconds | 0xf02d6ea5c22a71cffe930a4523fcb4f129be6c804db50e4202fb4e0b07ccb514 |
Cron10 | Every 10 blocks | ~1 minute | 0x04463f7c1651e6b9774d7f85c85bb94654e3c46ca79b0c16fb16d4183307b687 |
Cron100 | Every 100 blocks | ~12 minutes | 0xb49937fb8970e19fd46d48f7e3fb00d659deac0347f79cd7cb542f0fc1503c70 |
Cron1000 | Every 1000 blocks | ~2 hours | 0xe20b31294d84c3661ddc8f423abb9c70310d0cf172aa2714ead78029b325e3f4 |
Cron10000 | Every 10,000 blocks | ~28 hours | 0xd214e1d84db704ed42d37f538ea9bf71e44ba28bc1cc088b2f5deca654677a56 |