A TypeScript-based automated feeder for Aleo oracles, designed to submit prices, propose aggregates, and finalize them on the Aleo network.
The Aleo Oracle Feeder is a crucial component for maintaining a decentralized oracle on the Aleo blockchain. It automates the process of:
- Price Submission: Multiple configured accounts fetch and submit price data for a specific feed.
- Aggregate Proposal: A designated "proposer" account calculates a median from the submitted prices and proposes it to the oracle's aggregation contract.
- Aggregate Finalization: After a challenge window, the proposer account finalizes the aggregated price on-chain.
This ensures a continuous and reliable flow of data to the Aleo oracle.
The feeder operates in a fixed-interval cycle, performing the following actions sequentially:
- Price Submission Phase: All configured oracle accounts (up to
MAX_ACCOUNTS) individually submit their observed prices for theFEED_IDusing thesubmitPriceAction.ts. - Proposal Phase: One designated account (the first valid configured account) acts as the proposer.
- It calculates the median of the prices submitted in the current cycle.
- It checks the proposal cooldown based on
last_propose_blockfrom theAGGREGATE_PROGRAM_IDand theAGGREGATION_WINDOW_BLOCKS. - If the cooldown period has passed, it proposes the calculated median price using
proposeAggregateAction.ts.
- Finalization Phase:
- After the proposal is successfully made, the feeder waits for a
CHALLENGE_WINDOW_BLOCKSperiod. - Once the challenge window has passed, the proposer account finalizes the aggregated price using
finalizeAggregateAction.ts.
- After the proposal is successfully made, the feeder waits for a
The entire cycle then repeats.
Error handling and logging are implemented throughout the process to monitor the feeder's operation.
Key files and directories:
src/feeder.ts: The main entry point and scheduler for the oracle feeding process.programActions/:submitPriceAction.ts: Handles the logic for individual accounts to submit prices.proposeAggregateAction.ts: Handles the logic for proposing the median aggregated price.finalizeAggregateAction.ts: Handles the logic for finalizing the aggregated price after the challenge window.
utils/:envHelper.ts&envUtils.ts: Utilities for managing and validating environment variables.aleo/: Utilities for interacting with the Aleo network (e.g., getting block height, reading mappings).mathUtils.ts: Mathematical utilities (e.g., calculating median).
ecosystem.config.cjs: PM2 process manager configuration.Dockerfile&docker-compose.yml: For containerized deployment.
The feeder relies heavily on environment variables for its configuration. Refer to utils/envHelper.ts for a comprehensive list and their roles. Key variables include:
Account Configuration (up to MAX_ACCOUNTS, currently 8):
PK<i>: Private key for accounti(e.g.,PK1,PK2).ADDR<i>: Aleo address for accounti(e.g.,ADDR1,ADDR2). The first successfully configured account (PK1/ADDR1, then PK2/ADDR2, etc.) will be designated as the Proposer.
Oracle & Network Configuration:
FEED_ID: The identifier of the data feed (e.g.,12345field). This must be set.AGGREGATE_PROGRAM_ID: The Aleo program ID for the aggregation contract (e.g.,eclipse_oracle_aggregate_test_1.aleo).SUBMIT_PROGRAM_ID: The Aleo program ID for the price submission contract (e.g.,eclipse_oracle_submit_test_1.aleo).EXPLORER_ENDPOINT: Aleo explorer API endpoint.RPC_ENDPOINT: Aleo RPC endpoint.
Timing & Window Configuration:
FIXED_CYCLE_INTERVAL_MS: Interval for the entire oracle cycle in milliseconds (e.g.,60000for 1 minute). (Default: 60000)AGGREGATION_WINDOW_BLOCKS: The number of blocks that must pass since the last proposal before a new proposal can be made. (Used for proposal cooldown)CHALLENGE_WINDOW_BLOCKS: The number of blocks to wait after a proposal before it can be finalized.AVERAGE_BLOCK_TIME_SECONDS: Estimated average time per Aleo block, used for calculating wait times.
Transaction Parameters:
FEE: Transaction fee in microcredits.RECORD_PLAINTEXT: Set totrueif records should be plaintext,falseotherwise.
API Keys (for submitPriceAction.ts):
CMC_API_KEY: CoinMarketCap API key.CS_API_KEY: CoinStats API key.
Example .env file:
# Account 1 (Proposer if PK1/ADDR1 are set)
PK1=your_private_key_1
ADDR1=your_aleo_address_1
# Account 2
PK2=your_private_key_2
ADDR2=your_aleo_address_2
# ... potentially more accounts up to PK8/ADDR8
FEED_ID=token123field
AGGREGATE_PROGRAM_ID=my_aggregate_program.aleo
SUBMIT_PROGRAM_ID=my_submit_program.aleo
EXPLORER_ENDPOINT=https://api.explorer.provable.com/v1/testnet3
RPC_ENDPOINT=https://vm.aleo.org/api
# Timings (example values)
AGGREGATION_WINDOW_BLOCKS=10
CHALLENGE_WINDOW_BLOCKS=10
AVERAGE_BLOCK_TIME_SECONDS=15
# FIXED_CYCLE_INTERVAL_MS=60000 # Optional, defaults to 60s
FEE=1000000
RECORD_PLAINTEXT=false
CMC_API_KEY=your_cmc_key
CS_API_KEY=your_cs_keyMake sure to create a .env file in the root directory with your specific configuration.
Note: You can use either
npmorpnpmfor all commands below. Replacenpmwithpnpmif you prefer pnpm.
- Node.js (version specified in
Dockerfileorpackage.json) - npm or pnpm
- A configured
.envfile.
npm install
# or
pnpm installnpm start
# or
pnpm start
# or run directly with ts-node / tsx
npx ts-node src/feeder.ts
# or
pnpm exec tsx src/feeder.tsThe ecosystem.config.cjs file is provided for use with PM2, a process manager for Node.js.
# Install PM2 globally if you haven't already
npm install pm2 -g
# or
pnpm add -g pm2
# Start the feeder using PM2
pm2 start ecosystem.config.cjs
# To monitor logs
pm2 logs feeder
# To stop
pm2 stop feederDocker will use the package manager defined in your Dockerfile. If you want to use pnpm, make sure to update your Dockerfile accordingly (see below).
# Build the Docker image
docker build -t oracle-feeder .
# Run the container (ensure you pass environment variables, e.g., via --env-file)
docker run --env-file .env oracle-feeder
# Or using docker-compose (which also uses the .env file by default)
docker-compose up -dIf you want to use pnpm in your Docker build, here is an example Dockerfile:
FROM node:24-alpine
WORKDIR /app
# Install pnpm
RUN npm install -g pnpm
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm add -g pm2
CMD ["pm2-runtime", "ecosystem.config.cjs"]