For the complete documentation index, see llms.txt. This page is also available as Markdown.

Flap Candy Box

Overview

Flap Candy Box is an on-chain data oracle that delivers paginated, addressable lists to consumer contracts via a request-response callback pattern. It serves as an infrastructure layer for programmable, transparent, and automated reward distribution β€” vaults and other smart contracts can access dynamic CandyBox lists on-chain and act on them automatically, without maintaining the lists themselves.

Each subscription ID represents a list β€” it could be a static snapshot, a dynamically refreshed dataset, or any curated set of addresses. The first subscription is provided by Flap and refreshed every 24 hours, featuring the most active users from the previous day's trading activity on Flap. More subscriptions will be added over time.

Want to build your own custom list? Reach out to us β€” we'd love to explore it with you.

Deployed addresses:

Network
Address
Max Callback Gas

BSC Mainnet

0x6255fbd731272a517022e99f6CaCf6A5De9414Ee

2,000,000

BSC Testnet

0x8e6C16Bf07022a7Da9398B543f38846E9355Bd70

2,000,000

Max Callback Gas: The maximum gas limit supported for an onDataReceived callback.

How it works

Step by step:

  1. The consumer contract calls requestData(), paying the subscription fee and specifying the page (offset, limit) to fetch.

  2. The oracle records the request and emits FlapDataRequested.

  3. The backend queries the previous day's trade data and encodes it as a ResponseData struct.

  4. The backend calls fulfillData(requestId, encodedData) on the oracle.

  5. The oracle forwards the data to the consumer via onDataReceived() with a bounded gas limit.

  6. The oracle emits FlapDataFulfilled with the callback outcome.

Request status lifecycle

If the consumer's onDataReceived() reverts, the request status is set to FAILED. The oracle operator can retry delivery via retryFulfillData() once the consumer issue is resolved.


Subscriptions

Each subscription defines a dataset, its pricing, and the maximum page size.

Field
Type
Description

subscriptionId

bytes32

Pass to requestData()

description

string

Human-readable rule

responseStruct

string

Struct name for decoding data in onDataReceived()

fee

uint256

Required msg.value per request (wei)

maxLimit

uint32

Maximum records per page

Current limit cap: maxLimit is 145 per request. This accounts for consumer contracts that store the returned records on-chain β€” larger payloads may exceed the callback gas budget. Use pagination for more records.

Available subscriptions

Constant

subscriptionId

Description

responseStruct

FLAP_TRADE_BNB_24H

keccak256("FLAP_TRADE_BNB_24H")

Addresses based on their trading activity in the previous 24 hours β€” across both Bonding Curve and DEX β€” on tokens launched from Flap

ResponseData

Fees may change. Always call getFee(subscriptionId) on-chain immediately before sending requestData().


How to integrate

Step 1 β€” Query the fee

Step 2 β€” Call requestData()

Pass block.timestamp directly β€” do not manually compute startTime/endTime. The backend derives the previous UTC day window from the request timestamp.

Pagination example β€” fetching 3000 addresses in pages of 145:

Step 3 β€” Implement the consumer

The recommended approach is to inherit FlapCandyBoxConsumerBase, which provides the onlyFlapCandyBox modifier and hardcoded address resolution automatically.

Alternatively, implement IFlapCandyBoxConsumer directly and validate msg.sender yourself:


ResponseData struct

The data payload in onDataReceived() is ABI-encoded as ResponseData. Use getSubscription(subscriptionId).responseStruct to confirm the struct name for each subscription.

Use totalSize (not returnedCount) for proportional distribution calculations across pages.


Security checklist

Item
Detail

Validate msg.sender

Use FlapCandyBoxConsumerBase (recommended) or require(msg.sender == address(oracle))

Guard double-delivery

Track a fulfilled flag per requestId and revert on repeat

Reentrancy

Mark fulfilled before any external calls or token transfers

Callback gas

Keep onDataReceived() within getMaxCallbackGas(); check with oracle.getMaxCallbackGas()

Fee freshness

Call getFee() immediately before sending requestData() β€” fees may change

Page size

Use getMaxLimit(subscriptionId) to check the current cap before submitting


Reference

Full interface

Last updated