This project implements a cross-chain oracle system that mirrors Chainlink price feeds from origin chains (such as Base-Sepolia, Binance smart chain, Avalanche Fuji and Polygon Amoy) to a destination chain (Eth-sepolia). The system uses Reactive Network to monitor Chainlink price update events on origin chains and automatically forward them to destination chain contracts that implement the Chainlink AggregatorV3Interface.
You can select multiple price feeds from multiple chains, and deploy from UI. Currently Reactive network supports only limited Source and Destination chains. You can deploy from ALL supported origin chains to ALL destination chains
The project consists of three main components:
-
Smart Contracts and Deployment Scripts: Solidity contracts deployed on Reactive Network and Eth-Sepolia, along with Foundry scripts for deployment and funding operations.
-
Backend Server: Node.js Express server that orchestrates contract deployments, handles API requests from the frontend, and manages funding operations for both Reactive contracts and destination callback proxies.
-
Frontend UI: React-based web interface for creating new oracle bridges, viewing deployed bridges, and managing funding for contracts.
- Left: Echo App (Next.js + Node/Express)
- Frontend dashboard
- Backend that shells out to forge/cast
- Center: Reactive Lasna (ReactVM + Service)
- ChainlinkMirrorReactive + subscription logic
- Right: Destination: Eth Sepolia
- CallbackProxy (provided by Reactive)
- AbstractFeedProxy (mirrored Chainlink feed compatible with AggregatorV3Interface)
- One POST /api/bridges → deploys both contracts and returns all addresses.
- Two more endpoints (/api/fund/reactive, /api/fund/destination) let you fund the Lasna RC and Sepolia CallbackProxy from the UI, no terminals
aggreatorv3-reactive-bridge-abstract/
├── src/
│ ├── reactive/
│ │ └── ChainlinkMirrorReactive.sol # Reactive contract that subscribes to Chainlink events
│ ├── destination/
│ │ └── AbstractFeedProxy.sol # Destination contract implementing AggregatorV3Interface
│ └── interfaces/
│ └── AggregatorV3Interface.sol # Chainlink price feed interface
├── script/
│ ├── DeployAbstractFeedProxy.s.sol # Script to deploy destination feed proxy
│ ├── DeployChainlinkMirrorReactive.s.sol # Script to deploy reactive contract
│ ├── FundReactive.s.sol # Script to fund reactive contracts
│ └── FundCallbackProxy.s.sol # Script to fund destination callback proxy
├── test/
│ ├── AbstractFeedProxy.t.sol # Tests for destination feed proxy contract
│ └── ChainlinkMirrorReactive.t.sol # Tests for reactive contract
├── server/
│ └── server.js # Express backend API server
├── ui/
│ └── src/
│ ├── App.tsx # Main application component
│ ├── api.ts # API client functions
│ ├── types.ts # TypeScript type definitions
│ ├── components/
│ │ ├── BridgeCard.tsx # Bridge card display component
│ │ ├── BridgeDetails.tsx # Bridge details panel component
│ │ └── CreateBridgeModal.tsx # Create new bridge form component
│ ├── config/
│ │ └── origins.ts # Origin chain and feed configurations
│ └── hooks/
│ └── useLocalStorage.ts # LocalStorage state management hook
├── foundry.toml # Foundry project configuration
├── lib/ # External dependencies (forge-std, reactive-lib)
└── README.md # This file
ChainlinkMirrorReactive (src/reactive/ChainlinkMirrorReactive.sol)
A Reactive Contract deployed on Reactive Network (Lasna) that:
- Subscribes to
AnswerUpdatedevents from Chainlink price feeds on origin chains - Decodes event data (roundId, answer, timestamps)
- Emits callbacks to the destination chain with encoded price data
- Tracks the last mirrored round ID for debugging
The contract constructor takes:
_originFeed: Address of the Chainlink aggregator on the origin chain_originChainId: Chain ID of the origin chain_destinationChainId: Chain ID of the destination chain (Eth-sepolia)_destinationFeed: Address of the AbstractFeedProxy on the destination chain
AbstractFeedProxy (src/destination/AbstractFeedProxy.sol)
A destination contract deployed on Eth-Sepolia that:
- Implements Chainlink's
AggregatorV3Interfacefor compatibility - Receives price updates via
updateFromBridgefunction (only callable by the Reactive callback proxy) - Stores round data in a mapping
- Exposes
latestRoundData()andgetRoundData(uint80)for consumers
The contract constructor takes:
_sourceFeed: Original Chainlink feed address on the origin chain_callbackProxy: Reactive callback proxy address on Eth-sepolia_decimals: Price feed decimals (typically 8)_description: Human-readable description (e.g., "ETH / USD")_version: Version number
DeployAbstractFeedProxy.s.sol
Foundry script that deploys the AbstractFeedProxy contract to the destination chain. Reads environment variables:
ORIGIN_FEED: Source Chainlink feed addressCALLBACK_PROXY_ADDR: Reactive callback proxy addressFEED_DECIMALS: Feed decimalsFEED_DESCRIPTION: Feed descriptionFEED_VERSION: Version numberDESTINATION_PRIVATE_KEY: Private key for deployment
DeployChainlinkMirrorReactive.s.sol
Foundry script that deploys the ChainlinkMirrorReactive contract to Reactive Network. Reads environment variables:
ORIGIN_FEED: Origin Chainlink feed addressORIGIN_CHAIN_ID: Origin chain IDDESTINATION_CHAIN_ID: Destination chain IDDESTINATION_FEED: AbstractFeedProxy addressREACTIVE_PRIVATE_KEY: Private key for deployment
FundReactive.s.sol
Script to fund a Reactive contract on Lasna by calling the system contract's depositTo(address) function. Reads:
REACTIVE_PRIVATE_KEY: Private keySYSTEM_CONTRACT_ADDR: System contract addressREACTIVE_CONTRACT_ADDR: Reactive contract address to fundFUND_AMOUNT_WEI: Amount in wei
FundCallbackProxy.s.sol
Script to fund the destination callback proxy by calling depositTo(address) on the callback proxy. Reads:
DESTINATION_PRIVATE_KEY: Private keyCALLBACK_PROXY_ADDR: Callback proxy addressDESTINATION_FEED: AbstractFeedProxy addressFUND_AMOUNT_WEI: Amount in wei
The backend server (server/server.js) is an Express application that provides REST API endpoints for bridge management and contract funding.
For detailed information, check README.md inside /server
POST /api/bridges
Creates a new oracle bridge by:
- Introspecting the origin Chainlink feed to get decimals and description
- Deploying
AbstractFeedProxyto the destination chain - Deploying
ChainlinkMirrorReactiveto Reactive Network - Returning deployment addresses and feed metadata
POST /api/fund/reactive
Funds a Reactive contract on Lasna by sending ETH to the system contract's depositTo(address) function.
POST /api/fund/destination
Funds the destination callback proxy by sending ETH to the callback proxy's depositTo(address) function.
The server requires the following environment variables (set in .env):
DESTINATION_RPC: RPC URL for destination chainDESTINATION_PRIVATE_KEY: Private key for destination chain operationsCALLBACK_PROXY_ADDR: Reactive callback proxy address on destination chainREACTIVE_RPC: RPC URL for Reactive NetworkREACTIVE_PRIVATE_KEY: Private key for Reactive Network operationsREACTIVE_SYSTEM_CONTRACT: System contract address on Reactive Network (optional, has default)
The frontend (ui/) is a React application built with Vite, TypeScript, and Tailwind CSS. It provides a user interface for managing oracle bridges.
For detailed information regarding UI, refer to README.md inside /ui
App.tsx BridgeCard.tsx BridgeDetails.tsx CreateBridgeModal.tsx
origins.ts
Defines available origin chains and their price feeds:
- Chain ID, name, and RPC URL
- List of available Chainlink price feeds with addresses and labels
Bridges are stored in browser localStorage using the useLocalStorageState hook. Each bridge includes:
- Unique ID
- Origin and destination chain information
- Contract addresses
- Feed metadata (decimals, description)
- Status (deploying, active, error)
- Creation timestamp
The frontend communicates with the backend via functions in api.ts:
createBridge(): Creates a new bridgefundReactive(): Funds a Reactive contractfundDestination(): Funds a destination callback proxy
- Foundry installed
- Node.js and npm installed
- Access to RPC endpoints for origin chains, destination chain, and Reactive Network
- Private keys for deployment and funding operations
- Install Foundry dependencies:
forge install- Install backend dependencies:
cd server
npm install- Install frontend dependencies:
cd ui
npm installCreate a .env file in the project root with required environment variables (see Backend Server section).
- Start the backend server:
cd server
node server.js- Start the frontend development server:
cd ui
npm run dev- Access the UI at
http://localhost:5173(or the port shown by Vite)
forge buildFor Abstract Mainnet contracts, use foundry-zksync:
foundryup-zksync
forge build --zkThe project includes comprehensive test suites for both smart contracts using Foundry's testing framework.
Run all tests:
forge testRun tests with verbose output:
forge test -vvRun tests with very verbose output (shows traces):
forge test -vvvRun a specific test file:
forge test --match-path test/AbstractFeedProxy.t.sol
forge test --match-path test/ChainlinkMirrorReactive.t.solRun a specific test function:
forge test --match-test test_UpdateFromBridge_SuccessRun tests matching a pattern:
forge test --match-contract AbstractFeedProxyTest
forge test --match-contract ChainlinkMirrorReactiveTestAbstractFeedProxy Tests (test/AbstractFeedProxy.t.sol)
Tests for the destination chain feed proxy contract:
- Constructor initialization and immutable values
updateFromBridgefunctionality and state updates- Round data storage and retrieval
getRoundDataandlatestRoundDataview functions- Error handling (zero timestamps, non-existent rounds)
- Event emissions
- Multiple rounds handling
- AggregatorV3Interface compliance
ChainlinkMirrorReactive Tests (test/ChainlinkMirrorReactive.t.sol)
Tests for the Reactive Network contract:
- Constructor initialization
reactfunction log processing- Event filtering (chain ID, contract address, topic_0)
- Round ID tracking
- Event data decoding
- Callback emission
- Multiple rounds handling
- Configuration getter functions
The test suite includes 25 tests covering:
- Core functionality of both contracts
- Edge cases and error conditions
- Access control and security
- Event emissions
- State management
- Interface compliance
All tests pass successfully:
$ forge test
Ran 3 test suites: 25 tests passed, 0 failed- User selects an origin chain and price feed in the frontend
- Frontend calls
/api/bridgeswith chain and feed information - Backend introspects the Chainlink feed to get metadata
- Backend deploys
AbstractFeedProxyto the destination chain - Backend deploys
ChainlinkMirrorReactiveto Reactive Network - The Reactive contract subscribes to
AnswerUpdatedevents on the origin chain - When a price update occurs, the Reactive contract emits a callback to the destination chain
- The callback proxy on the destination chain calls
updateFromBridgeonAbstractFeedProxy - The
AbstractFeedProxystores the price data and exposes it vialatestRoundData() - Consumers can read prices from
AbstractFeedProxyusing the standard Chainlink interface

