# Get all Action Templates
Source: https://docs.boost.xyz/api-reference/action/get-all-action-templates
https://api-v2.boost.xyz/openapi.json get /action-templates
Returns all known Action Templates that can aid in initializing new Boost Actions and Incentives, with information about project smart contract deployments, event and function signatures, claimant configuration information, and placeholder reward configuration.
# Get an Action Template by actionTemplateId
Source: https://docs.boost.xyz/api-reference/action/get-an-action-template-by-actiontemplateid
https://api-v2.boost.xyz/openapi.json get /action-templates/:id
Returns an Action Template by actionTemplateId
# Explore contracts with Boosts
Source: https://docs.boost.xyz/api-reference/boost/explore-contracts-with-boosts
https://api-v2.boost.xyz/openapi.json get /explore
Discover Boosts with the most claim activity, along with their action and incentive configurations, and total USD distributed.
# Find event and function signatures used in Boost configurations
Source: https://docs.boost.xyz/api-reference/boost/find-event-and-function-signatures-used-in-boost-configurations
https://api-v2.boost.xyz/openapi.json get /known-signatures
Retrieve event or function signatures, human readable signature and their ABI which can be used when configuring Boost Actions.
# Generate a claim signature that can be used to submit a claim
Source: https://docs.boost.xyz/api-reference/boost/generate-a-claim-signature-that-can-be-used-to-submit-a-claim
https://api-v2.boost.xyz/openapi.json get /signatures
Given a tx hash, id, and valid claim data hash, generate a valid signature that can be used to claim a reward on chain.
# Get a list of Boosts
Source: https://docs.boost.xyz/api-reference/boost/get-a-list-of-boosts
https://api-v2.boost.xyz/openapi.json get /boosts
Accepts pagination, and filter options to return a list of boosts
# Get Budgets
Source: https://docs.boost.xyz/api-reference/boost/get-budgets
https://api-v2.boost.xyz/openapi.json get /budget-accounts
Accepts pagination, and filter options like owner address, chain ID, and permissioned member address.
# Get claimable signatures for an address
Source: https://docs.boost.xyz/api-reference/boost/get-claimable-signatures-for-an-address
https://api-v2.boost.xyz/openapi.json get /signatures/claimable/{address}
Returns all signatures for an address that have not yet been used to claim their associated boost.
# Get details for a Boost
Source: https://docs.boost.xyz/api-reference/boost/get-details-for-a-boost
https://api-v2.boost.xyz/openapi.json get /boosts/:id
Return detailed information about a Boost, including budget, allowlist, incentives, actions, and token metadata
# Get events for a specific Boost
Source: https://docs.boost.xyz/api-reference/boost/get-events-for-a-specific-boost
https://api-v2.boost.xyz/openapi.json get /boosts/:id/activity
Retrieve a history of claimed incentives
# Retrieve a single Budget by its system ID
Source: https://docs.boost.xyz/api-reference/boost/retrieve-a-single-budget-by-its-system-id
https://api-v2.boost.xyz/openapi.json get /budget-accounts/:id
Retrieve detailed information about a Budget Account, including contract address, chain ID, owner address, label, and members if applicable.
# Explore contracts with Boosts
Source: https://docs.boost.xyz/api-reference/contract/explore-contracts-with-boosts
https://api-v2.boost.xyz/openapi.json get /explore/contracts
Find information on contracts used to interact with Boosts, as well as total claims, total USD claimed, address, and chain ID
# Get blocklist:chainid:tokenaddress
Source: https://docs.boost.xyz/api-reference/get-blocklist:chainid:tokenaddress
https://api-v2.boost.xyz/openapi.json get /blocklist/:chainId/:tokenAddress
# Get a list of reward kit rewards
Source: https://docs.boost.xyz/api-reference/rewardkit/get-a-list-of-reward-kit-rewards
https://api-v2.boost.xyz/openapi.json get /reward-kit/boosts
Accepts pagination, and filter options to return a list of rewards
# Get a single RewardKit reward
Source: https://docs.boost.xyz/api-reference/rewardkit/get-a-single-rewardkit-reward
https://api-v2.boost.xyz/openapi.json get /reward-kit/boosts/:id
Retrieve details on a reward, optionally providing a claimant address to decorate with additional claim context.
# Get a user's RewardKit profile
Source: https://docs.boost.xyz/api-reference/rewardkit/get-a-users-rewardkit-profile
https://api-v2.boost.xyz/openapi.json get /reward-kit
Returns a list of active, claimable, and unclaimed rewards for a given user.
# Try to get claim signature without known transaction
Source: https://docs.boost.xyz/api-reference/rewardkit/try-to-get-claim-signature-without-known-transaction
https://api-v2.boost.xyz/openapi.json get /transactions
Query Dune for user transaction that can generate a valid claim signature
# Get trending rewards
Source: https://docs.boost.xyz/api-reference/rewardkittrending/get-trending-rewards
https://api-v2.boost.xyz/openapi.json get /reward-kit/trending
Returns a list of boosts that are trending based on the volume in the specified time period (1h or 24h).
# Introduction
Source: https://docs.boost.xyz/platform/overview
Boost product documentation for onchain incentive campaigns
## What is Boost
Boost is a platform for running token incentive campaigns onchain. Teams configure the behavior they want to reward — depositing into a vault, swapping on a DEX, voting on a proposal, holding a position over time — and Boost handles the operational layer: indexing onchain activity, calculating accrued rewards, snapshotting balances, and supporting claims.
The platform offers two products:
* **[Time-Based Incentives](/tbi/introduction)** reward users *continuously* for the size and duration of an onchain position they keep active. Designed for sustained behavior — deeper liquidity, longer holding periods, retention.
* **[One-Time Actions](/v2/documentation/overview/introduction)** reward users *once* for completing a discrete onchain activity. Designed for specific actions — mints, swaps, votes, governance, contract calls.
Campaigns reach end users through [rabbithole.gg](https://rabbithole.gg), Boost's consumer app where users discover, participate in, and claim rewards. Protocols don't need to host a claim UI — rabbithole.gg handles distribution for both products.
Boost is built for product, growth, and ecosystem teams at protocols and apps that want a focused way to incentivize specific onchain behavior.
## What You Can Do with Boost
A few common ways teams use Boost:
* **Bootstrap liquidity** — reward depositors in a new vault, LP, or staking pool, scaled by position size and duration. *(Time-Based Incentives)*
* **Drive launch-day actions** — pay one-time rewards for mints, swaps, or contract interactions tied to a launch. *(One-Time Actions)*
* **Reward sustained holding** — encourage longer holding periods on a token, vault share, or staked balance. *(Time-Based Incentives)*
* **Activate governance** — reward voters, delegators, or participants in DAO actions. *(One-Time Actions)*
* **Welcome new users** — onboard users with one-time rewards for their first swap, mint, or deposit. *(One-Time Actions)*
* **Target a specific audience** — restrict rewards to an allowlist (NFT holders, partner communities, existing users).
## Why Teams Choose Boost
* **Skip the infra build.** Indexing, snapshots, reward accounting, and claim flows are handled — focus on campaign design, not plumbing.
* **Reach users already on rabbithole.gg.** Campaigns surface to an existing user base instead of requiring you to drive traffic to a custom claim page.
* **Pay only for rewards that are claimed.** Anything undistributed returns to your funding account at the end of the claim window.
* **Shape rewards to the behavior you want.** Allowlists, minimum balances, deposit caps, fixed or variable rewards, and tiered payouts let you target a specific outcome.
## Browse the Docs
The docs are organized by product:
Reward users continuously for the size and duration of their onchain positions. Launched directly with the Boost team.
Reward users once when they complete a qualifying onchain action.
Explore the Time-Based Incentives and One-Time Actions API reference.
Get help from the Boost team and community.
## Product Model
| Product | Use it when |
| --------------------- | ----------------------------------------------------------------------------------------------------------- |
| Time-Based Incentives | Users should earn continuously based on the size and duration of an onchain position they keep active. |
| One-Time Actions | A user should earn after completing a discrete action, such as a mint, swap, vote, or contract interaction. |
Interested in running Time-Based Incentives campaigns? [Reach out to the Boost team](mailto:support@boost.xyz) — we'll work with you to integrate your protocol and launch the campaign.
## What's Shared Across Products
Both products distribute ERC-20 token rewards on EVM chains, and both surface campaigns to end users through [rabbithole.gg](https://rabbithole.gg) — Boost's consumer app where users discover and claim rewards.
| | Time-Based Incentives | One-Time Actions |
| --------------------------- | --------------------------- | -------------------------------------------- |
| **Reward type** | ERC-20 tokens | ERC-20 tokens |
| **Where users participate** | rabbithole.gg | rabbithole.gg |
| **Onboarding** | Partner with the Boost team | Self-serve at [boost.xyz](https://boost.xyz) |
Product-specific guides cover the details that differ — campaign configuration, eligibility rules, reward calculation, and lifecycle.
## Getting Help
[support@boost.xyz](mailto:support@boost.xyz) — best for partnership inquiries and TBI campaign setup.
Faster questions, token whitelist requests, and community support.
# Time-Based Incentives API
Source: https://docs.boost.xyz/reference/tbi-api
Placeholder for the Time-Based Incentives API reference
# Time-Based Incentives API
This page is a placeholder for the Time-Based Incentives API reference. Endpoint documentation will be added later.
# Campaign Lifecycle
Source: https://docs.boost.xyz/tbi/core-concepts/campaign-lifecycle
The phases a Time-Based Incentives campaign moves through, from setup to claim window close
Every campaign moves through a predictable arc. Knowing which phase a campaign is in — and what's possible at that phase — helps with launch planning, in-product UI, and answering user questions when something looks off.
The arc is the same for every campaign:
Setup is done, the campaign is on-chain, but earning hasn't started yet.
The campaign is live. Users earn continuously, claims open as snapshots publish.
Earning stops, but the system is still settling — final snapshots roll up the last rewards.
The campaign is settled. Unspent budget can be recovered. Users keep claiming.
The claim window has expired. Anything still unclaimed can be reclaimed by the creator.
A campaign can also leave the main arc early in a couple of ways, covered further down.
***
## Phase by phase
### Configured
The campaign config is committed, the on-chain contract is deployed, and the reward budget is locked in — but the campaign's `startTime` hasn't hit yet.
Nothing user-facing happens. This is the team's window to:
* Review the live config one last time.
* Seed any UI pieces that surface the campaign.
* Coordinate launch comms.
If the team needs to change anything material — eligibility, targets, modes — the campaign has to be cancelled and re-launched. Configuration is locked at deploy.
***
### Active
`startTime` has passed and `endTime` hasn't (or, in some setups, the budget hasn't been fully paid out — see [Automatic early end](#automatic-early-end) below). Users earn continuously while their position is eligible.
What's happening behind the scenes:
* Boost watches the chain for relevant events and updates earnings as positions change.
* A reward snapshot publishes on a regular cadence so claims become available shortly after the campaign goes live.
* The team can watch the campaign run on [rabbithole.gg](https://rabbithole.gg) — TVL, participants, distributed rewards, and live leaderboard stats are all surfaced there.
If the team needs to **end the campaign early**, this is the only window for that. See [Mid-flight adjustments](#mid-flight-adjustments) below.
***
### Ended
`endTime` has passed (or the campaign has otherwise stopped — see [Mid-flight adjustments](#mid-flight-adjustments)). Earning stops for everyone, but the campaign isn't fully wrapped up yet.
A few things are still in motion:
* The final reward snapshot rolls up the last few hours of earnings.
* Users keep claiming on the same schedule as during the active phase.
* The team can no longer change anything about the campaign.
This phase is short — usually a few hours — and ends as soon as the operator settles the final state.
***
### Finalized
The final merkle snapshot is published and locked. Total distributed rewards are confirmed.
At this point, the creator can recover any unspent budget. That can include:
* Rewards from periods when no one was participating.
* Rewards held back by Capped APY when the natural rate would have exceeded the cap.
* Forfeited rewards from broken cliffs (once Cliff is live).
Claims continue to work for the rest of the claim window.
***
### Closed
The claim window expires. Standard campaigns run a **60-day claim window** measured from finalization, but this is configurable per campaign.
Once the window closes:
* New claims stop being accepted.
* Anything still unclaimed becomes recoverable by the creator through the same withdraw flow.
* The campaign is fully wound down.
***
## Mid-flight adjustments
A campaign can leave the active phase before its scheduled `endTime` in two ways.
### Early termination
Creators (or the protocol admin in emergencies) can end a campaign before its scheduled `endTime`.
* Earning stops at the moment of cancellation.
* Already-accrued rewards stay claimable on the normal schedule.
* The campaign skips straight to settlement: ended → finalized → claim window.
Use this when goals shift, when a partner relationship changes, or when something in the underlying market makes the campaign no longer relevant.
### Automatic early end
A **Fixed APY** campaign ends as soon as its reward budget is fully paid out, even if `endTime` hasn't been reached. This happens when participation is higher than the team anticipated — the budget drains faster than the original schedule, and the campaign wraps up the moment the last reward is distributed.
Other modes don't trigger this. **Capped APY** never moves the schedule — when the cap binds, less reward gets distributed per second, and the held-back budget simply stays in the pool as recoverable funds at finalization. The various caps and filters affect who earns and how much, but they never push the campaign past — or short of — the scheduled `endTime`.
When a Fixed APY campaign ends early, the flow is the same as any other ending: status flips to `ended`, the final snapshot rolls up the last earnings, and the campaign moves into Finalized once the operator settles.
### What can't change
Once a campaign launches, the rules are locked:
* Reward token
* Targets (which positions count)
* Eligibility (open, allowlist, verification)
* Modes (caps, tiers, fixed APY, etc.)
* Schedule
Any meaningful change requires a new campaign.
***
## What users see at each phase
| Phase | What users can do |
| ---------- | ---------------------------------------------------------------------------- |
| Configured | View the campaign (if surfaced) and prepare to enter at launch. |
| Active | Hold an eligible position. Watch rewards accrue. Claim as snapshots publish. |
| Ended | Claim accrued rewards. New earning has stopped. |
| Finalized | Claim accrued rewards. Final totals are locked. |
| Closed | Claims rejected. Any unclaimed rewards can be reclaimed by the creator. |
***
## What teams do at each phase
| Phase | Team actions |
| ---------- | -------------------------------------------------- |
| Configured | Final review, marketing prep, integration testing. |
| Active | Monitor stats. End early if priorities shift. |
| Ended | Wait for settlement; no actions required. |
| Finalized | Recover unspent budget if applicable. |
| Closed | Recover any unclaimed leftover rewards. |
***
## Recoverable funds
When a campaign winds down, leftover budget can show up in a few places. All of it is recoverable by the creator through the same withdraw flow:
* **Undistributed budget** — periods when no one was participating, so no rewards were earned.
* **Throttled rewards** — when Capped APY held the rate below its natural value, the saved budget stays in the pool.
* **Forfeited rewards** — when Cliff ships, rewards from broken cliffs flow back here.
* **Unclaimed rewards** — after the claim window closes, anything users didn't pull is returned to the creator.
Nothing gets stuck. Everything that isn't paid out to a user eventually flows back.
## Keep Exploring
How eligible balance and time held translate into rewards.
The stackable rules that shape who earns and how much.
What it looks like to bring a campaign live with the Boost team.
Quick definitions for terms used across the docs.
# Campaign Modes
Source: https://docs.boost.xyz/tbi/core-concepts/campaign-modes
Stackable rules that shape who earns and how much during a Time-Based Incentives campaign
By default, every eligible token in a campaign earns at the same rate. Modes change that. They are stackable rules that filter, cap, weight, or reshape the reward math so a campaign can target a specific outcome instead of paying everyone the same way.
A campaign can run with no modes, one mode, or several modes stacked together. When more than one is active, they apply in a deterministic order so the math stays predictable.
## Mode Catalog
Modes fall into three groups by what they do.
Decide which balance counts toward rewards in the first place.
Limit how much of the reward pool any one user — or the whole campaign — can absorb.
Change the rate at which the reward pool drips out, or weight rewards by position size.
***
## Eligibility Filters
### Minimum Balance
Set a floor. Users below the threshold earn nothing; users at or above the threshold earn on their full balance.
**When to use it.** Filter out dust positions. Require meaningful skin in the game so the reward pool focuses on engaged users.
**Example.** A campaign sets the minimum at 1,000 tokens.
| User holds | Effective balance |
| ------------- | ----------------- |
| 500 tokens | 0 |
| 1,000 tokens | 1,000 |
| 50,000 tokens | 50,000 |
The threshold is binary — once you cross it, the full balance counts, not just the portion above the floor.
**Restrictions.** Cannot exceed `depositCap` if both are set.
***
### New Capital
Take a snapshot of every wallet's balance at a chosen block. From that point on, only the *increase* over the snapshot counts. Existing capital is invisible to the campaign.
**When to use it.** Drive new deposits, not pay existing holders. Useful when the goal is growth rather than retention of an existing base.
**Example.** Snapshot taken at block 18,000,000.
| User | Balance at snapshot | Current balance | Effective balance |
| ----- | ------------------- | --------------- | ----------------- |
| Alice | 5,000 | 5,000 | 0 |
| Bob | 0 | 2,000 | 2,000 |
| Carol | 1,000 | 4,000 | 3,000 |
Alice earns nothing unless she adds more. Bob earns on his full new position. Carol earns on the 3,000 she added.
***
## Position Caps
### Deposit Cap
Cap the eligible balance for any individual user. A user can hold more, but anything above the cap doesn't contribute to rewards.
**When to use it.** Spread the reward pool across more participants. Prevent a single whale from absorbing most of the rewards.
**Example.** Cap set at 10,000 tokens.
| User holds | Effective balance |
| ---------- | ----------------- |
| 5,000 | 5,000 |
| 10,000 | 10,000 |
| 100,000 | 10,000 |
The user with 100,000 still holds their full position — the cap only affects what counts toward rewards.
***
### Capital Cap
Cap the *total* eligible balance summed across all users. Once the cap is hit, additional deposits stop counting until someone exits and frees up room.
**When to use it.** Bound the reward rate. With a known cap, the worst-case earning rate per token is `rewardRate ÷ cap` — useful when you want predictable APY behavior without the complexity of Fixed APY.
**Example.** Cap set at 1,000,000 USDC of eligible balance.
* The first \$1M of deposits all count toward rewards.
* A user who deposits when the campaign is already at the cap accrues nothing on those new tokens until existing capital exits.
* A user who exits frees up room for the next deposit, in order.
***
### Participant Cap
Limit the campaign to a fixed number of distinct earners. Once the cap is reached, later participants can hold the position but accrue nothing.
**When to use it.** Gated programs where headcount matters more than capital — VIP campaigns, partner allowlists, capped beta programs. Pairs naturally with allowlist eligibility.
**Example.** Cap set at 100 participants. Whoever is among the first 100 to enter an eligible position earns. Number 101 holds the position but accrues nothing.
**Restrictions.** Single-target campaigns only.
***
## Reward Shaping
### Fixed APY
Pay out at a guaranteed annualized rate, regardless of how much capital is in the campaign. The reward rate adjusts automatically to maintain the target APY.
**When to use it.** Predictable user-side ROI. Marketing-friendly framing: "Earn 10% APY for holding." Removes the "early adopter advantage" — the APY is the same whether one user is in or one thousand.
**Example.** Target APY set at 10%.
A user deposits \$5,000 into the campaign on day one. They earn at 10% APY for as long as the position is in:
* After 30 days, they've earned roughly \$41.
* After 6 months, roughly \$250.
* Over a full year, roughly \$500.
It doesn't matter whether they're the only participant or sharing the campaign with thousands of others — the rate stays at 10%. What changes with participation is how fast the campaign budget is consumed: more capital in means rewards drain faster, and the campaign can end earlier than scheduled if usage outpaces the budget.
**Caveats.**
* The campaign budget is still a ceiling. If usage is much larger than expected, the campaign ends early when the budget runs out.
* The effective rate uses the live price of the reward token, so APY remains stable even if the reward token's price moves.
* Mutually exclusive with Capped APY.
***
### Capped APY
Set a ceiling on the effective APY. The natural rate runs as usual, but when it would exceed the ceiling — typically early in a campaign before participation builds up — the rate is throttled to the cap.
**When to use it.** Solve the cold-start problem. In the early days of a campaign, before users have noticed and entered, the natural APY can spike to absurd numbers — the very first user might see 5,000% APY because the reward pool is divided across very little capital. Capping the APY smooths user expectations and keeps the campaign from front-loading rewards onto whoever happened to be first in.
**Example.** Cap set at 10% APY.
* If natural APY would be 6%, users earn 6%.
* If natural APY would be 200% (early in the campaign, few participants), it's throttled to 10%.
* The campaign still ends at its scheduled time. Rewards held back during throttled periods stay in the pool and become recoverable budget when the campaign finalizes.
**Restrictions.** Mutually exclusive with Fixed APY.
***
### Deposit Tiers
Larger positions earn more per token, not just more in total. Tiers define balance breakpoints, each with a multiplier above 1x.
**When to use it.** Reward bigger commitments super-proportionally. Encourage users to size up rather than spread thin across many small positions.
**Example.** Three tiers:
| Tier | Minimum balance | Multiplier |
| ------ | --------------- | ---------- |
| Bronze | 1,000 | 1.5x |
| Silver | 5,000 | 2x |
| Gold | 25,000 | 3x |
Walking through users:
| User holds | Tier | Effective balance for rewards |
| ---------- | ------ | ----------------------------- |
| 500 | None | 500 (1x) |
| 1,500 | Bronze | 2,250 (1.5x) |
| 6,000 | Silver | 12,000 (2x) |
| 30,000 | Gold | 90,000 (3x) |
The multiplier applies to the full balance — once a user qualifies for Silver, every token in their position earns at 2x, not just the tokens above the Silver threshold.
***
## Coming Soon
### Cliff
Cliff is in active design and not yet available on production campaigns. The semantics below are confirmed; ship date is TBD.
Require users to maintain a non-zero position continuously for a minimum window before any rewards become claimable. If a user's balance hits zero at any point inside the cliff window, they forfeit every reward they had been accruing — not just future earnings. Users who hold through the entire cliff receive their full accrued rewards in the next snapshot after their cliff expires.
**When to use it.** Filter out users who deposit, claim a quick reward, and exit. The cliff makes early exit a real cost: breaking it forfeits everything earned during the window, which keeps the reward pool flowing toward genuine long-term holders.
**Example.** Cliff set at 30 days.
* **Alice** deposits and holds her position continuously for 30 days. Once she clears the cliff, her accrued rewards become claimable in the next snapshot.
* **Bob** deposits, holds for 20 days, then withdraws to zero on day 21. He forfeits everything he had earned for those 20 days.
* **Carol** deposits 1,000 tokens, then withdraws 600 on day 5 to keep 400. Partial withdrawals don't break the cliff. She keeps accruing on her remaining 400 tokens and clears the cliff at day 30.
* **Dave** breaks his cliff on day 10 by withdrawing to zero, forfeiting his rewards so far. He re-deposits on day 15 and gets a fresh start: a new 30-day cliff anchored at day 15. His forfeited rewards stay in the pool — they aren't restored.
**Key behavior.**
* The cliff is measured per user, starting from their first qualifying deposit — not from campaign start. Late joiners get their own full window.
* Only a balance hitting zero breaks the cliff. Top-ups and partial withdrawals all preserve it.
* A user who breaks the cliff can try again. Re-depositing starts a fresh cliff window with a new anchor; previously forfeited rewards do not come back.
* Forfeited rewards stay in the campaign pool and can be recovered by the creator at the end of the campaign.
* Deposits made too late to clear the cliff before the campaign ends won't qualify. Once the campaign reaches `endTime − cliffDays`, the system stops accepting new triggers (and fresh attempts) so every cliff window has time to finish.
**Restrictions.** `cliffDays` is capped at 50% of the campaign's total duration, which keeps at least half the campaign open for fresh deposits. A 30-day campaign supports a cliff up to 15 days; a 90-day campaign supports up to 45.
***
## How Modes Stack
When multiple modes are active, they apply in a fixed order. Each step transforms the previous step's output.
Subtract the snapshot balance. The result is the user's "new" balance. Existing capital becomes invisible from this step onward.
If the result is below the floor, zero it out. Otherwise pass through.
Clamp the result at the per-user cap.
Look up the tier that matches the clamped result. Multiply the full balance by that tier's multiplier.
The Capital Cap, Participant Cap, Fixed APY, and Capped APY modes don't slot into this chain — they affect the campaign at different points:
* **Capital Cap** — applied during entry, decides whether new deposits register at all.
* **Participant Cap** — applied during entry, decides whether a new wallet becomes eligible.
* **Fixed APY** and **Capped APY** — applied to the reward rate, not the user's balance.
### Worked example
A campaign uses `newCapitalOnly` (snapshot at block X), `minBalance` (1,000 tokens), and `depositTiers` (Bronze 1k → 1.5x, Silver 5k → 2x).
A user held 500 tokens at the snapshot and now holds 8,000 tokens. The math:
1. **New Capital.** Subtract snapshot: `8,000 − 500 = 7,500`.
2. **Minimum Balance.** `7,500 ≥ 1,000`, so the balance passes through unchanged.
3. **Deposit Tiers.** `7,500` qualifies for Silver. Multiplier = 2x. Effective balance = `15,000`.
The user earns rewards as if they were holding 15,000 tokens.
***
## Choosing Modes
Most campaigns use zero or one mode. Stack only when the goal genuinely needs it.
| Goal | Mode to consider |
| --------------------------------------- | --------------------- |
| Pay only meaningful positions | Minimum Balance |
| Reward growth, not existing holders | New Capital |
| Spread rewards across more participants | Deposit Cap |
| Bound the reward rate / cap exposure | Capital Cap |
| Run a closed program with limited seats | Participant Cap |
| Advertise a clean APY number | Fixed APY |
| Tame the early-adopter spike | Capped APY |
| Encourage users to size up | Deposit Tiers |
| Reward genuine long-term holders | Cliff *(coming soon)* |
Not sure which combination fits your campaign? [Reach out to the Boost team](mailto:support@boost.xyz) — we'll walk through the goal and suggest a setup.
## Keep Exploring
How eligible balance and time held translate into rewards.
The phases a campaign moves through, from setup to claim.
What it looks like to bring a campaign to life with the Boost team.
Quick definitions for terms used across the docs.
# Earning Activation
Source: https://docs.boost.xyz/tbi/core-concepts/earning-activation
Limit campaign rewards to users who entered the position through Boost
A standard campaign rewards anyone holding the target position. **Earning Activation** is an optional campaign control that limits rewards to users who entered the position through Boost — usually through [rabbithole.gg](https://rabbithole.gg) or another Boost-aware integration.
The mechanism is opt-in. A user's first deposit routes through a Boost contract, which records a one-time activation signal. From that moment on, every action that user takes — deposits, withdrawals, transfers — counts toward rewards in the normal way. They don't have to keep using Boost after activation.
For users on rabbithole.gg, this happens automatically. The frontend routes deposits through the Boost contract behind the scenes; the user just sees a normal deposit flow.
## When to Use It
Without it, anyone holding the target token earns — including users who arrived through other apps, integrations, or coincidence. Earning Activation focuses the budget on users you actually attracted.
The activation signal doubles as a clean attribution record — a way to see how much capital arrived through Boost versus through other paths.
Some receipt tokens end up in protocol contracts that have no real "user" behind them — wrappers, lending markets using the token as collateral, integrations. Without activation, they'd accrue rewards no one can claim.
Pair it with open eligibility for a campaign that anyone can join — but only if they actually arrive through Boost.
## How It Works
### For users
The flow is the same as any normal deposit, with one difference under the hood:
1. The user goes to their preferred entry point (rabbithole.gg or any Boost-aware integration).
2. They deposit into the eligible position. The deposit routes through Boost and emits an activation signal in the same transaction.
3. From that moment on, the user accrues rewards normally — just like a non-gated campaign.
4. They can withdraw, top up, or transfer the position any way they want from then on. They don't need to use Boost again.
Cross-chain entry is supported too — a user can deposit from another chain and have it routed through Boost into the target position.
### For protocols
A campaign config carries an "Earning Activation required" flag. When set:
* Users who route their first deposit through Boost are eligible to earn.
* Users who never route through Boost never get a position for this campaign — their balance changes are skipped entirely.
* Pre-existing holders aren't retroactively activated. If they held the target token before the campaign launched, they need to do a fresh deposit through Boost (or hold elsewhere — if they want in, they need to activate).
## What It Does Not Do
A few common assumptions that aren't quite right:
* **It doesn't restrict withdrawals.** Once activated, users can exit any way they want — direct withdrawal from the protocol, transfer to another address, etc.
* **It doesn't require Boost for every deposit.** Activation is a one-time event. Subsequent deposits via any path count toward the user's balance.
* **It doesn't lock receipt tokens to one address.** Receipt tokens (vault shares, aTokens, etc.) move freely. Only the activated user accrues rewards on their position.
* **It doesn't change the reward math.** Modes, eligibility, lifecycle, and snapshots all run exactly as they would for a non-gated campaign.
## How It Composes
Earning Activation is a separate axis from the other campaign controls:
* **Eligibility** (open / allowlist / verified) describes *who* is allowed to earn. Activation describes *how they opt in*. They stack — a campaign can require both.
* **Modes** (caps, tiers, fixed APY, etc.) operate on the eligible balance after activation has happened. They don't care how the user activated.
A campaign can run open eligibility with Earning Activation, allowlist with Earning Activation, allowlist with no activation, and so on. Mix as needed.
## Keep Exploring
The reward-shaping rules — caps, tiers, fixed APY, and more — that run on top of activated balances.
How eligible balance and time held translate into rewards.
The phases a campaign moves through from setup to claim window close.
What it looks like to bring a campaign live with the Boost team today.
# Reward Calculation
Source: https://docs.boost.xyz/tbi/core-concepts/reward-calculation
How eligible balance and time held translate into rewards
What a user earns from a Time-Based Incentives campaign comes down to two things: **how much eligible balance they hold** and **how long they hold it for**. This page walks through how those two inputs become real reward amounts, with a few worked examples.
## The drip
Every campaign defines a total reward budget and a duration. Together they set the rate at which rewards flow out:
```
reward rate = total budget ÷ duration
```
A 30-day campaign with a 60,000 USDC budget drips at roughly 2,000 USDC per day, or about 0.023 USDC per second. That stream runs continuously from `startTime` to `endTime`.
The drip doesn't pause and doesn't accelerate. The same amount flows every second, regardless of how many users are participating.
## Your share of the moment
At any given second, the drip is split across everyone holding eligible balance at that moment, in proportion to how much they hold:
```
your share = your eligible balance ÷ total eligible balance
```
If Alice holds \$100 of eligible balance and the total across all participants is \$1,000, Alice captures 10% of that second's drip. If she holds \$500 of \$1,000, she captures 50%.
The share changes the instant anything moves — someone deposits, someone withdraws, prices shift — and earnings adjust in real time.
## Pulling it together
A user's earnings over time are just the sum of every second's drip × their share for that second:
```
earnings = drip × share × time held
```
Bigger share or longer time held = bigger earnings. When the share or balance changes, the math segments and adds up across each interval.
## Worked examples
### One user, full duration
Campaign: 100 USDC over 10 days.
Alice is the only participant the entire time. She has 100% share at every moment. **Alice earns 100 USDC.**
### Two users, same balance
Campaign: 100 USDC over 10 days. Alice and Bob both hold \$100, both hold the entire time.
They each have 50% share at every moment. **Alice earns 50, Bob earns 50.**
### Two users, different balances
Campaign: 100 USDC over 10 days. Alice holds \$100. Bob holds \$200.
Bob's share is twice Alice's at every moment. **Alice earns \~33.3, Bob earns \~66.7.**
### A late joiner
Campaign: 100 USDC over 10 days, so 10 USDC per day. Alice holds \$100 from day 1. Bob joins on day 6 with \$100.
| Period | Who's in | Daily split |
| --------- | ----------- | ---------------------------------------- |
| Days 1–5 | Alice only | 10 → Alice (50 over 5 days) |
| Days 6–10 | Alice + Bob | 5 → Alice, 5 → Bob (25 each over 5 days) |
**Alice earns 75, Bob earns 25.** Alice earned more because she was there longer.
### An early exit
Same campaign. Alice holds \$100 from day 1, then exits on day 6. No one else joins.
| Period | Who's in | Daily split |
| --------- | ---------- | --------------------------- |
| Days 1–5 | Alice only | 10 → Alice (50 over 5 days) |
| Days 6–10 | Nobody | 10 per day undistributed |
**Alice earns 50.** The 50 USDC of drip from days 6–10 stays in the pool. With no one to receive it, it becomes recoverable budget for the creator at finalization.
## A note on undistributed drip
The drip never stops, but it can flow into an empty pool. When no one holds eligible balance during a stretch — at the start of a campaign, between participant rotations, or after everyone exits — that period's rewards aren't distributed.
Undistributed rewards stay in the campaign and are recoverable by the creator at the end of the campaign. Nothing gets lost; it just doesn't go to a user.
## What modes change
Most [campaign modes](/tbi/core-concepts/campaign-modes) shape the *inputs* to this formula rather than the formula itself:
* **Minimum Balance** — balances below the threshold are treated as 0.
* **Deposit Cap** — balances above the cap are clamped down.
* **New Capital** — only the increase over a snapshot counts.
* **Deposit Tiers** — the qualifying tier's multiplier is applied to the full balance.
A couple of modes change the rate or how it's computed:
* **Fixed APY** — replaces the fixed-amount drip with one that flexes to maintain a target APY across whatever balance is in the campaign.
* **Capped APY** — caps the per-second drip when the natural rate would exceed the ceiling. Held-back budget stays in the pool.
A couple of modes gate participation rather than transforming balance:
* **Capital Cap** — stops new balance from counting once the total cap is reached.
* **Participant Cap** — caps the number of distinct earners.
## When rewards land in a user's wallet
Earnings accrue continuously but they aren't transferred on every second. Boost takes a reward snapshot on a regular cadence, publishes it on-chain, and from that point the user can claim what's accrued so far.
Snapshots are cumulative — a user can claim now, claim later, or claim everything at once at the end. The drip math above is what determines how much is in their accrued balance at any given moment; the snapshot is what makes it withdrawable.
See [Campaign Lifecycle](/tbi/core-concepts/campaign-lifecycle) for how snapshots fit into the full campaign arc.
## Keep Exploring
The stackable rules that shape who earns and how much.
The phases a campaign moves through from setup to claim window close.
The plain-English overview of a campaign from end to end.
Quick definitions for terms used across the docs.
# Glossary
Source: https://docs.boost.xyz/tbi/glossary
Quick reference for terms used across the Time-Based Incentives docs
A quick reference for terminology used across the Time-Based Incentives docs. Each entry links to the relevant deeper coverage where useful.
## Foundations
**Boost** — The platform behind Time-Based Incentives. Boost runs the operational layer end-to-end: indexing on-chain events, computing rewards, publishing snapshots, and serving campaign data. References to "the operator" in these docs mean Boost.
**Campaign** — A single Time-Based Incentives program: a fixed reward budget paid out continuously over a fixed schedule against a chosen [target](#target) position. Each campaign has its own configuration — targets, budget, schedule, eligibility, and any modes.
**Creator** — The team or address that funded and configured a campaign.
**Eligibility** — Who is allowed to earn from a campaign. Three options: [open](#open), [allowlist](#allowlist), or [verified](#verified).
**Mode** — A stackable rule that shapes who earns and how much within a campaign. See [Campaign Modes](/tbi/core-concepts/campaign-modes) for the full catalog.
**rabbithole.gg** — Boost's consumer-facing app, where users discover campaigns, hold positions, and claim rewards. Teams running campaigns can monitor live stats here too.
**Target** — The on-chain position that counts toward earning. Could be a token, a vault share, an LP position, or another protocol position.
**Time-Based Incentives (TBI)** — A reward program where users earn in proportion to how much eligible balance they hold and how long they hold it. Rewards drip continuously over the campaign window.
***
## Earning mechanics
**Accrued rewards** — The reward total a user has earned but not yet claimed. Updates continuously as their position changes.
**Drip** — The continuous flow of rewards out of a campaign budget. The drip rate is set by `total budget ÷ duration` and runs every second the campaign is active.
**Eligible balance** — A user's position size as it counts for rewards. Modes can transform raw balance into eligible balance — capping it, multiplying it, or zeroing it out below a threshold.
**Eligible position** — A user's holding of a campaign's target, after eligibility rules have been applied. A user with the right asset but the wrong eligibility (e.g., not on an allowlist) does not have an eligible position.
**Merkle root** — The cryptographic commitment to a [snapshot](#snapshot), published on-chain. Users prove their claim against the latest root.
**Position** — A user's holding of a campaign's target asset. Tracked per user, per campaign.
**Reward rate** — Same as drip rate. The amount of reward token paid out per second of the campaign.
**Share** — A user's slice of the [drip](#drip) at a given moment. `share = your eligible balance ÷ total eligible balance`. Recomputed continuously as people enter and exit.
**Snapshot** — A periodic on-chain publication of cumulative rewards. Once a snapshot lands, the user's accrued earnings up to that point become claimable. Sometimes called a *reward snapshot* to distinguish it from a [snapshot block](#snapshot-block).
**Snapshot block** — A specific historical block used as a reference point. Used by the [New Capital](#new-capital) mode to measure pre-existing balance.
**Trigger** — The moment a user first becomes eligible to earn — typically when their balance moves from zero to non-zero and any eligibility checks pass. After triggering, they accrue continuously while the position stays active.
**TVL** — Total Value Locked. The aggregate eligible balance currently active in a campaign.
***
## Lifecycle phases
See [Campaign Lifecycle](/tbi/core-concepts/campaign-lifecycle) for the full arc.
**Active** — The campaign is live. `startTime` has passed and `endTime` hasn't. Users earn continuously.
**Cancelled** — The campaign was ended early by the creator or admin. Already-accrued rewards stay claimable on the normal schedule.
**Closed** — The claim window has expired. Anything still unclaimed can be reclaimed by the creator.
**Configured (Pending)** — The campaign is deployed and funded, but `startTime` hasn't hit. No earning yet.
**Ended** — `endTime` has passed (or the campaign otherwise stopped early). Earning has stopped, but the system is still settling.
**Finalized** — The final snapshot is locked. Total distributed rewards are confirmed and the creator can recover any unspent budget.
***
## Schedule
**Campaign window** — The interval between `startTime` and `endTime` during which earning happens.
**Claim window** — The period after a campaign finalizes during which users can still claim earned rewards. Standard campaigns use a 60-day window; configurable per campaign.
**endTime** — When earning stops on the standard schedule.
**startTime** — When earning begins.
***
## Money flow
**Claim** — The on-chain action that transfers a user's earned rewards to their wallet. Uses a merkle proof against the latest snapshot. Claims are cumulative — claiming once now and once later, or all at once at the end, all return the same total.
**Forfeited rewards** — Rewards a user accrued but lost — for example, by breaking a [Cliff](#cliff). Stay in the campaign pool, recoverable by the creator at finalization.
**Recoverable budget** — The leftover funds the creator can withdraw at the end of a campaign. Includes undistributed rewards, throttled rewards (Capped APY), forfeited rewards (Cliff, when live), and any rewards left unclaimed after the claim window closes.
**Reward budget** — The total amount of reward token committed to a campaign at launch.
**Reward token** — The token users earn. Set when the campaign is created and locked for the campaign's life.
**Throttled rewards** — Drip that was held back when [Capped APY](#capped-apy) bound the rate below its natural value. Stays in the pool as recoverable budget.
**Undistributed rewards** — Drip that flowed during periods when no one held eligible balance. Stays in the pool as recoverable budget.
***
## Modes (quick reference)
Each mode has its own section in [Campaign Modes](/tbi/core-concepts/campaign-modes); these are one-line summaries.
**Capital Cap** — Caps the *total* eligible balance summed across all participants.
**Capped APY** — Throttles the per-second rate when the natural APY would exceed a ceiling.
**Cliff** *(coming soon)* — Forfeits a user's accrued rewards if they exit before a hold window elapses.
**Deposit Cap** — Caps each individual user's eligible balance at a maximum.
**Deposit Tiers** — Larger positions earn more per token via tiered multipliers.
**Fixed APY** — Pays out at a guaranteed annualized rate, regardless of total balance.
**Minimum Balance** — Users below the threshold earn nothing; users at or above earn on their full balance.
**New Capital** — Only the increase over a snapshot-block balance counts toward rewards.
**Participant Cap** — Limits the number of distinct earners.
***
## Eligibility types
**Allowlist** — Only addresses on a pre-approved list can earn.
**Open** — Anyone holding the target position can earn.
**Verified** — Only addresses that meet a verification standard (e.g., World ID) can earn.
***
## Other
**APY** — Annual Percentage Yield. The annualized return rate a user would earn if conditions stayed constant for a year.
**Cold start** — The early phase of a campaign before participation builds up. The natural APY can spike to absurd numbers because the reward pool is divided across very little capital. [Capped APY](#capped-apy) exists to address this.
# How It Works
Source: https://docs.boost.xyz/tbi/how-it-works
How a Time-Based Incentives campaign actually runs — from goal to earning to claim
A Time-Based Incentives campaign rewards users for **how much they hold and how long they hold it**. Instead of paying on a single qualifying action, the campaign streams rewards every second a user's position is open and stops the moment they exit.
This page walks through how a campaign actually runs — what the protocol decides, what users experience, where funds sit, and how rewards flow from budget to wallet.
## The Earning Model
Rewards don't unlock at fixed intervals. They accrue every second, distributed in real time across everyone holding a qualifying position at that moment.
Two things shape what a user earns:
* **Position size** — the eligible balance they hold.
* **Time held** — how long that balance stays active inside the campaign window.
Bigger positions earn faster. Smaller positions still earn, just at a smaller share of the moment. When a user enters or exits, their rate adjusts immediately. Already-accrued rewards stay locked in — exiting a position doesn't forfeit anything earned along the way.
## The Campaign Loop
Pick the on-chain position the campaign should reward — a token, vault share, LP position, or another supported asset.
Set the eligibility, reward token, budget, schedule, and any [modes](/tbi/core-concepts/campaign-modes). Once funded, the campaign goes live at its scheduled start time.
Users enter or maintain the eligible position. Rewards accrue while the position stays active and stop the moment it exits.
Boost rolls accrued earnings into periodic on-chain snapshots. Users claim cumulative rewards whenever they're ready — partial, in full, now, or at the end.
For the phase-by-phase view of a campaign's life — pending, active, ended, finalized, claim window — see [Campaign Lifecycle](/tbi/core-concepts/campaign-lifecycle).
## What Each Side Does
### What protocols decide
Protocols set the shape of the campaign — what users are doing, who counts, and what they're earning.
| Decision | What it controls |
| ----------------------- | ------------------------------------------------ |
| Targets | Which positions or assets count |
| Reward token and budget | What's being paid out, and how much |
| Schedule | When earning starts and ends |
| Eligibility | Open, allowlist-gated, or verification-gated |
| Modes | Caps, tiers, fixed APY, and other reward shaping |
Once the campaign is live, the configuration is committed. Reward math runs on the rules set at launch.
### What Boost handles
Boost runs the operational layer end to end:
* Indexes the relevant on-chain events.
* Calculates each user's earnings continuously.
* Applies eligibility rules, caps, and any campaign-specific modes.
* Produces distribution snapshots and publishes them on-chain.
* Supports claim transactions when rewards are ready.
Protocols don't track positions, perform reward math, or maintain claim infrastructure.
### What users experience
From a user's point of view, the loop is simple:
1. Hold or open the eligible position.
2. Watch rewards accrue while the position stays active.
3. Claim whenever they're ready — once at the end, periodically, in full or in part.
Apps integrating with TBI can surface the same loop directly in their own product: the campaigns a user qualifies for, what they're earning right now, and what's available to claim.
## Where Funds Sit
Two pots of money live separately during a campaign.
**User principal** stays in the protocol or vault where the position lives. Boost does not custody user deposits or move user principal into a Boost-controlled pool. Users stake, deposit, or hold exactly the way they would without a campaign attached.
**Reward funds** live in campaign smart contracts deployed at launch. Reward tokens sit there and pay out to users as they claim earned rewards.
After a campaign ends, any reward funds that were never allocated — undistributed, throttled, or forfeited — can be reclaimed by the creator. Users then have the campaign's [claim window](/tbi/core-concepts/campaign-lifecycle#schedule) (60 days for standard campaigns) to claim what they earned. After the window closes, any still-unclaimed reward funds can also be reclaimed.
## Example Campaign
A protocol wants users to keep capital in a vault for 90 days. It launches a Time-Based Incentives campaign with a fixed reward budget and points it at the vault share token.
During the campaign:
* Users holding vault shares are eligible to earn.
* A user with a larger eligible balance earns more than a user with a smaller balance.
* A user who stays in the vault the full 90 days earns more than a user who exits after a few weeks.
* A user who joins on day 60 still earns for the time they participate.
* Users can claim accrued rewards along the way as snapshots publish — they don't have to wait for the end.
The protocol gets a campaign designed around retention, not just initial entry.
## Beyond the Basics
The mechanics above describe the default flow. Most campaigns layer additional rules on top — minimum balances, tiered earning, fixed APY, allowlists, snapshots — to match a specific goal.
The drip math: how eligible balance, share of the moment, and time held translate into rewards.
The reward-shaping rules — caps, tiers, fixed APY, and more — that stack on top of a campaign.
Phase-by-phase walkthrough from setup through claim window close.
What it looks like to bring a campaign live with the Boost team today.
# Time-Based Incentives
Source: https://docs.boost.xyz/tbi/introduction
Reward users for keeping eligible onchain positions active over time
Time-Based Incentives help teams grow sustained onchain participation by rewarding users for the positions they keep active over time.
Instead of paying the same reward to every user who completes a single action, a Time-Based Incentives campaign rewards users based on the size of their eligible position and how long they maintain it. Larger positions can earn more. Longer-held positions can earn more. Users who exit early stop accruing, while the rewards they already earned remain available to claim.
Use Time-Based Incentives when your goal is to build durable behavior: deeper liquidity, longer holding periods, stronger retention, or more committed participation in a protocol, vault, token, or ecosystem.
**Interested in launching a campaign?** We'll work with your team to scope the campaign, integrate your protocol, and ship it live. [Reach out to get started](mailto:support@boost.xyz), or read [Launch a Campaign](/tbi/launch-a-campaign) for what to expect.
## Who It Is For
Launch campaigns that align rewards with the behavior you actually want: users keeping valuable positions active through a campaign window.
Earn rewards by maintaining a qualifying position. The bigger the eligible position and the longer it stays active, the more it can earn.
## What You Can Build
Time-Based Incentives are flexible enough to support different growth strategies without changing the core user experience.
* Bootstrap liquidity for a new market, vault, or ecosystem.
* Reward loyal users who keep positions active instead of rotating out immediately.
* Run a targeted campaign for a specific partner, segment, or allowlist.
* Protect a budget with minimum balances, caps, tiers, or reward-rate limits.
* Show users clear earnings, accrued rewards, and claimable rewards inside your own product.
For one-time behaviors like minting, swapping, voting, or completing a specific contract interaction, use [One-Time Actions](/v2/documentation/overview/introduction) instead.
## How Campaigns Work
Choose the supported token, vault, or protocol position that should count toward rewards.
Define the reward token, total budget, schedule, eligibility, and any campaign controls.
Users enter or maintain the eligible position. Their rewards accrue while the position remains eligible.
Boost tracks positions, calculates accrued rewards, surfaces campaign and user data, and supports reward claims.
**Cross-chain by default.** A campaign can index user activity on one chain and pay rewards on another, so users earn where they already are.
## Campaign Controls
Teams can shape campaigns with a few important controls:
| Control | What it answers |
| --------------- | -------------------------------------------------------------------------------------------------- |
| Target position | What user behavior should count? *(e.g., holding a vault token, an LP share, or a staked balance)* |
| Reward budget | How much are you willing to spend? |
| Schedule | When should rewards start and stop? |
| Eligibility | Who should be able to earn? |
| Caps and tiers | How should rewards be distributed across small and large participants? |
| Claim window | How long should users have to claim after the campaign ends? |
These controls let you design around a campaign goal instead of forcing every rewards program into the same shape.
## User Experience
From a user's point of view, the earning model is simple:
1. Join an eligible campaign.
2. Keep the qualifying position active.
3. Watch rewards accrue over time.
4. Claim available rewards when they are ready.
Integrations can show campaign opportunities, eligible balances, estimated earnings, accrued rewards, claimed rewards, and claimable rewards.
## Why Teams Use It
Time-Based Incentives are built for programs where duration matters.
Reward users who continue contributing value, instead of paying equally for short-lived and long-lived participation.
Use campaign rules to manage who earns, how much balance counts, and how rewards are paced.
Give users a visible reason to maintain the position that matters to your product.
Boost handles tracking, reward accounting, campaign data, and claim infrastructure.
## Keep Exploring
Learn the campaign flow from setup through earning and claiming.
Reward calculation, campaign lifecycle, and campaign modes.
Interested in running a Time-Based Incentives campaign? [Reach out to the Boost team](mailto:support@boost.xyz) — see [Launch a Campaign](/tbi/launch-a-campaign) for what onboarding looks like.
# Launch a Campaign
Source: https://docs.boost.xyz/tbi/launch-a-campaign
How to bring a TBI campaign to life with the Boost team
Today, every TBI campaign is set up directly with the Boost team. There's no self-serve dashboard yet — a public-facing one is on the roadmap, but until it ships our team handles onboarding and deployment for each program. The upside: you get hands-on help shaping the campaign before it goes live, and you don't have to write any integration code to get started.
This page walks through what to expect.
## Start with a Conversation
Most campaigns kick off with a single message. [Reach out to the Boost team](mailto:support@boost.xyz) with:
* **The protocol or asset** you want to incentivize
* **The goal** — deeper liquidity, more holders, longer holds, retention boost, etc.
* **Rough budget and timeline**
* **Any constraints** — chains involved, eligibility rules, partner relationships
We'll come back with a recommended setup, confirm the engineering lift on our side, and schedule the work.
## What We'll Set Up Together
### 1. Indexer onboarding
If the protocol or asset you want to reward isn't already supported, we extend our indexer to track it. This is the main reason early campaigns aren't self-serve — our system needs to know how to read your contract events and how to measure each user's position.
You'll bring:
* Contract addresses
* Chain(s) to index
* Token standards involved (ERC20, ERC1155, etc.)
* How position size should be measured (token balance, USD value, LP share, etc.)
Once a protocol is onboarded, additional campaigns on it can launch much faster.
### 2. Campaign design
The shape of the campaign — who earns, how much, for how long. We'll work through these decisions together:
| Decision | What it controls |
| ---------------- | -------------------------------------------------------------------------- |
| **Targets** | Which positions count toward rewards |
| **Eligibility** | Who can earn — open, allowlist, or verification-gated |
| **Modes** | Stackable rules that shape reward math (caps, tiers, snapshots, fixed APY) |
| **Reward token** | What you're paying out |
| **Total budget** | The reward pool size |
| **Duration** | Start and end times |
You don't need to come in with all of this answered. The right configuration usually falls out of a conversation about goals.
Read up on campaign modes to see what the reward math can do. You'll come into the conversation with a clearer picture of what's possible.
### 3. Deployment
Once the design is locked, we deploy:
1. The campaign config is hashed and committed
2. The on-chain campaign contract is created from your funded budget
3. Indexing begins; accruals start at the campaign's start time
You'll get a campaign ID and the campaign goes live at its scheduled `startTime`.
## After Launch
* **End early** if priorities shift — already-accrued rewards remain claimable
* **Watch it run** on [rabbithole.gg](https://rabbithole.gg), where TVL, participants, and live leaderboard stats are surfaced
## Coming Soon
A self-serve dashboard for designing, funding, and deploying campaigns is in development. When it ships, this page will become a guide to that flow. Until then, the white-glove path means a tight feedback loop with our team and a campaign tuned to your goals from day one.
# User Experience
Source: https://docs.boost.xyz/tbi/user-experience
What participating in a Time-Based Incentives campaign looks like for a user — discovery, depositing, earning, and claiming
This page walks through what participating in a campaign actually looks like for a user — from discovering an opportunity to claiming earned rewards. The screenshots are from [rabbithole.gg](https://rabbithole.gg), Boost's consumer-facing app where active campaigns live alongside one-time actions and other earning opportunities.
## The Home Dashboard
When a user lands on rabbithole.gg, the home dashboard surfaces everything they need to know about what they're earning and what's available to earn next.
The key elements:
* **Projected Live Earnings** — a real-time counter ticking up every second as rewards accrue. The Day / Week / Month / Year toggle changes the projection horizon.
* **Potential Earnings** — what the user is on track to earn over the selected window if they keep their current positions.
* **Current Rank** — the user's leaderboard position and tier (Bronze, Silver, Gold, Platinum, Diamond).
* **Dashboard stats** — at-a-glance numbers for active boosts, claimable rewards, lifetime claimed, total deposited, and one-time terms completed.
* **Hold to Earn** — the active TBI campaigns the user qualifies for, with APY, TVL, term length, and current status.
* **One-Time Actions** — discrete tasks (swaps, mints, votes) that pay a fixed reward, separate from continuous Hold to Earn rewards.
## Joining a Campaign
Clicking into a Hold to Earn opportunity opens the campaign's detail panel.
The top of the panel gives the user a quick read on the campaign:
* **APY** — the current rate, with a sub-line indicating whether it's fixed for the term or floats with TVL.
* **Term, Participants, TVL, TVE** — the four-card summary of campaign size, schedule, and engagement.
* **Remaining Rewards** — how much of the budget is still flowing.
* **Description** — what the user has to hold to earn.
* **Deposit Tiers** (when configured) — multiplier breakpoints that reward larger positions super-proportionally. See [Campaign Modes](/tbi/core-concepts/campaign-modes).
* **Activation callout** (when applicable) — a reminder that only deposits routed through Rabbithole earn rewards on this campaign. See [Earning Activation](/tbi/core-concepts/earning-activation).
Scrolling down on the panel opens the deposit form:
The form handles everything in one transaction:
* **Amount** — the position the user wants to open, with 25/50/75/MAX percentage shortcuts.
* **Token selector** — pick the token to deposit; the form swaps under the hood if the campaign target uses a different asset.
* **Swap + Deposit fee** — a preview of the network fee for the transaction.
* **Deposit** — confirms the transaction. The deposit routes through Boost (activating the user for any campaigns with Earning Activation enabled) and lands in the underlying protocol.
When the deposit confirms, the user receives the receipt token (vault shares, aTokens, etc.) directly in their wallet and rewards start accruing.
## Watching Rewards Accrue
After depositing, the home dashboard's "Projected Live Earnings" counter starts ticking up in real time. The "Potential Earnings" estimate adjusts as the campaign's APY shifts. There's nothing the user has to do to keep earning — the position just has to stay open.
If the user partially withdraws, their earning rate drops proportionally. If they fully exit, accrual stops, but every reward they had already earned stays in their accrued balance and remains claimable.
## Claiming Rewards
Rewards become claimable shortly after a campaign goes live and at every snapshot afterward. When a campaign has rewards ready, the user can claim them directly from the campaign detail panel.
The claim flow:
* **Reward amount** — the cumulative total available right now, denominated in the campaign's reward token.
* **Network fee** — gas estimate for the claim transaction, refreshed periodically.
* **Claim rewards** — a single button that submits the on-chain claim and transfers the rewards to the user's wallet.
Claims are cumulative — claiming once now and once again at the end of the campaign, or saving up and claiming everything at the end, all return the same total. Users lose nothing by waiting, so they can batch claims to save on gas.
## Tier and Leaderboard
Beyond per-campaign rewards, rabbithole.gg tracks user activity into a tier system: **Bronze → Silver → Gold → Platinum → Diamond**. Tiers reflect cumulative engagement across all the campaigns and actions a user has participated in.
Tier perks vary depending on what's running — priority on capacity-limited campaigns, allowlist access to gated launches, and fee or boost benefits at higher tiers.
## Where Funds Sit
* **Principal stays in the protocol.** A vault deposit goes into the vault; an Aave supply goes into Aave. Boost doesn't custody user funds — receipt tokens live in the user's own wallet from the moment of deposit.
* **Reward funds live in the campaign contract.** Each campaign has its own contract holding its reward budget. Rewards transfer to the user when they claim.
The two pots stay separate the entire time. Users withdraw their position from the underlying protocol exactly the way they'd withdraw without a campaign attached.
## Keep Exploring
The full picture of a campaign — how protocols configure it, how Boost runs it, and how rewards flow.
How eligible balance and time held translate into actual reward amounts.
How some campaigns gate rewards to users who entered through Boost.
Quick definitions for terms used across the docs.
# Generate a claim signature that can be used to submit a claim
Source: https://docs.boost.xyz/v2/api-reference/boost/generate-a-claim-signature-that-can-be-used-to-submit-a-claim
get /signatures
Given a tx hash, id, and valid claim data hash, generate a valid signature that can be used to claim a reward on chain.
# Get a list of Boosts
Source: https://docs.boost.xyz/v2/api-reference/boost/get-a-list-of-boosts
get /boosts
Accepts pagination, and filter options to return a list of boosts
# Get details for a Boost
Source: https://docs.boost.xyz/v2/api-reference/boost/get-details-for-a-boost
get /boosts/:id
Return detailed information about a Boost, including budget, allowlist, incentives, actions, and token metadata
# Get events for a specific Boost
Source: https://docs.boost.xyz/v2/api-reference/boost/get-events-for-a-specific-boost
get /boosts/:id/activity
Retrieve a history of claimed incentives
# OpenAPI JSON File
Source: https://docs.boost.xyz/v2/api-reference/openapi/openapi-json
Download the OpenAPI JSON specification for the Boost API
Download our OpenAPI JSON specification
## Available One-Time Actions API Endpoints
`GET /boosts` - Get a paginated list of boosts with optional filters
`GET /boosts/{id}` - Get detailed information about a specific boost
`GET /boosts/{id}/activity` - Get activity history for a boost
`GET /signatures` - Get signatures required for boost claims
## Available Reward Kit API Endpoints
`GET /reward-kit/boosts` - Get a paginated list of rewards.
`GET /reward-kit/boosts/{id}` - Get details for a single reward.
`GET /reward-kit` - Get reward status for a claimant address.
`GET /transactions` - Check recent transactions for a qualifying claim signature.
# Get a list of reward kit rewards
Source: https://docs.boost.xyz/v2/api-reference/rewardkit/get-a-list-of-reward-kit-rewards
get /reward-kit/boosts
Accepts pagination, and filter options to return a list of rewards
# Get a single RewardKit reward
Source: https://docs.boost.xyz/v2/api-reference/rewardkit/get-a-single-rewardkit-reward
get /reward-kit/boosts/:id
Retrieve details on a reward, optionally providing a claimant address to decorate with additional claim context.
# Get a user's RewardKit profile
Source: https://docs.boost.xyz/v2/api-reference/rewardkit/get-a-users-rewardkit-profile
get /reward-kit
Returns a list of active, claimable, and unclaimed rewards for a given user.
# Try to get claim signature without known transaction
Source: https://docs.boost.xyz/v2/api-reference/rewardkit/try-to-get-claim-signature-without-known-transaction
get /transactions
Query Dune for user transaction that can generate a valid claim signature
# Boost Activity
Source: https://docs.boost.xyz/v2/boost-api/boost-activity
How to retrieve the claim history for a specific boost
The Boost API allows you to fetch the claim history for a specific boost through the [GET /boosts/:id/activity endpoint](/v2/api-reference/boost/get-events-for-a-specific-boost).
## Basic Usage
Fetch the activity history for a specific boost:
```typescript theme={null}
const boostId = '8453:0x378632819f39c74c4f56b1429e760739c5fb51b7:12';
const response = await fetch(
`https://api-v2.boost.xyz/boosts/${boostId}/activity`
);
const { activityEvents } = await response.json();
```
For complete API details including all available parameters and response fields, see the [API Reference](/v2/api-reference/boost/get-events-for-a-specific-boost).
## Response Structure
The response includes:
* `activityEvents`: Array of claim events
* `totalActivityCount`: Total number of claims made on this boost
```typescript theme={null}
// Example response structure
{
"activityEvents": [
{
"claimant": "0xB0057...", // Address that claimed the incentive
"blockTimestamp": "1731373464",
"txHash": "0x2dfc8f...", // Transaction hash of the claim
"incentives": [
{
"type": "ERC20Incentive",
"asset": "0xf3b2d...", // Token address
"limit": "10",
"reward": "500000000000000000",
"strategy": 0,
"totalAmountClaimed": "",
"totalClaims": "1"
}
]
}
],
"totalActivityCount": 1
}
```
## Pagination
The API returns an empty array when there are no more activity events to fetch.
The endpoint supports pagination similar to the boost list endpoint:
```typescript theme={null}
const params = new URLSearchParams({
page: '1',
pageSize: '20' // Adjust based on your needs (max 100)
});
const response = await fetch(
`https://api-v2.boost.xyz/boosts/${boostId}/activity?${params.toString()}`
);
const { activityEvents } = await response.json();
```
# Boost Details
Source: https://docs.boost.xyz/v2/boost-api/boost-details
How to retrieve a single boost by its ID
The Boost API allows you to fetch a single boost through the [GET /boosts/:id endpoint](/v2/api-reference/boost/get-details-for-a-boost).
## Basic Usage
The `id` parameter is a combination of the chainId, the BoostCore address, and the boostId (e.g., `8453:0x378632819f39c74c4f56b1429e760739c5fb51b7:12`).
Fetch a specific boost using its ID:
```typescript theme={null}
const boostId = '8453:0x378632819f39c74c4f56b1429e760739c5fb51b7:12';
const response = await fetch(
`https://api-v2.boost.xyz/boosts/${boostId}`
);
const { boost } = await response.json();
```
For complete API details including all available parameters and response fields, see the [API Reference](/v2/api-reference/boost/get-details-for-a-boost).
# Fetch Boosts
Source: https://docs.boost.xyz/v2/boost-api/fetch-boosts
How to query for Boosts using various filters
The Boost API allows you to fetch and filter boosts through the [GET /boosts endpoint](/v2/api-reference/boost/get-a-list-of-boosts).
## Basic Usage
Fetch all boosts with no filters:
```typescript theme={null}
const response = await fetch(
'https://api-v2.boost.xyz/boosts'
);
const { boosts } = await response.json();
```
For complete API details including all available parameters and response fields, see the [API Reference](/v2/api-reference/boost/get-a-list-of-boosts).
## Filtering Boosts
The owner represents the address that initiated the boost deployment through a budget account.
You can filter boosts using various parameters:
* `chainId`: Filter by specific blockchain network
* `owner`: Filter by the address that deployed the boost
* `budgetAccount`: Filter by specific budget account
Example using multiple filters:
```typescript theme={null}
const params = new URLSearchParams({
owner: '0x1234...',
chainId: '8453', // Base
budgetAccount: '0x5678...'
});
const response = await fetch(
`https://api-v2.boost.xyz/boosts?${params.toString()}`
);
const { boosts } = await response.json();
```
## Pagination
The maximum page size is 100 items.
The endpoint uses simple pagination through `page` and `pageSize` parameters. Here's how to fetch all boosts:
```typescript theme={null}
async function fetchAllBoosts() {
const pageSize = 20; // Adjust based on your needs (max 100)
let currentPage = 1;
let allBoosts = [];
while (true) {
const params = new URLSearchParams({
page: currentPage.toString(),
pageSize: pageSize.toString()
});
const response = await fetch(
`https://api-v2.boost.xyz/boosts?${params.toString()}`
);
const { boosts } = await response.json();
// When the API returns an empty boosts array, we've fetched all boosts
if (!boosts || boosts.length === 0) {
break;
}
allBoosts = [...allBoosts, ...boosts];
currentPage++;
}
return allBoosts;
}
```
# Get Claim Signature
Source: https://docs.boost.xyz/v2/boost-api/get-signature
How to retrieve a signature required for claiming boost incentives
The Boost API provides two methods to get a signature for claiming boost incentives:
1. Use a transaction hash via the [GET /signatures endpoint](/v2/api-reference/boost/generate-a-claim-signature-that-can-be-used-to-submit-a-claim)
2. Check recent transactions for an address via the [GET /transactions endpoint](/v2/api-reference/rewardkit/try-to-get-claim-signature-without-known-transaction)
In order to produce a valid signature, the transaction must meet the boost's requirements and the transaction must occur after the boost was created.
## Fetching Signature by Transaction Hash
If you know the specific transaction that completed the boost's required action, you can fetch the signature directly:
```typescript theme={null}
const params = new URLSearchParams({
txHash: '0x2dfc8f4f24b1a3a7ee73ea13b8a20a63fe7457d1c96b95178d3ea4126b6bf5ad',
boostId: '8453:0x378632819f39c74c4f56b1429e760739c5fb51b7:12'
});
const response = await fetch(
`https://api-v2.boost.xyz/signatures?${params.toString()}`
);
const { signature, claimant, incentiveId } = await response.json();
```
### Required Parameters for /signatures
* `txHash`: The transaction hash that completed the boost's required action
* `boostId`: The ID of the boost (format: `chainId:boostNumber`)
* `claimData`: (Optional) Additional data required for variable incentive types
### Response
```typescript theme={null}
{
"signature": "0x...", // Signature for verification
"claimant": "0x...", // Address eligible to claim
"incentiveId": 0 // ID of the claimable incentive
}
```
For complete API details including all available parameters and response fields, see the [API Reference](/v2/api-reference/boost/generate-a-claim-signature-that-can-be-used-to-submit-a-claim).
## Fetching Signature by Address
If you want to check whether a wallet has completed the boost requirements, you can scan recent transactions:
```typescript theme={null}
const params = new URLSearchParams({
address: '0x1234...',
boostId: '8453:0x378632819f39c74c4f56b1429e760739c5fb51b7:12'
});
const response = await fetch(
`https://api-v2.boost.xyz/transactions?${params.toString()}`
);
const { signature, claimant, incentiveId } = await response.json();
```
For complete API details, see the [GET /transactions reference](/v2/api-reference/rewardkit/try-to-get-claim-signature-without-known-transaction).
## Using the Signature
Once you have the signature from either endpoint, you can use it with the following SDK methods to claim your boost:
* [BoostCore.claimIncentive](/v2/boost-sdk/boost-core/claim-incentive#claimincentive) - Claim for the transaction sender
* [BoostCore.claimIncentiveFor](/v2/boost-sdk/boost-core/claim-incentive#claimincentivefor) - Claim on behalf of another address
See our SDK documentation for a complete example of claiming a boost with the returned signature.
# Asset Retrieval
Source: https://docs.boost.xyz/v2/boost-sdk/asset-retrieval
Learn how to retrieve assets from a budget or incentive
The One-Time Actions docs are under active development and will be subject to changes.
There are two locations assets are stored within the Boost Protocol, [budgets](/v2/boost-sdk/budgets/managed/retrieval) and [incentives](/v2/boost-sdk/incentives/erc20/overview).
Both budgets and incentives only allow authorized users to retrieve funds, requiring the sender to either be the owner, or have the [Roles.MANAGER](https://sdk.boost.xyz/enums/Roles.html#MANAGER)
## Retrieve from Budget
### `clawback`
The clawback function is used to reclaim available funds from the budget. Can only be called by owner or admin.
```ts index.ts theme={null}
await budget.clawback({
amount: 0n,
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D',
target: account.address, // Address to receive the funds
});
```
* Using `clawback` with `0n` will reclaim all available funds for the given asset.
* Use the zero address (`0x0000000000000000000000000000000000000000`) to clawback native assets (ie. ETH).
#### Parameters
FungibleTransferPayload}>
The amount of tokens to transfer.
The address of the asset. Use zero address (0x0) for ETH transfers.
The address to receive the funds.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if the clawback was successful.
### `disburse`
Transfers available funds from the budget to a specified recipient. You must have the manager, admin or owner role to call this function.
The `disburse` method is authorized for the `MANAGER` role. Be aware that this allows a manager to disburse funds from the budget at will. Make sure you trust the manager before giving them authorization.
```ts index.ts theme={null}
import { parseUnits } from "viem";
await budget.disburse({
amount: parseUnits('1', 18),
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D',
target: account.address, // Address to receive the funds
});
```
#### Parameters
FungibleTransferPayload}>
The amount of tokens to transfer.
The address of the asset. Use zero address (0x0) for ETH transfers.
The address to receive the funds.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if the disbursement was successful.
### `disburseBatch`
Transfers available funds from the budget to a list of specified recipients. You must have the manager, admin or owner role to call this function. This method is more gas-efficient for multiple transfers.
```ts index.ts theme={null}
import { parseUnits } from "viem";
await budget.disburseBatch([
{
amount: parseUnits('1', 18),
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D',
target: recipient1.address, // Address to receive the funds
},
{
amount: parseUnits('1', 18),
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D',
target: recipient2.address, // Address to receive the funds
},
]);
```
#### Parameters
FungibleTransferPayload[]}>
The amount of tokens to transfer.
The address of the asset. Use zero address (0x0) for ETH transfers.
The address to receive the funds.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if the disbursement was successful.
## Retrieve from Incentive
### `clawbackFromTarget`
The clawback function allows authorized users (admin or manager) to reclaim assets from a Boost's incentive back to its associated budget.
The amount reclaimed will be subject to protocol fee collection, so attempting to reclaim an amount where `reclaimAmount + calculatedProtocolFee > incentiveBalance` will revert with an `InsufficientFunds` error.
For `ERC20Incentive`, the amount specified in the clawback data must be an exact multiple of the reward value set in the incentive. Otherwise, the transaction will fail.
Depending on the type of incentive, calculating the clawback amount will be different.
```ts theme={null}
// Example for fixed-reward ERC20 incentives
const boost = await core.getBoost(0n)
const budget = boost.budget
const incentiveIndex = 0
const incentive = boost.incentives.at(incentiveIndex)
if (incentive instanceof ERC20Incentive) {
// Calculate exact amount to clawback based on remaining claims
const remainingClaims = await incentive.getRemainingClaimPotential()
const rewardValue = await incentive.reward()
const clawbackAmount = remainingClaims * rewardValue
await budget.clawbackFromTarget(
core.assertValidAddress(),
incentive.buildClawbackData(clawbackAmount),
boost.id,
incentiveIndex
);
}
```
```ts theme={null}
// Example for variable-reward incentives
const boost = await core.getBoost(0n)
const budget = boost.budget
const incentiveIndex = 0
const incentive = boost.incentives.at(incentiveIndex)
if (incentive instanceof ERC20VariableIncentive ||
incentive instanceof ERC20VariableCriteriaIncentive) {
// For variable incentives, we can directly get the remaining claimable amount
const remainingFunds = await incentive.getRemainingClaimPotential()
await budget.clawbackFromTarget(
core.assertValidAddress(),
incentive.buildClawbackData(remainingFunds),
boost.id,
incentiveIndex
);
}
```
Using `clawbackFromTarget` will modify the amount of the asset marked as distributed via the budget.
#### Parameters
The address of the contract implementing `clawback`, typically the Boost Core contract address.
The encoded clawback data payload. Generate this using `incentive.buildClawbackData(amount)`.
The ID of the Boost containing the target incentive.
The index of the incentive to clawback from.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns a tuple containing:
* The amount that was successfully clawed back
* The address of the token that was retrieved
# Claiming Incentives
Source: https://docs.boost.xyz/v2/boost-sdk/boost-core/claim-incentive
The One-Time Actions docs are under active development and will be subject to changes.
```mermaid theme={null}
sequenceDiagram
actor claimant as Boost Claimant
participant signer as Signer Service
participant core as BoostCore.sol
participant validator as SignerValidator.sol
participant incentive as Incentive.sol
claimant ->> signer: submits a claim for an eligible boost
signer ->> claimant: validates claimant activity and returns a signature for the claim
claimant ->> core: submits signed claim to Boost Core
core ->> validator: validates the signature
core ->> incentive: executes the claim
incentive ->> claimant: transfers payout
```
## API
### `claimIncentive`
Claims one incentive from a boost. Must be called by claimant.
To learn how to encode the `claimDataPayload`, see the full [Claim Example](/v2/boost-sdk/examples/claim-example).
```ts theme={null}
await core.claimIncentive(
boost.id,
0n, // the position of the incentive in the incentive array
'0xCLAIMANT', // the claimant
claimDataPayload,
);
```
#### Parameters
A `bigint` representing the boost ID.
A `bigint` representing the incentive ID. The incentive ID is the index of the incentive in the `incentives` array.
An address representing the claimant of the incentive.
A hexadecimal string containing the encoded claim data payload.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
### `claimIncentiveFor`
Claim an incentive for a Boost on behalf of another user.
To learn how to encode the `claimDataPayload`, see the full [Claim Example](/v2/boost-sdk/examples/claim-example).
```ts theme={null}
await core.claimIncentiveFor(
boost.id,
0n, // the position of the incentive in the incentive array
"0xREFERER", // referrer (if any). use zero address if no referrer
claimDataPayload,
"0xCLAIMANT", // the address of the account the claim is being made for
);
```
#### Parameters
A `bigint` representing the boost ID.
A `bigint` representing the incentive ID. The incentive ID is the index of the incentive in the `incentives` array.
The address of the referrer (if any). Use zero address if no referrer.
A hexadecimal string containing the encoded claim data payload.
The address of the user eligible for the incentive payout
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
# Get Boosts
Source: https://docs.boost.xyz/v2/boost-sdk/boost-core/get-boosts
The One-Time Actions docs are under active development and will be subject to changes.
## API
### `getBoost`
Retrieves a single Boost by ID and instantiate the correct interfaces for all components.
```ts theme={null}
const boost = await core.getBoost(1n)
// You can also supply a numerical string, or hexadecimal representation
const boost = await core.getBoost(
"1" // or "0x1"
)
// Afterwards, all components will be constructed with their correct interfaces
assert(boost.budget instanceof ManagedBudget)
```
This method also executes several read calls to ascertain the correct Boost component interfaces. If you'd like to save on calls, use [core.readBoost](#readboost) instead, which returns the raw on-chain representation with component addresses instead of instantiated classes.
#### Parameters
A `bigint`, or numerical string, or hexadecimal string.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the Boost if found. Otherwise, throws `BoostNotFoundError`
### `readBoost`
Retrieves the raw on-chain representation of a Boost.
```ts theme={null}
const boost = await core.readBoost(1n)
// You can also supply a numerical string, or hexadecimal representation
const boost = await core.readBoost(
"1" // or "0x1"
)
// All fields and components are primitives
assert(isAddress(boost.budget))
const budget = await budgetFromAddress(boost.budget)
assert(budget instanceof ManagedBudget)
```
#### Parameters
A `bigint`, or numerical string, or hexadecimal string.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the Boost if found. Otherwise, throws `BoostNotFoundError`
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/boost-core/overview
Learn about how to use the Boost Core contract with the SDK
The One-Time Actions docs are under active development and will be subject to changes.
Read the smart contracts
See technical documentation
See the source
The Boost Core contract is the protocol's main touchpoint for Boosts, exposing functionality to create and retrieve Boosts, and claim incentives.
Unless you're an advanced user, you will likely never need to deploy your own `core` contract. The `SDK` comes preconfigured with the correct contract addresses for all deployed core implementations.
## Usage
```ts theme={null}
import { BoostCore } from '@boostxyz/sdk/BoostCore'
// you can also access BoostCore from the root export
import { BoostCore } from '@boostxyz/sdk'
// Basic instantiation
const core = new BoostCore({ config: wagmiConfig })
// In Node, you'll need to supply a viem local account https://viem.sh/docs/accounts/local.html
const core = new BoostCore({ config: wagmiConfig, ...(!!account && { account: viemLocalAccount }) })
```
# Cloned Components
Source: https://docs.boost.xyz/v2/boost-sdk/boost-registry/clones
The One-Time Actions docs are under active development and will be subject to changes.
Once you've initialized a component through the Boost Registry, there are several methods to help you keep track of what you've deployed.
## API
### `getCloneIdentifier`
Retrieves the unique identifier for a previously initialized clone.
```ts theme={null}
const id = await registry.getCloneIdentifier(
RegistryType.BUDGET,
ManagedBudget.bases[11155111],
'0xME',
'MyBudget'
)
```
#### Parameters
An enum representing the type of previously initialized component.
```ts theme={null}
import { RegistryType } from '@boostxyz/sdk'
RegistryType.ACTION // 0
RegistryType.ALLOW_LIST // 1
RegistryType.BUDGET // 2
RegistryType.INCENTIVE // 3
RegistryType.VALIDATOR // 4
```
The address of the base implementation your component was cloned from. You can either use [Component.bases\[chainId\]](https://sdk.boost.xyz/classes/DeployableTarget.html#bases) or [getBaseImplementation](#getbaseimplementation) to retrieve this.
The address of the user that initialized the clone.
The display name used when initializing the clone
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the clone's unique identifier
```ts theme={null}
const id = await registry.getCloneIdentifier(
RegistryType.BUDGET,
ManagedBudget.bases[11155111],
'0xME',
'MyBudget'
)
const address = await registry.getClone(id) // Address
const budget = core.ManagedBudget(address)
```
### `getClone`
Retrieves an initialized clone given its identifier.
```ts theme={null}
const id = await registry.getCloneIdentifier(
RegistryType.BUDGET,
ManagedBudget.bases[11155111],
'0xME',
'MyBudget'
)
const { instance, baseType, deployer, name } = await registry.getClone(id)
// initialize to interact with deployed budget
const budget = core.ManagedBudget(instance)
```
#### Parameters
The clone's unique identifier, which can be ascertained with [registry.getCloneIdentifier](#getcloneidentifier)
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the clone
### `getClones`
Retrieves the identifiers for all clones initialized by a given address.
```ts theme={null}
const identifiers = await registry.getClones('0xME')
const allClones = await Promise.all(identifiers.map(identifier => registry.getClone(identifier)))
```
#### Parameters
The address of the clones' deployer
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns a list of hex strings that can be used with [registry.getClone](#getclone)
### `getBaseImplementation`
Retrieves the address of an initialized clone's base implementation. This is a low-level API, and base addresses are managed for you under the hood if using Boost components provided through the SDK.
```ts theme={null}
const id = await registry.getCloneIdentifier(
RegistryType.BUDGET,
ManagedBudget.bases[11155111],
'0xME',
'MyBudget'
)
const managedBudgetBase = await registry.getBaseImplementation(id) // Address
const managedBudget = core.ManagedBudget(managedBudgetBase)
assert(managedBudget.isBase, true)
```
#### Parameters
The clone's unique identifier, which can be ascertained with [registry.getCloneIdentifier](#getcloneidentifier)
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the address of the clone's base implementation.
# Initializing Components
Source: https://docs.boost.xyz/v2/boost-sdk/boost-registry/initializing-components
The One-Time Actions docs are under active development and will be subject to changes.
Boost components aren't instantiated traditionally, with parameters supplied to the contract's constructor on deployment. Instead, to ensure each Boost component is compliant with a registered base implementation, and save on gas, components are either cloned from a deployed base contract and initialized ahead of time through registry before being referenced in Boost creation, or, for some components, initialized at time of Boost creation.
`Incentives` are not re-usable between multiple Boosts, and cannot be initialized by the registry.
## What is a base implementation?
A base implementation is any deployed contract registered with the registry, thus valid for use with Boost creation, that satisfies the interfaces for either `Action`, `AllowList`, `Budget`, `Incentive` or `Validator`. For example, [ManagedBudget](https://github.com/rabbitholegg/boost-protocol/blob/main/packages/evm/contracts/budgets/ManagedBudget.sol) is a base implementation that satisfies the [Budget](https://github.com/rabbitholegg/boost-protocol/blob/main/packages/evm/contracts/budgets/ABudget.sol) interface.
## API
### `initialize`
Deploys and initializes a new instance of a registered component's base implementation and returns the component with its address attached.
```ts theme={null}
const budget = await registry.initialize('MyNewBudget', core.ManagedBudget({
owner: '0xME',
authorized: ['0xME', core.assertValidAddress()],
roles: [Roles.ADMIN, Roles.MANAGER]
}))
```
Clones are uniquely identified by hashing the component's type, base address, your address, and name. For example, you cannot initialize two `ManagedBudget`'s with the same `displayName`
#### Parameters
A display name for the new instance. Must be unique per account and base implementation.
An instance of the component you'd like to initialize.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the target parameter with its deployed address attached.
```ts theme={null}
const budget = await registry.initialize('MyNewBudget', ...)
isAddress(budget.assertValidAddress()) // true
```
You can then reuse it during Boost creation.
```ts theme={null}
await core.createBoost({ budget, ...otherComponents })
// or you can re-use with its address if you already have it
await core.createBoost({ budget: core.ManagedBudget(budget.assertValidAddress()), ...otherComponents })
```
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/boost-registry/overview
Learn about the role the Registry plays in the SDK
The One-Time Actions docs are under active development and will be subject to changes.
Read the smart contracts
See technical documentation
See the source
[//]: # "TODO: link these somewhere"
The Boost Registry contract is a key touchpoint within the protocol, allowing users to initialize components they intend to re-use across multiple Boosts, like Budgets or AllowLists.
Unless you're an advanced user, you will likely never need to deploy your own and `registry` contract. The `SDK` comes preconfigured with the correct contract addresses for all deployed core implementations.
The registry aspect to the `Registry` is just that, a registry of valid `Action`, `AllowList`,
`Budget`, `Incentive` and `Validator` implementations that can be utilized and
extended by clients of the protocol.
Going forward, the term `component` in reference to a Boost, refers to any `Action`, `AllowList`, `Budget`, `Incentive` or `Validator`.
As opposed to upgrading the source of the protocol contracts over time to add features, the Boost team will, as needed, register new implementations that adhere to `Action`, `AllowList`, `Budget`, `Incentive` or `Validator` interfaces that can then be leveraged in the creation of new Boosts.
In most cases, you'll be interacting with the registry to initialize budgets that you can then use and reuse in your Boosts. For example,
```ts theme={null}
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { BoostRegistry } from '@boostxyz/sdk/BoostRegistry'
import { ManagedBudget, Roles } from '@boostxyz/sdk'
const registry = new BoostRegistry({ config: wagmiConfig }),
core = new BoostCore({ config: wagmiConfig })
const coreAddress = core.assertValidAddress()
// to create a new budget and grant the correct permissions to the protocol
const budget = await registry.initialize('MyNewBudget', core.ManagedBudget({
owner: '0xME',
authorized: ['0xME', coreAddress],
roles: [Roles.ADMIN, Roles.MANAGER]
}))
// or use an existing one, ensuring the protocol has the correct level of access
budget = core.ManagedBudget('0xEXISTING_BUDGET_ADDRESS')
// if you've successfully used this budget with the protocol before, you won't need to do this
if ((await budget.isAuthorized(coreAddress)) === false) {
await budget.grantRoles(coreAddress, Roles.MANAGER)
}
```
Ideally, you'll store a record of your budgets in some database, but the registry does contain some helpers to retrieve information about components you've initialized. For example, you can retrieve the address of the budget initialized above with the following flow.
```ts theme={null}
import { ManagedBudget, Roles } from '@boostxyz/sdk'
import { RegistryType } from '@boostxyz/sdk/BoostRegistry'
// get the initialized budget's unique identifier
const identifier = await registry.getCloneIdentifier(RegistryType.BUDGET, ManagedBudget.BASE, '0xME', "MyNewBudget")
// use the identifier to retrieve the deployed address
const budgetAddress = await registry.getClone(identifier)
// instantiate a `ManagedBudget` to access further SDK functionality
const budget = core.ManagedBudget(budgetAddress)
```
Any component you initialize through the registry can be retrieved this way as well by passing the address you used to interact with the registry.
```ts theme={null}
const identifiers = await registry.getClones('0xME')
const allCloneAddresses = await Promise.all(identifiers.map(identifier => registry.getClone(identifier)))
```
### What does it mean to `clone` a `base` implmentation?
Boost components aren't instantiated traditionally, with parameters supplied to the contract's constructor on deployment. Instead, to ensure each Boost component is compliant with a registered base implementation, and save on gas, components are either cloned from a deployed base contract and initialized ahead of time through registry before being referenced in Boost creation, or, for some components, initialized at time of Boost creation, like `Incentives`.
A base implementation is any deployed contract registered with the registry, thus valid for use with Boost creation, that satisfies the interfaces for either `Action`, `AllowList`, `Budget`, `Incentive` or `Validator`. For example, [ManagedBudget](https://github.com/boostxyz/boost-protocol/blob/main/packages/evm/contracts/budgets/ManagedBudget.sol) is a base implementation that satisfies the [Budget](https://github.com/boostxyz/boost-protocol/blob/main/packages/evm/contracts/budgets/ABudget.sol) interface.
# Authorization
Source: https://docs.boost.xyz/v2/boost-sdk/budgets/managed/authorization
The One-Time Actions docs are under active development and will be subject to changes.
A budget has two types of roles that can be granted: `ADMIN` and `MANAGER`. These roles can be accessed through the `Roles` enum:
```ts theme={null}
import { Roles } from "@boostxyz/sdk"
// Roles.MANAGER
// Roles.ADMIN
```
* The `MANAGER` role allows an address to manage funds within the budget and deploy boosts using available funds.
* The `ADMIN` role allows an address to grant and revoke roles on the budget as well as manage available funds.
Accounts authorized with the `MANAGER` role are allowed to disburse available funds from the budget, so make sure you trust the address before granting permissions.
## API
### `grantRoles`
Grants access control roles to an address.
```ts theme={null}
await budget.grantRoles(account.address, Roles.ADMIN);
```
You can grant both `ADMIN` and `MANAGER` roles at once:
```ts theme={null}
await budget.grantRoles(account.address, Roles.ADMIN | Roles.MANAGER);
```
#### Parameters
The address to grant the roles to.
The roles to grant, accessed via the `Roles` enum.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
### `grantManyRoles`
Grants access control roles to multiple addresses. The roles array must match the addresses array in length and order.
```ts theme={null}
await budget.grantManyRoles(
[account1.address, account2.address],
[Roles.ADMIN, Roles.MANAGER]
);
```
#### Parameters
Array of addresses to grant roles to.
Array of roles to grant, accessed via the `Roles` enum.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
### `revokeRoles`
Revokes access control roles from an address.
```ts theme={null}
await budget.revokeRoles(account.address, Roles.ADMIN);
```
You can revoke both `ADMIN` and `MANAGER` roles at once:
```ts theme={null}
await budget.revokeRoles(account.address, Roles.ADMIN | Roles.MANAGER);
```
#### Parameters
The address to revoke roles from.
The roles to revoke, accessed via the `Roles` enum.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
### `revokeManyRoles`
Revokes access control roles from multiple addresses. The roles array must match the addresses array in length and order.
```ts theme={null}
await budget.revokeManyRoles(
[account1.address, account2.address],
[Roles.ADMIN, Roles.MANAGER]
);
```
#### Parameters
Array of addresses to revoke roles from.
Array of roles to revoke, accessed via the `Roles` enum.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
### `setAuthorized`
Grants or revokes the `MANAGER` role for multiple addresses. The authorized values array must match the addresses array in length and order.
```ts theme={null}
await budget.setAuthorized([account1.address, account2.address], [true, false]);
```
#### Parameters
Array of addresses to set authorization for.
Array of boolean values where `true` grants and `false` revokes the `MANAGER` role.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
### `isAuthorized`
Checks if an address is authorized to manage the budget. Returns `true` if the address is authorized, otherwise `false`.
```ts theme={null}
await budget.isAuthorized(account.address);
```
#### Parameters
The address to check authorization for.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns `true` if the address is authorized, otherwise `false`.
### `hasAllRoles`
Checks if an address has all the roles specified. Returns `true` if the address has all the roles, otherwise `false`.
```ts theme={null}
// Check if the address has the ADMIN role
await budget.hasAllRoles(account.address, Roles.ADMIN);
// Check if the address has both the ADMIN and MANAGER roles
await budget.hasAllRoles(account.address, Roles.ADMIN | Roles.MANAGER);
```
#### Parameters
The address to check roles for.
The roles to check for, accessed via the `Roles` enum.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns `true` if the address has all the specified roles, otherwise `false`.
### `hasAnyRole`
Checks if an address has at least one of the roles specified. Returns `true` if the address has at least one of the roles, otherwise `false`.
```ts theme={null}
// Check if the address has either the ADMIN or MANAGER roles
await budget.hasAnyRole(account.address, Roles.ADMIN | Roles.MANAGER);
```
#### Parameters
The address to check roles for.
The roles to check for, accessed via the `Roles` enum.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns `true` if the address has at least one of the specified roles, otherwise `false`.
### `rolesOf`
Returns the an array of roles for a given address. Returns an empty array if the address has no roles.
`1n` = `Roles.MANAGER`, `2n` = `Roles.ADMIN`
```ts theme={null}
await budget.rolesOf(account.address);
// [ 1n, 2n ]
```
#### Parameters
The address to check roles for.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns an array of roles for the address.
### `owner`
Returns the budget owner's address.
```ts theme={null}
await budget.owner();
// "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The budget owners address.
# Funding
Source: https://docs.boost.xyz/v2/boost-sdk/budgets/managed/funding
The One-Time Actions docs are under active development and will be subject to changes.
The Managed Budget contract is designed to hold and distribute assets that can be used to deploy boosts. The Boost SDK provides several methods to manage and view the available funds in a budget.
## API
### `allocate`
Allocates assets to the budget. The caller must have already approved the contract to transfer the asset. If the asset transfer fails, the allocation will revert.
Before using `allocate`, you must approve the ManagedBudget to spend the asset you want to allocate.
```ts index.ts theme={null}
import { parseUnits } from "viem";
// Allocate assets to the budget
await budget.allocate({
amount: parseUnits('1', 18), // the amount of tokens to allocate
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D', // token address
target: account.address, // the address allocating the funds
});
```
You can also directly transfer tokens to the ManagedBudget contract address instead of using the `allocate` function. This allows you to bypass the approval process for ERC20 tokens.
#### Parameters
FungibleTransferPayload}>
The amount of tokens to transfer.
The address of the asset. Use zero address (0x0) for ETH transfers.
The account to transfer from.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if the allocation was successful.
### `available`
Shows the available balance of an asset in the budget.
```ts index.ts theme={null}
await budget.available({
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D', // token address
});
// 1000000000000000000n
```
#### Parameters
The address of the asset. Leave blank to get the balance of the native asset. (ie: ETH)
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The available balance of the asset.
### `distributed`
Get the total amount of an asset distributed from the budget.
```ts index.ts theme={null}
await budget.distributed({
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D', // token address
});
// 1000000000000000000n
```
#### Parameters
The address of the asset. Leave blank to get the distributed amount of the native asset. (ie: ETH)
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The total amount of the asset distributed from the budget.
### `total`
Get the total amount of an asset allocated to the budget, including any that have been distributed.
`total` = `available` + `distributed`
```ts index.ts theme={null}
await budget.total({
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D', // token address
});
// 1000000000000000000n
```
#### Parameters
The address of the asset. Leave blank to get the total amount of the native asset. (ie: ETH)
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The total amount of the asset allocated to the budget, including any that have been distributed.
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/budgets/managed/overview
Learn about how to interact with the Managed Budget using the SDK
The One-Time Actions docs are under active development and will be subject to changes.
The Managed Budget contract is a flexible budget implementation that allows for the distribution of tokens with role-based access control. A Managed Budget is required to deploy boosts.
In order to deploy boosts from a budget, the budget must be funded with enough ERC-20 tokens to cover the cost of the boost incentives, plus any fees.
Read the smart contracts
See technical documentation
See the source
ManagedBudget is designed to hold and distribute assets while providing fine-grained control over who can perform various operations.
## Key Features
* Role-based access control for management and administration
* Flexible allocation and distribution of assets
* Clawback functionality for asset recovery
#### Create a new ManagedBudget
**To initialize a new ManagedBudget contract:**
```ts index.ts theme={null}
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { BoostRegistry } from '@boostxyz/sdk/BoostRegistry'
import { Roles } from '@boostxyz/sdk'
import { config } from "./config";
const core = new BoostCore({ config });
const registry = new BoostRegistry({ config });
// initialize a new budget contract
const budget = await registry.initialize(
"MyBoostBudget",
core.ManagedBudget({
owner: account.address,
authorized: [account.address, core.assertValidAddress()],
roles: [Roles.ADMIN, Roles.MANAGER],
})
);
const budgetAddress = budget.assertValidAddress();
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
#### Get an existing ManagedBudget
**To get an existing ManagedBudget contract:**
```ts index.ts theme={null}
const budget = core.ManagedBudget("0xc55F719709bDad022B320E76f9DfF7e6F5680767")
// or if you want a budget from a specific chain
const budgetOnBase = core.ManagedBudget(
"0xc55F719709bDad022B320E76f9DfF7e6F5680767",
{ chainId: 8453 }
)
```
[Learn more about targeting specific chains](/v2/boost-sdk/quick-start#targeting-specific-chains)
Once a budget is created, it can be [funded](/v2/boost-sdk/budgets/managed/funding) and used to deploy boosts.
# Retrieval
Source: https://docs.boost.xyz/v2/boost-sdk/budgets/managed/retrieval
The One-Time Actions docs are under active development and will be subject to changes.
The ManagedBudget contract allows authorized users to clawback or disburse funds from the budget.
## API
### `clawback`
The clawback function is used to reclaim available funds from the budget. Can only be called by owner or admin.
```ts index.ts theme={null}
await budget.clawback({
amount: 0n,
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D',
target: account.address, // Address to receive the funds
});
```
* Using `clawback` with `0n` will reclaim all available funds for the given asset.
* Use the zero address (`0x0000000000000000000000000000000000000000`) to clawback native assets (ie. ETH).
#### Parameters
FungibleTransferPayload}>
The amount of tokens to transfer.
The address of the asset. Use zero address (0x0) for ETH transfers.
The address to receive the funds.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if the clawback was successful.
### `clawbackFromTarget`
The clawback function allows authorized users (admin or manager) to reclaim assets from a Boost's incentive back to its associated budget.
The amount reclaimed will be subject to protocol fee collection, so attempting to reclaim an amount where `reclaimAmount + calculatedProtocolFee > incentiveBalance` will revert with an `InsufficientFunds` error.
For `ERC20Incentive`, the amount specified in the clawback data must be an exact multiple of the reward value set in the incentive. Otherwise, the transaction will fail.
Depending on the type of incentive, calculating the clawback amount will be different.
```ts theme={null}
// Example for fixed-reward ERC20 incentives
const boost = await core.getBoost(0n)
const budget = boost.budget
const incentiveIndex = 0
const incentive = boost.incentives.at(incentiveIndex)
if (incentive instanceof ERC20Incentive) {
// Calculate exact amount to clawback based on remaining claims
const remainingClaims = await incentive.getRemainingClaimPotential()
const rewardValue = await incentive.reward()
const clawbackAmount = remainingClaims * rewardValue
await budget.clawbackFromTarget(
core.assertValidAddress(),
incentive.buildClawbackData(clawbackAmount),
boost.id,
incentiveIndex
);
}
```
```ts theme={null}
// Example for variable-reward incentives
const boost = await core.getBoost(0n)
const budget = boost.budget
const incentiveIndex = 0
const incentive = boost.incentives.at(incentiveIndex)
if (incentive instanceof ERC20VariableIncentive ||
incentive instanceof ERC20VariableCriteriaIncentive) {
// For variable incentives, we can directly get the remaining claimable amount
const remainingFunds = await incentive.getRemainingClaimPotential()
await budget.clawbackFromTarget(
core.assertValidAddress(),
incentive.buildClawbackData(remainingFunds),
boost.id,
incentiveIndex
);
}
```
Using `clawbackFromTarget` will modify the amount of the asset marked as distributed via the budget.
#### Parameters
The address of the contract implementing `clawback`, typically the Boost Core contract address.
The encoded clawback data payload. Generate this using `incentive.buildClawbackData(amount)`.
The ID of the Boost containing the target incentive.
The index of the incentive to clawback from.
#### Returns
Returns a tuple containing:
* The amount that was successfully clawed back
* The address of the token that was retrieved
### `disburse`
Transfers available funds from the budget to a specified recipient. You must have the manager, admin or owner role to call this function.
The `disburse` method is authorized for the `MANAGER` role. Be aware that this allows a manager to disburse funds from the budget at will. Make sure you trust the manager before giving them authorization.
```ts index.ts theme={null}
import { parseUnits } from "viem";
await budget.disburse({
amount: parseUnits('1', 18),
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D',
target: account.address, // Address to receive the funds
});
```
#### Parameters
FungibleTransferPayload}>
The amount of tokens to transfer.
The address of the asset. Use zero address (0x0) for ETH transfers.
The address to receive the funds.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if the disbursement was successful.
### `disburseBatch`
Transfers available funds from the budget to a list of specified recipients. You must have the manager, admin or owner role to call this function. This method is more gas-efficient for multiple transfers.
```ts index.ts theme={null}
import { parseUnits } from "viem";
await budget.disburseBatch([
{
amount: parseUnits('1', 18),
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D',
target: recipient1.address, // Address to receive the funds
},
{
amount: parseUnits('1', 18),
asset: '0xf3B2d0E4f2d8F453DBCc278b10e88b20d7f19f8D',
target: recipient2.address, // Address to receive the funds
},
]);
```
#### Parameters
FungibleTransferPayload[]}>
The amount of tokens to transfer.
The address of the asset. Use zero address (0x0) for ETH transfers.
The address to receive the funds.
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if the disbursement was successful.
# Boost a Vote on Agora
Source: https://docs.boost.xyz/v2/boost-sdk/examples/agora-vote
Learn how to reward users with ERC20 rewards when they vote for/against a specific proposal
In this example, you want a certain proposal to pass/fail, and want to incentivize users to swing votes in your favor by offering additional rewards.
See the full example code
Make sure you've completed the [quick start](/v2/boost-sdk/quick-start) guide before proceeding with this example.
## Set up a Managed Budget
First you'll need to either initialize a new budget, or use an existing one, and allow the budget to spend a certain amount of your selected ERC20.
```ts index.ts theme={null}
import {
BoostCore,
BoostRegistry,
ManagedBudget,
Roles
} from '@boostxyz/sdk'
import { parseUnits } from 'viem'
import { config as wagmiConfig } from './config'
// Initialize the BoostCore and BoostRegistry modules
const registry = new BoostRegistry({ config: wagmiConfig })
const core = new BoostCore({ config: wagmiConfig })
// Get the address of the BoostCore module
const coreAddress = core.assertValidAddress()
// Create a new budget and grant the correct permissions to the protocol
const budget = await registry.initialize('MyNewBudget', core.ManagedBudget({
owner: '0xME',
authorized: ['0xME', coreAddress],
roles: [Roles.ADMIN, Roles.MANAGER]
}))
// scaffold your desired incentive to help calculate the total amount required to fund your budget
const erc20Incentive = core.ERC20Incentive({
asset: erc20.assertValidAddress(),
reward: parseEther('0.1'),
limit: parseEther('1'),
strategy: StrategyType.POOL,
})
const budgetedAmount = await erc20Incentive.getTotalBudget() // 10 WETH
const amountIncludingFees = await core.calculateProtocolFee(budgetedAmount)
// Allow the budget to spend your ERC20 token
await writeContract(wagmiConfig, {
abi,
address: erc20.assertValidAddress(), // The address of the ERC20 token you want to approve
functionName: 'approve',
args: [
budget.assertValidAddress(),
amountIncludingFees // The amount of ERC20 token you want to approve
]
})
// Allocate assets to the budget
await budget.allocate({
amount: amountIncludingFees,
asset: '0xERC20',
target: '0xME',
})
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
## Define The Action
Next you'll need to define the action that qualifies a user to claim the reward.
For Agora, we want to reward users that vote for a specific proposal on Agora. To do this, we'll key off of the `VoteCast` event,
which has the event signature `VoteCast(address indexed,uint256,uint8,uint256,string)`.
There is also a `VoteCastWithParams` event. The SDK can only validate one at a time (for now), so this tutorial will just check for the `VoteCast` event.
A workaround for this is to create one boost for `VoteCast` and another boost for `VoteCastWithParams`.
Here is how you can structure the EventActionPayload to target the `VoteCast` event.
If you're using a known event, you can use the selectors package to get the signature.
```ts theme={null}
import { selectors } from '@boostxyz/signatures/events'
const selector = selectors[
'VoteCast(address indexed,uint256,uint8,uint256,string)'
] as Hex;
```
```ts theme={null}
import {
EventActionPayload,
ActionStep,
ActionClaimant,
SignatureType,
FilterType,
PrimitiveType
} from '@boostxyz/sdk/Actions/EventAction'
import { selectors } from '@boostxyz/signatures/events'
import { toHex } from 'viem'
const targetContract = '0xcdf27f107725988f2261ce2256bdfcde8b382b10'
// If one exists, use the signature from the selectors package
const selector = selectors[
'VoteCast(address indexed,uint256,uint8,uint256,string)'
] as Hex;
// These EventAction steps outlines the criteria that the validator uses to determine eligibility for reward redemption.
const filterProposalIdStep: ActionStep = {
chainid: optimism.id,
signature: selector, // VoteCast event signature
signatureType: SignatureType.EVENT, // We're working with an event
targetContract: targetContract, // Address of the ERC20 contract
// We want to target the ProposalId property on the VoteCast event
actionParameter: {
filterType: FilterType.EQUAL, // Filter to check for equality
fieldType: PrimitiveType.UINT, // The field we're filtering is a uint
fieldIndex: 1, // Targeting the 'proposalId' uint
filterData: toHex(
BigInt(
'54194543592303757979358957212312678549449891089859364558242427871997305750980',
),
), // Filtering based on the proposal id
},
};
const filterSupportStep: ActionStep = {
chainid: optimism.id,
signature: selector, // VoteCast event signature
signatureType: SignatureType.EVENT, // We're working with an event
targetContract: targetContract, // Address of the ERC20 contract
// We want to target the Support property on the VoteCast event
actionParameter: {
filterType: FilterType.EQUAL, // Filter to check for equality
fieldType: PrimitiveType.UINT, // The field we're filtering is a uint
fieldIndex: 2, // Targeting the 'support' uint
filterData: toHex(1n, { size: 1 }), // Filtering based on the support value (uint8 is 1 byte)
},
};
```
Next, we need to define the action claimant and create the payload for the new event action.
The `eventAction` payload consists of the `actionClaimant` and the `actionSteps` we defined previously.
The purpose of the `eventAction` is to track and reward users based on their interactions with the specified event.
1. **actionClaimant**: This object defines the conditions under which a user is eligible to claim rewards. It includes:
* `signatureType`: Specifies that the signature type is an event.
* `signature`: The event signature we are targeting, in this case, the `VoteCast` event.
* `fieldIndex`: Indicates which field in the event data we are interested in; here, it targets the 'voter' address (the address that initiated the vote).
* `targetContract`: The address of the contract we are monitoring for the event.
2. **actionSteps**: This array can contain up to four action steps that outline the specific actions or conditions that must be met for the event action to be valid. In this example, we include the previously defined `eventActionStepOne` and `eventActionStepTwo`.
Next, we use `core.EventAction` to set up the event action with our constructed `eventActionPayload`.
```ts theme={null}
const eventActionPayload = {
actionClaimant: {
chainid: optimism.id,
signatureType: SignatureType.EVENT,
signature: selector, // VoteCast(address,uint256,uint8,uint256,string) event signature
fieldIndex: 0, // Targeting the 'voter' address
targetContract: targetContract, // The Agora vote contract we're monitoring
},
actionSteps: [filterProposalIdStep, filterSupportStep] // use can place up to 4 action steps
};
// Initialize EventAction with the custom payload
const eventAction = core.EventAction(eventActionPayload);
```
## Deploy the Boost
Once the event action is created, we can set up the incentives and deploy our boost.
```ts theme={null}
// This allows for participants to be rewarded with up to 10 wei of the reward asset.
//The maximum amount of the rewards distributed by this incentive in this boost would be 100 wei
const incentives = [
core.ERC20VariableIncentive({
asset: erc20.assertValidAddress(),
reward: parseEther('0.1'),
limit: parseEther('1'),
}),
],
```
```ts theme={null}
// Deploy the boost
const boost = await core.createBoost({
maxParticipants: 100n, // Set a max number of participants
budget: budget, // Use the ManagedBudget we set up earlier
action: eventAction, // Pass the manually created EventAction
incentives: incentives,
allowList: core.OpenAllowList(),
});
```
`core.OpenAllowList()` makes this boost available to all addresses. If you want to limit it to a specific address, you can use a `SimpleAllowList`.
## Claiming Incentives
Once a claimant has successfully voted, you can claim the incentive for that claimant. You will need the address of the user that cast the vote, the hash of the vote cast transaction, and the boost id.
But first, you'll need to implement some logic to determine the variable reward amount.
`ERC20VariableIncentive` uses off-chain logic to determine the reward amount. If you would like to base your reward on on-chain logic, you can use the `ERC20VariableCriteriaIncentive` type.
```ts theme={null}
// Check the amount of votes the claimant has at the time they made the tx to vote
const getVotesAbi = functions.abi[functions.selectors['getVotes(address account, uint256 blockNumber) view returns (uint256)'] as '0x00000000000000000000000000000000000000000000000000000000eb9019d4']
const amountOfVotes = (await walletClient.readContract({
address: '0xcdf27f107725988f2261ce2256bdfcde8b382b10',
abi: [getVotesAbi],
functionName: 'getVotes',
args: [claimant, 127417170n], // claimant is the address of the user that cast the vote
})) as bigint;
// If the amountOfVotes is greater than 100, then the reward should be 0.1 ETH, otherwise it will be 0.01 ETH
const rewardAmount =
amountOfVotes >= parseEther('100')
? parseEther('0.1')
: parseEther('0.01');
```
Once the reward amount is determined, we can generate the signature payload to allow us to claim the reward for the claimant.
To generate the signature payload you will need to call the Boost `/signatures` api endpoint with the following params:
* `boostId`: The id of the boost where the action was completed. The format is `chainId:boostId` (e.g. `8453:0x378632819f39c74c4f56b1429e760739c5fb51b7:12`)
* `txHash`: The hash of the transaction that completed the action.
* `claimData`: This is only nessecary for variableIncentives. For this parameter you would pass in the `rewardAmount` which is used to determine the amount of the incentive to claim.
If you have more than one variable incentive you can pass in a comma-separated list of claimData values.
The signatures api will return an array of signatures, one for each available incentive on the boost.
See additional documentation on how to build a claim signature
```ts theme={null}
import axios from 'axios';
const { data } = await axios.get(`${BOOST_API_URL}/signatures`, {
params: {
boostId: `${chainId}:${boostCoreAddress}:${boost.id}`,
txHash,
claimData: rewardAmount.toString(),
}
});
for (const item of data) {
const { signature, incentiveId, claimant } = item;
// Claim the incentive for the claimant
await core.claimIncentiveFor(
boost.id,
incentiveId,
referrer,
signature,
claimant,
);
}
```
There can be multiple incentives to claim in a single boost. The example shown only has one incentive.
The boost will stay active until the `maxParticipants` set for the boost is reached, or the incentive budget is fully spent.
# Claim Rewards from a Boost
Source: https://docs.boost.xyz/v2/boost-sdk/examples/claim-example
Learn how to claim rewards from a Boost
In this example, you'll learn how to claim a reward from a Boost.
To claim a reward from a Boost, you'll need to follow these general steps:
1. Retrieve the Boost you want to claim from
2. Prepare the claim data payload
3. Execute the claim transaction
This process will differ depending on if you have set up your own validator or are using the default validator.
If using the default validator, you will need to call the [Boost API `/signatures` endpoint](/v2/boost-api/get-signature) to generate the signature payload,
otherwise you will need to build the payload yourself.
The default validator will be always be used by default unless you specify your own validator on boost creation.
In order to claim an incentive from a boost using the default validator, you will need to generate a valid signature payload.
You can do this by calling the boost api `/signatures` endpoint and passing in the boostID and txHash.
You can also generate a signature by providing a wallet address instead of a transaction hash using the `/transactions` endpoint.
See [Fetching Signature by Address](/v2/boost-api/get-signature#fetching-signature-by-address) for details.
This example assumes you have already instantiated a `BoostCore` client and have a valid transaction hash for the action that the claimant completed.
See additional documentation on how to build a claim signature
### Generate the Signature Payload
```ts theme={null}
import axios from 'axios';
const chainId = 8453; // base mainnet
const boost = await core.getBoost(3n, { chainId })
const boostCoreAddress = core.assertValidAddress()
const { data } = await axios.get(`${BOOST_API_URL}/signatures`, {
params: {
boostId: `${chainId}:${boostCoreAddress}:${boost.id}`,
txHash,
}
});
```
The `/signatures` endpoint will return an array of signatures, one for each available incentive on the boost.
Each item in the array will contain the following fields:
* `signature`: The signature to be used in the claim transaction
* `incentiveId`: The id of the incentive being claimed
* `claimant`: The address of the claimant
If using `ERC20VariableIncentive` or `ERC20VariableCriteriaIncentive`, you will need to pass in the `rewardAmount` as `claimData` to the `/signatures` endpoint.
Here's an example of how you would accomplish that with multiple incentives.
```ts theme={null}
const allClaimData = boost.incentives.map(incentive => {
if (incentive instanceof ERC20VariableIncentive || incentive instanceof ERC20VariableCriteriaIncentive) {
const rewardAmount = parseUnits("10", 18);
return incentive.buildClaimData(rewardAmount);
} else {
return ''
}
})
const { data } = await axios.get(`${BOOST_API_URL}/signatures`, {
params: {
boostId: `${chainId}:${boostCoreAddress}:${boost.id}`,
txHash,
claimData: allClaimData.join(',')
}
});
```
### Claiming the incentives
Once you have a valid response from the `/signatures` endpoint, you can loop through the response and claim the incentives.
```ts index.ts {13-22} theme={null}
import axios from 'axios';
const chainId = 8453; // base mainnet
const boost = await core.getBoost(3n, { chainId })
const boostCoreAddress = core.assertValidAddress()
const { data } = await axios.get(`${BOOST_API_URL}/signatures`, {
params: {
boostId: `${chainId}:${boostCoreAddress}:${boost.id}`,
txHash,
}
});
for (const item of data) {
const { signature, incentiveId, claimant } = item;
// Allow the claimant to claim the incentive
await core.claimIncentive(
boost.id,
incentiveId,
claimant,
signature,
);
}
```
`claimIncentive` must be called by the claimant. This is useful if you have access to the claimants wallet, like in the case of a dapp.
If you are claiming on behalf of another user, you must use `claimIncentiveFor`.
You can also claim on behalf of another user by using the `claimIncentiveFor` method.
```ts theme={null}
// Claim on behalf of another user
await core.claimIncentiveFor(
boost.id,
incentiveId, // the incentiveId (position in the array of incentives)
"0xREFERRER", // referrer (if any). use zero address if no referrer
signature,
claimant, // the address of the account the claim is being made for
);
```
[//]: # "TODO: link to validator page when it's published to provde more context"
[//]: # "TODO: show how to validate actions without using the api"
```ts theme={null}
// get the boost you want to claim from
const boost = await core.getBoost(1n)
// get an incentive from the boost
const incentiveId = 0n; // the position in the incentives array
const incentive = boost.incentives[incentiveId]
// prepare the incentive payload
let incentivePayload: Hex;
// variable reward incentives require the reward amount to be provided in buildClaimData
if (incentive instanceof ERC20VariableIncentive ||
incentive instanceof ERC20VariableCriteriaIncentive) {
const rewardAmount = parseUnits("10", 18);
incentivePayload = incentive.buildClaimData(rewardAmount);
} else {
incentivePayload = incentive.buildClaimData();
}
// encode claimData payload
const claimDataPayload = await boost.validator.encodeClaimData({
signer: signer.account,
incentiveData: incentivePayload,
chainId: 8453,
incentiveQuantity: 1,
claimant: "0xCLAIMANT", // the address of the account claiming the incentive
boostId: boost.id,
});
// claim the incentive from the boost
await core.claimIncentive(
boost.id,
incentiveId, // the incentiveId (position in the array of incentives)
"0xCLAIMANT", // the claimant
claimDataPayload,
);
```
The claimant must be authorized to claim the incentive (e.g., on allowlist)
You can also claim on behalf of another user by using the `claimIncentiveFor` method.
```ts theme={null}
await core.claimIncentiveFor(
boost.id,
0n, // the incentiveId (position in the array of incentives)
"0xREFERRER", // referrer (if any). use zero address if no referrer
claimDataPayload,
"0xCLAIMANT", // the address of the account the claim is being made for
);
```
# Deploy a Boost
Source: https://docs.boost.xyz/v2/boost-sdk/examples/deploy-example
Learn how to deploy a Boost using the Boost SDK
In this example, you'll learn how to deploy a Boost using the Boost SDK.
Before you deploy a boost, you will need to have a budget account deployed and funded with the tokens you want to distribute.
If you don't have one, you can deploy one using the [SDK](/v2/boost-sdk/budgets/managed/overview#create-a-new-managedbudget), or by following
the [Budget Accounts guide](/v2/documentation/getting-started/setting-up-accounts) to deploy one through the Boost interface.
Below is a full example of how to deploy a Boost using the SDK. The action in this example targets
the Zora TimedSalesStrategy contract. The main steps to deploying a boost are:
1. Setting up the Action
2. Setting up the Incentives
3. Deploying the Boost
You can also optionally set up an AllowList, which is useful if you want to limit the participants of the boost.
This example uses the `OpenAllowList`, which allows anyone to participate.
```typescript index.ts theme={null}
import { BoostCore } from "@boostxyz/sdk";
import { config } from "./config";
import { eventActionPayload } from "./eventAction";
const core = new BoostCore(config);
const budget = core.ManagedBudget('0x...');
const action = core.EventAction(eventActionPayload);
const incentives: [
core.ERC20Incentive({
asset: '0x...',
reward: parseEther('0.5'),
limit: 10n,
strategy: StrategyType.POOL,
manager: '0x...',
}),
]
(async () => {
await core.deployBoost(
{
maxParticipants: 10n,
budget,
action,
incentives,
allowList: core.OpenAllowList(),
},
)
})();
```
```typescript eventAction.ts theme={null}
import {
ActionStep,
SignatureType,
FilterType,
PrimitiveType,
ActionClaimant,
} from "@boostxyz/sdk/Actions/EventAction";
import { selectors as funcSelectors } from "@boostxyz/signatures/functions";
import { Hex, toHex } from "viem";
import { zora } from "viem/chains";
const targetContract = "0x777777722d078c97c6ad07d9f36801e653e356ae"; // Zora TimedSalesStrategy
const mintSelector = funcSelectors[
"mint(address mintTo,uint256 quantity,address collection,uint256 tokenId,address mintReferral,string comment)"
] as Hex;
const commonParams = {
chainid: zora.id,
signature: mintSelector,
signatureType: SignatureType.FUNC,
targetContract: targetContract,
} as const;
const collectionActionStep: ActionStep = {
...commonParams,
actionParameter: {
filterType: FilterType.EQUAL,
fieldType: PrimitiveType.ADDRESS,
fieldIndex: 2, // collection
filterData: "0x3263023c87502f1676f00df902b1237f93da26a9",
},
};
const tokenIdActionStep: ActionStep = {
...commonParams,
actionParameter: {
filterType: FilterType.EQUAL,
fieldType: PrimitiveType.UINT,
fieldIndex: 3, // tokenId
filterData: toHex(1),
},
};
const quantityActionStep: ActionStep = {
...commonParams,
actionParameter: {
filterType: FilterType.GREATER_THAN_OR_EQUAL,
fieldType: PrimitiveType.UINT,
fieldIndex: 1, // quantity
filterData: toHex(1),
},
};
const actionClaimant: ActionClaimant = {
...commonParams,
fieldIndex: 0, // mintTo
};
export const eventActionPayload = {
actionClaimant,
actionSteps: [collectionActionStep, tokenIdActionStep, quantityActionStep],
};
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
# Boost an NFT Mint on Zora
Source: https://docs.boost.xyz/v2/boost-sdk/examples/zora-mint
Learn how to reward users with ERC20 rewards when they mint an NFT on Zora.
In this example, you've created an NFT on Zora, and want to incentivize users to mint it by offering additional rewards.
Make sure you've completed the [quick start](/v2/boost-sdk/quick-start) guide before proceeding with this example.
See the full example code
## Set up a Managed Budget
First you'll need to either initialize a new budget, or use an existing one, and allow the budget to spend a certain amount of your selected ERC20.
```ts index.ts theme={null}
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { BoostRegistry } from '@boostxyz/sdk/BoostRegistry'
import { ManagedBudget, Roles } from '@boostxyz/sdk'
import { parseUnits, erc20Abi } from 'viem'
import { wagmiConfig } from '../config'
// Initialize the BoostCore and BoostRegistry modules
const registry = new BoostRegistry({ config: wagmiConfig })
const core = new BoostCore({ config: wagmiConfig })
// Get the address of the BoostCore module
const coreAddress = core.assertValidAddress()
// Create a new budget and grant the correct permissions to the protocol
const budget = await registry.initialize('MyBudget', core.ManagedBudget({
owner: "0xME",
authorized: ["0xME", coreAddress], // authorized addresses
roles: [Roles.ADMIN, Roles.MANAGER] // roles that the budget can assign to the protocol
}))
// scaffold your desired incentive to help calculate the total amount required to fund your budget
const erc20Incentive = core.ERC20Incentive({
asset: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
reward: parseEther('1'),
limit: 10n,
strategy: StrategyType.POOL,
})
const budgetedAmount = await erc20Incentive.getTotalBudget() // 10 WETH
const amountIncludingFees = await core.calculateProtocolFee(budgetedAmount)
// Allow the budget to spend your ERC20 token.
await writeContract(wagmiConfig, {
abi: erc20Abi,
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // The address of the ERC20 token you want to approve
functionName: 'approve',
args: [
budget.assertValidAddress(), // The address of the budget account
budgetedAmount // The amount of ERC20 token you want to approve, including protocol fee
],
})
// Allocate assets to the budget
await budget.allocate({
amount: budgetedAmount
asset: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
target: "0xME",
})
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
## Define The Action
Next you'll need to define the action that qualifies a user to claim the reward.
For Zora, we want to reward users that mint a specific NFT on Zora. To do this, we'll key off of the `Purchased` event,
which has the event signature `Purchased(address indexed,address indexed,uint256 indexed,uint256,uint256)`.
Here is how you can structure the EventActionPayload to target the `Purchased` event.
The important parameters to target in the event data are the tokenId and the NFT contract address.
If you're using a known event, you can use the selectors package to get the signature.
```ts theme={null}
import { selectors } from '@boostxyz/signatures/events'
const selector = selectors[
'Purchased(address indexed,address indexed,uint256 indexed,uint256,uint256)'
] as Hex;
```
[Here is an example of the Purchased event](https://basescan.org/tx/0x17a4d7e08acec16f385d2a038b948359919e3675eca22a09789b462a9178a769#eventlog#187)
```ts theme={null}
import {
EventActionPayload,
ActionStep,
ActionClaimant,
SignatureType,
FilterType,
PrimitiveType
} from '@boostxyz/sdk/Actions/EventAction'
import { selectors } from '@boostxyz/signatures/events'
// Use the Purchased signature from the selectors package
const selectedSignature = selectors[
'Purchased(address indexed,address indexed,uint256 indexed,uint256,uint256)'
] as Hex;
// The EventAction step outlines the criteria that the validator uses to determine eligibility for reward redemption.
const eventActionStep: ActionStep = {
signature: selectedSignature,
signatureType: SignatureType.EVENT,
targetContract: '0x9D2FC5fFE5939Efd1d573f975BC5EEFd364779ae', // The address of the Zora NFT contract
// We want to target the `tokenId` property on the Purchase event
actionParameter: {
filterType: FilterType.EQUAL, // Filter to check for equality
fieldType: PrimitiveType.UINT, // The field we're filtering is the tokenId
fieldIndex: 2, // The index of the tokenId in the event data
filterData: toHex(3n, { size: 1 }), // Targetting tokenId 3 on the collection (filter data must be in hex)
},
};
```
Next, we need to define the action claimant and create the payload for the new event action.
The `eventAction` payload consists of the `actionClaimant` and the `actionSteps` we defined previously.
The purpose of the `eventAction` is to track and reward users based on their interactions with the specified event.
1. **actionClaimant**: This object defines the conditions under which a user is eligible to claim rewards. It includes:
* `signatureType`: Specifies that the signature type is an event.
* `signature`: The event signature we are targeting, in this case, the `Purchased` event.
* `fieldIndex`: Indicates which field in the event data we are interested in; We can target any field that contains the claimants address.
* `targetContract`: The address of the contract we are monitoring for the event.
2. **actionSteps**: This array can contain up to four action steps that outline the specific actions or conditions that must be met for the event action to be valid. In this example, we include the previously defined `eventActionStep`.
Next, we create an `EventAction`, passing in the constructed `eventActionPayload`.
```ts theme={null}
const eventActionPayload = {
actionClaimant: {
signatureType: SignatureType.EVENT,
signature: selectedSignature, // Purchased event signature
fieldIndex: 0, // Targeting the 'sender' argument which is the address that initiated the purchase
targetContract: '0x9D2FC5fFE5939Efd1d573f975BC5EEFd364779ae', // The address of the Zora NFT contract
},
actionSteps: [eventActionStep] // use can place up to 4 action steps
};
// create the EventAction with the custom payload
const eventAction = core.EventAction(eventActionPayload);
```
## Deploy the Boost
Once the event action is created, we can set up the incentives and deploy our boost.
```ts theme={null}
import { StrategyType } from '@boostxyz/sdk/Incentive'
// This allows for 10 participants to be rewarded with 1 token each
const incentives = [
core.ERC20Incentive({
asset: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
reward: parseEther('1'),
limit: 10n,
strategy: StrategyType.POOL,
}),
],
```
```ts theme={null}
// Deploy the boost
const boost = await core.createBoost({
maxParticipants: 10n, // Set a max number of participants
budget: budget, // Use the ManagedBudget we set up earlier
action: eventAction, // Pass the manually created EventAction
incentives: incentives,
allowList: core.OpenAllowList(),
});
```
## Claiming Incentives
Once a claimant has successfully completed the action, you can claim the incentive for that claimant.
`ERC20Incentive` is a fixed reward incentive. If you would like to have a variable reward based on on-chain logic, you can use the `ERC20VariableCriteriaIncentive` type.
In order to successfully claim the reward for a claimant, you will need to generate the signature payload.
To generate the signature payload you will need to call the Boost `/signatures` api endpoint with the following params:
* `boostId`: The id of the boost where the action was completed. The format is `chainId:boostId` (e.g. `8453:0x378632819f39c74c4f56b1429e760739c5fb51b7:12`)
* `txHash`: The hash of the transaction that completed the action.
The signatures api will return an array of signatures, one for each available incentive on the boost.
See additional documentation on how to build a claim signature
```ts theme={null}
import axios from 'axios';
const { data } = await axios.get(`${BOOST_API_URL}/signatures`, {
params: {
boostId: `${chainId}:${boostCoreAddress}:${boost.id}`,
txHash,
}
});
for (const item of data) {
const { signature, incentiveId, claimant } = item;
// Claim the incentive for the claimant
await core.claimIncentiveFor(
boost.id,
incentiveId,
referrer,
signature,
claimant,
);
}
```
There can be multiple incentives to claim in a single boost. The example shown only has one incentive.
The boost will stay active until the `maxParticipants` set for the boost is reached.
# Claims
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/cgda/claims
Retrieve ERC20 Incentive claim information and draw raffle
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Incentive is designed to store and distribute assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about claim state, as well as drawing raffles.
## API
### `claims`
Retrieves the total number of completed claims.
```ts index.ts theme={null}
await cgdaIncentive.claims();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total number of claims.
### `claimed`
Checks if an address has previously claimed this incentive.
```ts index.ts theme={null}
await cgdaIncentive.claimed(address);
```
#### Parameters
The address to check claimed status.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address has claimed the incentive.
### `isClaimable`
Check if an incentive is claimable by an address. Will return false if the recipient has already claimed, or incentive does not have the balance to cover the current reward.
```ts index.ts theme={null}
await cgdaIncentive.isClaimable(address);
```
#### Parameters
The address to check eligibility for.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address can claim incentive.
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/cgda/overview
Learn about how to interact with a Continuous Gradual Dutch Auction Incentive using the SDK
The Continuous Gradual Dutch Auction Incentive is a protocol incentive that allows for rewards that adjust dynamically based on claim volume. [See this document for more information on gradual dutch auctions](https://moallemi.com/ciamac/papers/pricing-dutch-auctions-2024.pdf). On Boost creation, the incentive's total budget of the specified asset is transferred into this incentive from the associated budget.
The One-Time Actions docs are under active development and will be subject to changes.
Read the smart contracts
See technical documentation
See the source
## Key Features
* Supports native assets as well as any ERC20
* Rewards programmatically decline after each claim
* Rewards programmatically increase each hour without a claim
Unlike budgets, allowlists, and validators, incentives cannot be re-used between multiple Boosts.
### Create a new CGDAIncentive
```ts index.ts theme={null}
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { config } from "./config";
const core = new BoostCore({ config });
await core.createBoost({
maxParticipants: 10n,
budget,
action,
allowList,
incentives: [
core.CGDAIncentive({
asset: '0xERC20', // The address of the underlying asset, or zeroAddress for native token
initialReward: 5n, // The initial amount for the first claim
totalBudget: 10n, // The total amount distributable by this incentive
rewardBoost: 1n, // How much to increase the reward each hour
rewardDecay: 1n, // How much to subtract from the reward each hour
manager: '0xBUDGET_ADDRESS',
}),
],
});
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
### Get an existing CGDAIncentive
```ts index.ts theme={null}
// if an incentive address is known, directly construct it
const incentive = core.CGDAIncentive("0xc55F719709bDad022B320E76f9DfF7e6F5680767")
// or if you want a budget from a specific chain
const incentiveOnBase = core.CGDAIncentive(
"0xc55F719709bDad022B320E76f9DfF7e6F5680767",
{ chainId: 8453 }
)
// or accessible off of a a pre-exiting Boost
const boost = await core.getBoost(0n)
const incentive = boost.incentives.find(incentive => incentive instanceof CGDAIncentive)
```
# Rewards
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/cgda/rewards
Retrieve information about ERC20 Incentive rewards
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Variable Incentive is designed to store and distribute dynamic amounts of assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about the current state of its rewards.
## API
### `asset`
Retrieves the address of the ERC20-like asset to be rewarded on claim.
```ts index.ts theme={null}
await cgdaIncentive.asset();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The address of the asset transferred on claim
### `currentReward`
Calculates the current reward based on the time since the last claim.
The reward is calculated based on the time since the last claim, the available budget, and the reward parameters. It increases linearly over time in the absence of claims, with each hour adding `rewardBoost` to the current reward, up to the available budget.
For example, if there is one claim in the first hour, then no claims for three hours, the claimable reward would be `initialReward - rewardDecay + (rewardBoost * 3)`
```ts index.ts theme={null}
await cgdaIncentive.currentReward()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns reward as of this moment in time
### `totalBudget`
Retrieves the total amount this incentive is allowed to distribute.
```ts index.ts theme={null}
await cgdaIncentive.totalBudget()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the budget
### `cgdaParams`
Retrieves the total amount this incentive is allowed to distribute.
```ts index.ts theme={null}
const { rewardDecay, rewardBoost, lastClaimTime, currentReward } = await cgdaIncentive.cgdaParams()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
CGDAParameters}>
The amount to subtract from the current reward after each claim
The amount by which the reward increases for each hour without a claim (continuous linear increase)
The timestamp of the last claim
The current reward amount
# Claims
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-pegged-variable-criteria/claims
Retrieve ERC20PeggedIncentive claim information
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20PeggedVariableCriteriaIncentive is designed to store and distribute assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about claim state.
## API
### `claims`
Retrieves the total number of completed claims.
```ts index.ts theme={null}
await erc20PeggedVariableCriteriaIncentive.claims();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total number of claims.
### `claimed`
Checks if an address has previously claimed this incentive.
```ts index.ts theme={null}
await erc20PeggedVariableCriteriaIncentive.claimed(address);
```
#### Parameters
The address to check claimed status.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address has claimed the incentive.
### `isClaimable`
Check if an incentive is claimable by an address. Will return false if the recipient has already claimed, or total incentive claims has met the configured limit.
```ts index.ts theme={null}
await erc20PeggedVariableCriteriaIncentive.isClaimable(address);
```
#### Parameters
The address to check eligibility for.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address can claim incentive.
### `limit`
The total number of tokens (in wei) that can be claimed from this incentive.
```ts index.ts theme={null}
await erc20PeggedVariableCriteriaIncentive.limit();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the maximum number of tokens that can be claimed from this incentive.
### `getRemainingClaimPotential`
Retrieves the number of remaining possible claims for an incentive by comparing total claims against the limit.
Note that this does not check if a specific user is eligible to claim.
```ts index.ts theme={null}
await erc20PeggedVariableCriteriaIncentive.getRemainingClaimPotential();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the number of remaining possible claims (limit minus current claims)
### `canBeClaimed`
Checks if any claims remain for an incentive by comparing total claims against the limit.
Returns a boolean indicating if claims are still possible.
Note that this does not check if a specific user is eligible to claim.
```ts index.ts theme={null}
await erc20PeggedVariableCriteriaIncentive.canBeClaimed();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns `true` if there are remaining claims available, `false` otherwise
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-pegged-variable-criteria/overview
Learn about how to interact with an ERC20PeggedVariableCriteriaIncentive using the SDK
The ERC20 Pegged Variable Criteria Incentive is a specialized protocol incentive that allows for the distribution of ERC-20 assets with rewards pegged to the value of another token, where the reward amount is determined by transaction criteria. This enables creating dynamic rewards that maintain a specific value relative to a pegged asset, with the actual reward amount varying based on transaction data.
The One-Time Actions docs are under active development and will be subject to changes.
Read the smart contracts
See technical documentation
See the source
## Key Features
* Rewards pegged to the value of another token
* Dynamic reward amounts based on transaction criteria
* Variable reward calculation using event or function data
* Maximum reward cap per claim
* Total distribution limit in reward asset tokens
* Managed access for incentive control and clawbacks
Unlike budgets, allowlists, and validators, incentives cannot be re-used between multiple Boosts.
To peg rewards to the chain's native token (e.g., ETH), use `zeroAddress` (0x0000000000000000000000000000000000000000) as the peg parameter. This tells the protocol to use the native token's price for calculations.
## Setting Up Incentive Criteria
The incentive criteria determines how the reward amount is extracted from transaction data. You can configure it to read values from either contract events or function calls.
### Criteria Configuration
The criteria object requires four fields:
* `criteriaType`: Either `SignatureType.EVENT` for event logs or `SignatureType.FUNC` for function calls
* `signature`: The event or function signature to match against
* `fieldIndex`: Which parameter in the event/function to extract the value from (0-based index)
* `targetContract`: The contract address to watch for the event/function
You can use the `@boostxyz/signatures` package to get pre-defined event signatures instead of using raw hex values. For example: `import { selectors } from '@boostxyz/signatures/events'`
### Example: Event-based Criteria
Here's an example setting up criteria to extract an amount from a Transfer event:
```ts theme={null}
import { selectors } from '@boostxyz/signatures/events'
import { SignatureType } from '@boostxyz/sdk/claiming'
const criteria = {
criteriaType: SignatureType.EVENT,
// Use pre-defined signature for Transfer(address,address,uint256)
signature: selectors['Transfer(address indexed,address indexed,uint256)'],
fieldIndex: 2, // Extract amount from the third parameter
targetContract: '0xTokenContract' // Contract to watch for events
}
```
### Example: Function-based Criteria
Here's an example setting up criteria to extract an amount from a mint function:
```ts theme={null}
import { selectors } from '@boostxyz/signatures/functions'
import { SignatureType } from '@boostxyz/sdk/claiming'
const criteria = {
criteriaType: SignatureType.FUNC,
// Use pre-defined signature for mint(uint256)
signature: selectors['mint(uint256)'],
fieldIndex: 0, // Extract amount from the first parameter
targetContract: '0xNFTContract' // Contract to watch for function calls
}
```
The extracted value will be used to calculate the reward amount, which is then capped by the `maxReward` parameter if necessary.
### Create a new ERC20PeggedVariableCriteriaIncentive
```ts index.ts theme={null}
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { config } from "./config";
import { parseUnits, zeroAddress } from 'viem'
import { SignatureType } from '@boostxyz/sdk/claiming'
const core = new BoostCore({ config });
await core.createBoost({
maxParticipants: 10n,
budget,
action,
allowList,
incentives: [
core.ERC20PeggedVariableCriteriaIncentiveV2({
asset: '0xRewardToken', // token that will be distributed
peg: '0xPeggedToken' || zeroAddress, // token to peg the reward value to (e.g., USDC or ETH)
maxReward: parseUnits("100", 6), // maximum reward per claim in pegged token value
limit: parseUnits("1000", 18), // maximum total amount of asset token (in wei) that can be distributed
manager: "0xManager", // address with administrative control over the incentive
criteria: {
criteriaType: SignatureType.EVENT, // or SignatureType.FUNC
signature: "0x...", // event or function signature to extract value from
fieldIndex: 2, // which parameter to extract the value from
targetContract: "0x..." // contract address to watch for the event/function
}
}),
],
});
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
The manager parameter specifies an address that has administrative control over the incentive. This address can perform management operations like triggering clawbacks of undistributed rewards if needed.
### Get an existing ERC20PeggedVariableCriteriaIncentive
```ts index.ts theme={null}
// if an incentive address is known, directly construct it
const incentive = core.ERC20PeggedVariableCriteriaIncentive("0xc55F719709bDad022B320E76f9DfF7e6F5680767")
// or if you want a budget from a specific chain
const incentiveOnBase = core.ERC20PeggedVariableCriteriaIncentive(
"0xc55F719709bDad022B320E76f9DfF7e6F5680767",
{ chainId: 8453 }
)
// or accessible off of a a pre-exiting Boost
const boost = await core.getBoost(0n)
const incentive = boost.incentives.find((incentive) => incentive instanceof ERC20PeggedVariableCriteriaIncentive)
```
# Rewards
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-pegged-variable-criteria/rewards
Retrieve information about ERC20 Incentive rewards
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Incentive is designed to store and distribute assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about the current state of its rewards.
## API
### `reward`
Retrieves the amount of the pegged asset configured at time of initialization. This represents the value of the reward denominated in the pegged token (e.g., 1 USDC). When claimed, the user receives an equivalent value in the actual reward asset token, calculated based on the current price ratio between the pegged token and the reward asset.
For example:
* If the reward is pegged to 1 USDC
* And the reward asset token is worth 0.25 USDC per token
* Then claiming the reward would transfer 4 reward asset tokens to the claimant (1 USDC ÷ 0.25 = 4 tokens)
```ts index.ts theme={null}
await erc20PeggedIncentive.reward()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the amount (in wei) of the pegged asset that each claim is valued at.
### `asset`
Retrieves the address of the ERC20-like token that will be transferred when rewards are claimed.
```ts index.ts theme={null}
await erc20PeggedIncentive.asset();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The address of the reward asset token that will be transferred when claims are processed.
### `peg`
Retrieves the address of the token that the reward value is pegged to.
```ts index.ts theme={null}
await erc20PeggedIncentive.peg();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The address of the token that the reward is pegged to.
### `getIncentiveCriteria`
Fetches the [IncentiveCriteria](https://sdk.boost.xyz/interfaces/IncentiveCriteria.html) struct from the contract. The criteria determines how a claim value will be extracted from a transaction.
```ts index.ts theme={null}
await erc20PeggedVariableCriteriaIncentive.getIncentiveCriteria()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
IncentiveCriteria}>
SignatureType}>
Either `SignatureType.EVENT` or `SignatureType.FUNC`, determines how the validator decodes transaction data.
The padded 4byte signature of the contract's event or function that will be examined when extracting the claim amount.
The index of the function argument or log parameter where the numerical amount will be extracted.
The address of the underlying asset that called the function or emitted the event matching the signature, must have occured within the same transaction submitted for validation.
### `maxReward`
Retrieves the maximum amount that can be claimed at a time, if the amount extracted from the claim transaction given the criteria is more than this value, then the claim value will be this max reward value.
```ts index.ts theme={null}
await erc20PeggedVariableCriteriaIncentive.maxReward()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the maximum reward per claim
### `limit`
Retrieves the spending limit for this incentive. The sum of all claims cannot exceed this amount. Denominated in the asset token.
```ts index.ts theme={null}
await erc20PeggedVariableCriteriaIncentive.limit()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total budget for this incentive, denominated in the asset token.
# Claims
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-pegged/claims
Retrieve ERC20PeggedIncentive claim information
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20PeggedIncentive is designed to store and distribute assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about claim state.
## API
### `claims`
Retrieves the total number of completed claims.
```ts index.ts theme={null}
await erc20PeggedIncentive.claims();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total number of claims.
### `claimed`
Checks if an address has previously claimed this incentive.
```ts index.ts theme={null}
await erc20PeggedIncentive.claimed(address);
```
#### Parameters
The address to check claimed status.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address has claimed the incentive.
### `isClaimable`
Check if an incentive is claimable by an address. Will return false if the recipient has already claimed, or total incentive claims has met the configured limit.
```ts index.ts theme={null}
await erc20PeggedIncentive.isClaimable(address);
```
#### Parameters
The address to check eligibility for.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address can claim incentive.
### `limit`
The total number of tokens (in wei) that can be claimed from this incentive.
```ts index.ts theme={null}
await erc20PeggedIncentive.limit();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the maximum number of tokens that can be claimed from this incentive.
### `getRemainingClaimPotential`
Retrieves the number of remaining possible claims for an incentive by comparing total claims against the limit.
Note that this does not check if a specific user is eligible to claim.
```ts index.ts theme={null}
await erc20PeggedIncentive.getRemainingClaimPotential();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the number of remaining possible claims (limit minus current claims)
### `canBeClaimed`
Checks if any claims remain for an incentive by comparing total claims against the limit.
Returns a boolean indicating if claims are still possible.
Note that this does not check if a specific user is eligible to claim.
```ts index.ts theme={null}
await erc20PeggedIncentive.canBeClaimed();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns `true` if there are remaining claims available, `false` otherwise
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-pegged/overview
Learn about how to interact with an ERC20PeggedIncentive using the SDK
The ERC20 Pegged Incentive is a specialized protocol incentive that allows for the distribution of ERC-20 assets with rewards pegged to the value of another token. This enables creating rewards that maintain a specific value relative to a pegged asset. When users claim rewards, they receive an amount of the reward token equivalent in value to the pegged amount.
The One-Time Actions docs are under active development and will be subject to changes.
Read the smart contracts
See technical documentation
See the source
## Key Features
* Rewards pegged to the value of another token (e.g., USDC) or native chain token (e.g., ETH)
* Dynamic reward amounts based on current price ratios
* Supports any ERC20 token as the reward asset
* Total distribution limit in reward asset tokens
* Managed access for incentive control and clawbacks
Unlike budgets, allowlists, and validators, incentives cannot be re-used between multiple Boosts.
To peg rewards to the chain's native token (e.g., ETH), use `zeroAddress` (0x0000000000000000000000000000000000000000) as the peg parameter. This tells the protocol to use the native token's price for calculations.
### Create a new ERC20PeggedIncentive
```ts index.ts theme={null}
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { config } from "./config";
import { parseUnits, zeroAddress } from 'viem'
const core = new BoostCore({ config });
await core.createBoost({
maxParticipants: 10n,
budget,
action,
allowList,
incentives: [
core.ERC20PeggedIncentive({
asset: '0xRewardToken', // token that will be distributed
peg: '0xPeggedToken' || zeroAddress, // token to peg the reward value to (e.g., USDC or ETH)
reward: parseUnits("1", 6), // amount in pegged token value (e.g., 1 USDC worth)
limit: parseUnits("1000", 18), // maximum total amount of asset token (in wei) that can be distributed as rewards
manager: "0xManager", // address with administrative control over the incentive
}),
],
});
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
The manager parameter specifies an address that has administrative control over the incentive. This address can perform management operations like triggering clawbacks of undistributed rewards if needed.
### Get an existing ERC20PeggedIncentive
```ts index.ts theme={null}
// if an incentive address is known, directly construct it
const incentive = core.ERC20PeggedIncentive("0xc55F719709bDad022B320E76f9DfF7e6F5680767")
// or if you want a budget from a specific chain
const incentiveOnBase = core.ERC20PeggedIncentive(
"0xc55F719709bDad022B320E76f9DfF7e6F5680767",
{ chainId: 8453 }
)
// or accessible off of a a pre-exiting Boost
const boost = await core.getBoost(0n)
const incentive = boost.incentives.find((incentive) => incentive instanceof ERC20PeggedIncentive)
```
# Rewards
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-pegged/rewards
Retrieve information about ERC20 Incentive rewards
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Incentive is designed to store and distribute assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about the current state of its rewards.
## API
### `reward`
Retrieves the amount of the pegged asset configured at time of initialization. This represents the value of the reward denominated in the pegged token (e.g., 1 USDC). When claimed, the user receives an equivalent value in the actual reward asset token, calculated based on the current price ratio between the pegged token and the reward asset.
For example:
* If the reward is pegged to 1 USDC
* And the reward asset token is worth 0.25 USDC per token
* Then claiming the reward would transfer 4 reward asset tokens to the claimant (1 USDC ÷ 0.25 = 4 tokens)
```ts index.ts theme={null}
await erc20PeggedIncentive.reward()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the amount (in wei) of the pegged asset that each claim is valued at.
### `asset`
Retrieves the address of the ERC20-like token that will be transferred when rewards are claimed.
```ts index.ts theme={null}
await erc20PeggedIncentive.asset();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The address of the reward asset token that will be transferred when claims are processed.
### `peg`
Retrieves the address of the token that the reward value is pegged to.
```ts index.ts theme={null}
await erc20PeggedIncentive.peg();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The address of the token that the reward is pegged to.
### `limit`
Retrieves the spending limit for this incentive. The sum of all claims cannot exceed this amount. Denominated in the asset token.
```ts index.ts theme={null}
await erc20PeggedIncentive.limit()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total budget for this incentive, denominated in the asset token.
# Claims
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-variable-criteria/claims
Retrieve ERC20 Incentive claim information and draw raffle
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Incentive is designed to store and distribute assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about claim state, as well as drawing raffles.
## API
### `claims`
Retrieves the total number of completed claims.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.claims();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total number of claims.
### `totalClaimed`
Returns the total amount distributed by this incentive.
```ts index.ts theme={null}
await erc20VariableIncentive.totalClaimed();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total amount claimed
### `claimed`
Checks if an address has previously claimed this incentive.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.claimed(address);
```
#### Parameters
The address to check claimed status.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address has claimed the incentive.
### `entries`
By index, retrieve the address in the list of all raffle entries.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.entries(0n);
```
#### Parameters
The raffle entry address to retrieve by index
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the entry's address
### `isClaimable`
Check if an incentive is claimable by an address. Will return false if the recipient has already claimed, or total incentive claims has met the configured limit.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.isClaimable(address);
```
#### Parameters
The address to check eligibility for.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address can claim incentive.
### `limit`
The total number of tokens (in wei) that can be claimed from this incentive.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.limit();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the maximum number of tokens that can be claimed from this incentive.
### `getRemainingClaimPotential`
Retrieves the remaining amount of tokens (in wei) remaining in the incentive by comparing total claims against the limit.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.getRemainingClaimPotential();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the remaining amount of tokens (in wei) remaining in the incentive
### `canBeClaimed`
Checks if the balance of the incentive is greater than 0.
Note that this does not check if a specific user is eligible to claim.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.canBeClaimed();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns `true` if the incentive has a remaining balance, `false` otherwise
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-variable-criteria/overview
Learn about how to interact with an ERC20 Variable Criteria Incentive using the SDK
The ERC20 Variable Criteria Incentive is a protocol incentive that allows for a variable distribution of native and ERC-20 assets, with each claim amount being determined dynamically at claim time depending on a value extracted from a provided transaction.
On Boost creation, a predetermined amount of the specified asset is transferred into this incentive from the associated budget.
The One-Time Actions docs are under active development and will be subject to changes.
Read the smart contracts
See technical documentation
See the source
## Key Features
* Supports native assets as well as any ERC20
* Rewards dynamic at claim time, depending on the value of a field extracted from a transaction log or function argument.
Unlike budgets, allowlists, and validators, incentives cannot be re-used between multiple Boosts.
### Create a new ERC20VariableCriteriaIncentive
```ts index.ts theme={null}
import { events as eventSelectors } from '@boostxyz/signatures/events'
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { selectors as eventSelectors } from "@boostxyz/signatures/events";
import { config } from "./config";
const core = new BoostCore({ config });
await core.createBoost({
maxParticipants: 10n,
budget,
action,
allowList,
incentives: [
core.ERC20VariableCriteriaIncentive({
asset: '0xERC20' || zeroAddress, // use zero address for native assets
reward: parseEther('0.5'), // Amount to multiply a claim amount by, if 0n or 1 ether, user will be transferred the whole amount asserted at claim time.
limit: parseEther('10'), // The total budget allocated to this incentive
maxReward: parseEther('5'), // The maximum amount that can be claimed at a time, if the amount extracted from the transaction given the criteria is more than this value, then the claim value will be this max reward value.
criteria: {
criteriaType: SignatureType.EVENT,
signature: eventSelectors[
"Transfer(address indexed,address indexed,uint256 indexed)"
] as Hex, // Function selector for mint
fieldIndex: 2, // Field index where the dynamic claim value resides
targetContract: '0xERC721',
}
}),
],
});
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
### Get an existing ERC20VariableCriteriaIncentive
```ts index.ts theme={null}
// if an incentive address is known, directly construct it
const incentive = core.ERC20VariableCriteriaIncentive("0xc55F719709bDad022B320E76f9DfF7e6F5680767")
// or if you want a budget from a specific chain
const incentiveOnBase = core.ERC20VariableCriteriaIncentive(
"0xc55F719709bDad022B320E76f9DfF7e6F5680767",
{ chainId: 8453 }
)
// or accessible off of a a pre-exiting Boost
const boost = await core.getBoost(0n)
const incentive = boost.incentives.find(incentive => incentive instanceof ERC20VariableCriteriaIncentive)
```
## Setting Up Incentive Criteria
The incentive criteria determines how the reward amount is extracted from transaction data. You can configure it to read values from either contract events or function calls.
### Criteria Configuration
The criteria object requires four fields:
* `criteriaType`: Either `SignatureType.EVENT` for event logs or `SignatureType.FUNC` for function calls
* `signature`: The event or function signature to match against
* `fieldIndex`: Which parameter in the event/function to extract the value from (0-based index)
* `targetContract`: The contract address to watch for the event/function
You can use the `@boostxyz/signatures` package to get pre-defined event signatures instead of using raw hex values. For example: `import { selectors } from '@boostxyz/signatures/events'`
### Example: Event-based Criteria
Here's an example setting up criteria to extract an amount from a Transfer event:
```ts theme={null}
import { selectors } from '@boostxyz/signatures/events'
import { SignatureType } from '@boostxyz/sdk'
const criteria = {
criteriaType: SignatureType.EVENT,
// Use pre-defined signature for Transfer(address,address,uint256)
signature: selectors['Transfer(address indexed,address indexed,uint256)'],
fieldIndex: 2, // Extract amount from the third parameter
targetContract: '0xTokenContract' // Contract to watch for events
}
```
### Example: Function-based Criteria
Here's an example setting up criteria to extract an amount from a mint function:
```ts theme={null}
import { selectors } from '@boostxyz/signatures/functions'
import { SignatureType } from '@boostxyz/sdk/claiming'
const criteria = {
criteriaType: SignatureType.FUNC,
// Use pre-defined signature for mint(uint256)
signature: selectors['mint(uint256)'],
fieldIndex: 0, // Extract amount from the first parameter (quantity)
targetContract: '0xNFTContract' // Contract to watch for function calls
}
```
The extracted value will be used to calculate the reward amount, which is then capped by the `maxReward` parameter if necessary.
### Sign a claim amount
```ts theme={null}
// Get the index of the incentive to claim
const incentiveIndex = boost.incentives.findIndex(incentive => incentive instanceof ERC20VariableCriteriaIncentive)
// build claim data, signing a variable amount for the incentive
const allClaimData = boost.incentives.map(incentive => {
if (incentive instanceof ERC20VariableCriteriaIncentive || incentive instanceof ERC20VariableCriteriaIncentive) {
const rewardAmount = parseEther("1");
return incentive.buildClaimData(rewardAmount);
} else {
return ''
}
})
// Sign a valid transaction with the variable reward amount
const { data } = await axios.get('/signatures', {
params: {
boostId: `${chainId}:${boostCoreAddress}:${boost.id}`,
txHash,
claimData: allClaimData.join(',')
}
});
// claim the ERC20VariableCriteriaIncentive
const { signature, incentiveId, claimant } = data.at(incentiveIndex);
await core.claimIncentive(
boost.id,
incentiveId,
claimant,
signature,
);
```
# Rewards
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-variable-criteria/rewards
Retrieve information about ERC20 Incentive rewards
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Variable Incentive is designed to store and distribute dynamic amounts of assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about the current state of its rewards.
## API
### `asset`
Retrieves the address of the ERC20-like asset to be rewarded on claim.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.asset();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The address of the asset transferred on claim
### `getIncentiveCriteria`
Fetches the [IncentiveCriteria](https://sdk.boost.xyz/interfaces/IncentiveCriteria.html) struct from the contract. The criteria determines how a claim value will be extracted from a transaction.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.getIncentiveCriteria()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
IncentiveCriteria}>
SignatureType}>
Either `SignatureType.EVENT` or `SignatureType.FUNC`, determines how the validator decodes transaction data.
The padded 4byte signature of the contract's event or function that will be examined when extracting the claim amount.
The index of the function argument or log parameter where the numerical amount will be extracted.
The address of the underlying asset that called the function or emitted the event matching the signature, must have occured within the same transaction submitted for validation.
### `maxReward`
Retrieves the maximum amount that can be claimed at a time, if the amount extracted from the claim transaction given the criteria is more than this value, then the claim value will be this max reward value.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.maxReward()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the maximum reward per claim
### `reward`
Retrieves the ETH decimal format reward multiplier. If set to `0n` or `1 ether`, user will be transferred the whole amount asserted at claim time.
```ts index.ts theme={null}
await erc20VariableIncentive.reward()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the reward multiplier.
### `limit`
Retrieves the spending limit for this incentive. The sum of all claims cannot exceed this amount.
```ts index.ts theme={null}
await erc20VariableCriteriaIncentive.limit()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total budget for this incentive
# Claims
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-variable/claims
Retrieve ERC20 Incentive claim information and draw raffle
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Incentive is designed to store and distribute assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about claim state, as well as drawing raffles.
## API
### `claims`
Retrieves the total number of completed claims.
```ts index.ts theme={null}
await erc20VariableIncentive.claims();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total number of claims.
### `totalClaimed`
Returns the total amount distributed by this incentive.
```ts index.ts theme={null}
await erc20VariableIncentive.totalClaimed();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total amount claimed
### `claimed`
Checks if an address has previously claimed this incentive.
```ts index.ts theme={null}
await erc20VariableIncentive.claimed(address);
```
#### Parameters
The address to check claimed status.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address has claimed the incentive.
### `isClaimable`
Check if an incentive is claimable by an address. Will return false if the recipient has already claimed, or total incentive claims has met the configured limit.
```ts index.ts theme={null}
await erc20VariableIncentive.isClaimable(address);
```
#### Parameters
The address to check eligibility for.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address can claim incentive.
### `getRemainingClaimPotential`
Retrieves the remaining amount of tokens (in wei) remaining in the incentive by comparing total claims against the limit.
```ts index.ts theme={null}
await erc20VariableIncentive.getRemainingClaimPotential();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the remaining amount of tokens (in wei) remaining in the incentive
### `canBeClaimed`
Checks if the balance of the incentive is greater than 0.
Note that this does not check if a specific user is eligible to claim.
```ts index.ts theme={null}
await erc20VariableIncentive.canBeClaimed();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns `true` if the incentive has a remaining balance, `false` otherwise
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-variable/overview
Learn about how to interact with an ERC20Variable Incentive using the SDK
The ERC20 Variable Incentive is a protocol incentive that allows for a variable distribution of native and ERC-20 assets, with each claim amount being determined dynamically at claim time. On Boost creation, a predetermined amount of the specified asset is transferred into this incentive from the associated budget.
The One-Time Actions docs are under active development and will be subject to changes.
Read the smart contracts
See technical documentation
See the source
## Key Features
* Supports native assets as well as any ERC20
* Rewards dynamic at claim time
Unlike budgets, allowlists, and validators, incentives cannot be re-used between multiple Boosts.
### Create a new ERC20VariableIncentive
```ts index.ts theme={null}
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { config } from "./config";
const core = new BoostCore({ config });
await core.createBoost({
maxParticipants: 10n,
budget,
action,
allowList,
incentives: [
core.ERC20VariableIncentive({
asset: '0xERC20' || zeroAddress, // use zero address for native assets
reward: parseEther('0.5'), // Amount to multiply a claim amount by, if 0n or 1 ether, user will be transferred the whole amount asserted at claim time.
limit: parseEther('1'), // The total budget allocated to this incentive
}),
],
});
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
### Get an existing ERC20VariableIncentive
```ts index.ts theme={null}
// if an incentive address is known, directly construct it
const incentive = core.ERC20VariableIncentive("0xc55F719709bDad022B320E76f9DfF7e6F5680767")
// or if you want a budget from a specific chain
const incentiveOnBase = core.ERC20VariableIncentive(
"0xc55F719709bDad022B320E76f9DfF7e6F5680767",
{ chainId: 8453 }
)
// or accessible off of a a pre-exiting Boost
const boost = await core.getBoost(0n)
const incentive = boost.incentives.find(incentive => incentive instanceof ERC20VariableIncentive)
```
### Sign a claim amount
```ts theme={null}
// Get the index of the incentive to claim
const incentiveIndex = boost.incentives.findIndex(incentive => incentive instanceof ERC20VariableCriteriaIncentive)
// build claim data, signing a variable amount for the incentive
const allClaimData = boost.incentives.map(incentive => {
if (incentive instanceof ERC20VariableIncentive || incentive instanceof ERC20VariableCriteriaIncentive) {
const rewardAmount = parseUnits("10", 18);
return incentive.buildClaimData(rewardAmount);
} else {
return ''
}
})
// Sign a valid transaction with the variable reward amount
const { data } = await axios.get('/signatures', {
params: {
boostId: `${chainId}:${boostCoreAddress}:${boost.id}`,
txHash,
claimData: allClaimData.join(',')
}
});
// claim the ERC20VariableIncentive
const { signature, incentiveId, claimant } = data.at(incentiveIndex);
await core.claimIncentive(
boost.id,
incentiveId,
claimant,
signature,
);
```
# Rewards
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20-variable/rewards
Retrieve information about ERC20 Incentive rewards
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Variable Incentive is designed to store and distribute dynamic amounts of assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about the current state of its rewards.
## API
### `reward`
Retrieves the ETH decimal format reward multiplier. If set to `0n` or `1 ether`, user will be transferred the whole amount asserted at claim time.
```ts index.ts theme={null}
await erc20VariableIncentive.reward()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the reward multiplier.
### `asset`
Retrieves the address of the ERC20-like asset to be rewarded on claim.
```ts index.ts theme={null}
await erc20VariableIncentive.asset();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The address of the asset transferred on claim
### `limit`
Retrieves the spending limit for this incentive. The sum of all claims cannot exceed this amount.
```ts index.ts theme={null}
await erc20VariableIncentive.limit()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total budget for this incentive
# Claims
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20/claims
Retrieve ERC20 Incentive claim information and draw raffle
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Incentive is designed to store and distribute assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about claim state, as well as drawing raffles.
## API
### `claims`
Retrieves the total number of completed claims.
```ts index.ts theme={null}
await erc20Incentive.claims();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the total number of claims.
### `claimed`
Checks if an address has previously claimed this incentive.
```ts index.ts theme={null}
await erc20Incentive.claimed(address);
```
#### Parameters
The address to check claimed status.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address has claimed the incentive.
### `drawRaffle`
When using an ERC20 Incentive initialized with a `RAFFLE` strategy. Pick a random entry from a list of all claims and transfer the reward to them. Only callable by incentive. managers.
```ts index.ts theme={null}
await erc20Incentive.drawRaffle();
```
#### Parameters
Optional parameters to pass to the underlying `writeContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/writeContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Promise that successfully resolves if a winner has been paid out
### `entries`
By index, retrieve the address in the list of all raffle entries.
```ts index.ts theme={null}
await erc20Incentive.entries(0n);
```
#### Parameters
The raffle entry address to retrieve by index
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the entry's address
### `isClaimable`
Check if an incentive is claimable by an address. Will return false if the recipient has already claimed, or total incentive claims has met the configured limit.
```ts index.ts theme={null}
await erc20Incentive.isClaimable(address);
```
#### Parameters
The address to check eligibility for.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns true if address can claim incentive.
### `getRemainingClaimPotential`
Retrieves the number of remaining possible claims for an incentive by comparing total claims against the limit.
Note that this does not check if a specific user is eligible to claim.
```ts index.ts theme={null}
await erc20Incentive.getRemainingClaimPotential();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the number of remaining possible claims (limit minus current claims)
### `canBeClaimed`
Checks if any claims remain for an incentive by comparing total claims against the limit.
Returns a boolean indicating if claims are still possible.
Note that this does not check if a specific user is eligible to claim.
```ts index.ts theme={null}
await erc20Incentive.canBeClaimed();
```
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns `true` if there are remaining claims available, `false` otherwise
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20/overview
Learn about how to interact with an ERC20Incentive using the SDK
The ERC20 Incentive is an essential protocol incentive that allows for the distribution of native and ERC-20 assets, supporting raffle functionality as well as basic fixed rewards. On Boost creation, a predetermined amount of the specified asset is transferred into this incentive from the associated budget.
The One-Time Actions docs are under active development and will be subject to changes.
Read the smart contracts
See technical documentation
See the source
## Key Features
* Supports native assets as well as any ERC20
* Rewards fixed at time of creation
* Raffle functionality, with full incentive balance going to random claimant
Unlike budgets, allowlists, and validators, incentives cannot be re-used between multiple Boosts.
### Create a new ERC20Incentive
```ts index.ts theme={null}
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { StrategyType } from '@boostxyz/sdk/claiming'
import { config } from "./config";
const core = new BoostCore({ config });
await core.createBoost({
maxParticipants: 10n,
budget,
action,
allowList,
incentives: [
core.ERC20Incentive({
asset: '0xERC20' || zeroAddress, // use zero address for native assets
reward: parseEther("1"), // how much to distribute on each claim, with the RAFFLE stratgy, the total reward is `parseEther("1") * limit`
limit: 10n, // how many times can this incentive be claimed
strategy: StrategyType.POOL || StrategyType.RAFFLE,
manager: budget.assertValidAddress(),
}),
],
});
```
```ts config.ts theme={null}
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
### Get an existing ERC20Incentive
```ts index.ts theme={null}
// if an incentive address is known, directly construct it
const incentive = core.ERC20Incentive("0xc55F719709bDad022B320E76f9DfF7e6F5680767")
// or if you want a budget from a specific chain
const incentiveOnBase = core.ERC20Incentive(
"0xc55F719709bDad022B320E76f9DfF7e6F5680767",
{ chainId: 8453 }
)
// or accessible off of a a pre-exiting Boost
const boost = await core.getBoost(0n)
const incentive = boost.incentives.find(incentive => incentive instanceof ERC20Incentive)
```
# Rewards
Source: https://docs.boost.xyz/v2/boost-sdk/incentives/erc20/rewards
Retrieve information about ERC20 Incentive rewards
The One-Time Actions docs are under active development and will be subject to changes.
The ERC20 Incentive is designed to store and distribute assets that can be claimed by users. The Boost SDK provides several methods to retrieve information about the current state of its rewards.
## API
### `reward`
Retrieves the amount of the reward configured at time of initialization. When claimed, this is the amount that is transferred to the claimant.
```ts index.ts theme={null}
await erc20Incentive.reward()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns the amount of the asset rewarded per-claim.
### `asset`
Retrieves the address of the ERC20-like asset to be rewarded on claim.
```ts index.ts theme={null}
await erc20Incentive.asset();
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
The address of the asset transferred on claim
### `strategy`
Retrieve the strategy configured at time of intitialization, either. [Either POOL, or RAFFLE](https://sdk.boost.xyz/enums/StrategyType.html). A typical Boost setup will be using [StrategyType.POOL](https://sdk.boost.xyz/enums/StrategyType.html#POOL)
```ts index.ts theme={null}
await erc20Incentive.strategy();
```
#### Parameters The total amount of the asset distributed from the budget.
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
StrategyType}>
The total amount of the asset distributed from the budget.
### `limit`
Retrieves the amount of times this incentive can be claimed.
```ts index.ts theme={null}
await erc20Incentive.limit()
```
#### Parameters
Optional parameters to pass to the underlying `readContract` method. Checkout [wagmi's documentation](https://wagmi.sh/core/api/actions/readContract#parameters) for more information. `address`, `args`, `functionName`, `abi` are handled for you under the hood.
#### Returns
Returns total number of claims that can be made against this incentive.
# Installation
Source: https://docs.boost.xyz/v2/boost-sdk/installation
The One-Time Actions docs are under active development and will be subject to changes.
You can install `@boostxyz/sdk` via [NPM](https://www.npmjs.com/package/@boostxyz/sdk)
### NPM
```bash npm theme={null}
npm i @boostxyz/sdk
```
```bash pnpm theme={null}
pnpm add @boostxyz/sdk
```
```bash yarn theme={null}
yarn add @boostxyz/sdk
```
```bash bun theme={null}
bun add @boostxyz/sdk
```
### Peer Dependencies
Boost SDK is designed to work with [Wagmi Configurations](https://wagmi.sh/core/api/createConfig#config) and requires the following peer dependencies
```
"@wagmi/core": "2",
"viem": "2",
"abitype": "1"
```
which, if they are not already in your project, you can include with the following command
```bash npm theme={null}
npm i @wagmi/core @wagmi/connectors viem@2.x
```
```bash pnpm theme={null}
pnpm add @wagmi/core @wagmi/connectors viem@2.x
```
```bash yarn theme={null}
yarn add @wagmi/core @wagmi/connectors viem@2.x
```
```bash bun theme={null}
bun add @wagmi/core @wagmi/connectors viem@2.x
```
### Runtime Requirements
Boost SDK is optimized for modern browsers. It is compatible with the following browser configurations:
```
Chrome >= 87
Firefox >= 78
Edge >= 88
Safari >= 14
```
> If you want to support older browsers, you need to transpile the library from `node_modules` yourselves.
# Integrating your Protocol or DApp
Source: https://docs.boost.xyz/v2/boost-sdk/integrating-your-contracts
How to add your contracts' function and event signatures into Boost's well-known registry
The One-Time Actions docs are under active development and will be subject to changes.
The Boost Protocol, fundamentally, needs to be able to validate that certain actions have occured in order to allow users to claim incentives. So in order to make it simple to create Boosts and validate them, we publish [@boostxyz/signatures](https://www.npmjs.com/package/@boostxyz/signatures) which is a registry of known function and event signatures, their 32 byte selectors, and [AbiEvents](https://viem.sh/docs/glossary/types#abievent) or [AbiFunctions](https://viem.sh/docs/glossary/types#abifunction) in an easy to consume JSON format.
You can use this library as you're creating a Boost and building out [action steps](https://sdk.boost.xyz/interfaces/ActionStep.html) for use with the [Event Actions](https://sdk.boost.xyz/classes/EventAction.html). You'll need to specify the function or event's [signature](https://sdk.boost.xyz/interfaces/ActionStep.html#signature), which allows Boost's validators to parse the event or function arguments, and compare them against the rest of the actions' parameters.
So in order to simplify applications that would otherwise need to know the hex encoded 4 byte function selector, or 32 byte event selector, and their associated ABI items, you can instead use this library to save on code and complexity.
```ts theme={null}
// you can import both events and functions registries
import { events, functions } from '@boostxyz/signatures'
// ...or individually
import events from '@boostxyz/signatures/events'
import functions from '@boostxyz/signatures/functions'
// both events and functions manifests have the following interface:
type eventsOrFunctionsRegistry = { abi: Record<(Hex | string), AbiItem>, selectors: Record }
// where string keys are the signature ie: Transfer(address indexed,address indexed,uint256 indexed)
// the event signature we'll be using to compose the action
const knownSignature = "Transfer(address indexed,address indexed,uint256 indexed)"
const selector = events.selectors[knownSignature] // 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
const abiItem = events.abi[knownSignature] // {name:"Transfer",type:"event",inputs:[{type:"address",indexed:true},{type:"address",indexed:true},{type:"uint256",indexed:true}]}
// now you have everything you need to construct your event action
const filterOnReceiver: ActionStep = {
chainid: 8453,
signature: selector,
signatureType: SignatureType.EVENT, // We're working with an event
targetContract: targetContract, // Address of the contract emitting the `Transfer` event
// We want to target the 'receiver' property on the Transfer event
actionParameter: {
filterType: FilterType.EQUAL, // Filter to check for equality
fieldType: PrimitiveType.ADDRESS, // The field we're filtering is an address
fieldIndex: 1, // We want to target the second argument (index 1) of the event signature
filterData: '0xc0ffee', // Filtering based on the recipient's address
},
};
```
If you've used a function or event signature from `@boostxyz/signatures` in the creation of your Boost, then you don't have to do anything else for validation to work.
Otherwise, if you're supplying a custom event signature unknown to `@boostxyz/signatures` and dealing with a Boost using a custom validator, you'll need to additionally supply your own `AbiEvent` to validation methods so event logs can be correctly pulled off transactions.
```ts theme={null}
await boostCore.validateActionSteps({
...,
knownEvents: {
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef': {
name: "Transfer",
type: "event",
inputs: [
{ type: "address", indexed: true },
{ type: "address", indexed: true },
{ type: "uint256", indexed: true }
]
}
}
})
```
For more detailed examples, see [sample use cases](https://github.com/boostxyz/boost-protocol/tree/main/examples)
## Contributing new signatures
If using the default Boost validator, you also have the option of submitting your contract events and functions to [openchain.xyz](https://openchain.xyz/signatures/import) or [4byte.directory](https://www.4byte.directory/submit/), but contributing to `@boostxyz/signatures` is preferred due to its approach as a static manifest as opposed to a third party API.
To integrate your contracts with the Boost protocol:
* First, check to see if `@boostxyz/signatures` [already supports your signature](https://github.com/boostxyz/boost-protocol/tree/main/packages/signatures/manifests). We include several popular signatures and you may not need to do anything at all.
* [Fork the `boostxyz/boost-protocol` repository](https://github.com/boostxyz/boost-protocol/fork)
* Add your function or event signature to the [correct manifest JSON file](https://github.com/boostxyz/boost-protocol/tree/main/packages/signatures/manifests) following the existing formatting and conventions.
* Entries starting with `//` are treated as comments so you can distinguish a block of related signatures
* Commit your modified manifest file
* [Create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork)
The Boost Protocol team will review the submission and promptly release a new version of the `@boostxyz/signatures` package so you can continue your Boost Protocol integrations.
# Overview
Source: https://docs.boost.xyz/v2/boost-sdk/overview
Interacting directly with Boosts in Node and the Browser
The One-Time Actions docs are under active development and will be subject to changes.
The *Boost SDK* is a modern Typescript library for the browser and Node.js that simplifies interacting with the [Boost protocol](https://github.com/boostxyz/boost-protocol/blob/main), making creating and interacting with One-Time Actions campaigns straightforward.
## Features
*Boost SDK* includes everything you need to create and manage Boosts. Inside, you'll find:
* Browser first support with [wagmi](https://wagmi.sh/)
* Node.js support with [Viem](https://viem.sh/) accounts
* Modern, supporting both CommonJS and ESM modules
* Easily understood, taking an object oriented design approach
* Well-typed
* Extensible, allowing custom configurations for nearly every common operation
## Design Principles
A few underlying drivers motivate us as engineers as we build this library.
* Developer focused: You should be able to do anything you need to do wherever you need to do it.
* Simple: Selectively abstract away any emergent complexity that you would otherwise face interacting with the protocol directly.
* Interoperable: You should be able to bring the tools you're already using with you.
# Protocol Fees
Source: https://docs.boost.xyz/v2/boost-sdk/protocol-fees
How fees work and how to calculate them
The Boost Protocol collects protocol fees from budgets on 3 distinct occasions:
* At time of Boost creation, transferring assets to incentives from budgets
* On claim, recouping assets from the incentive's backing asset
* On retrieving assets from an incentive back to a budget
At time of writing, the base protocol fee in basis points is `10%` and, for any qualifying event, the math can be summarized with the following equation:
```ts theme={null}
const protocolFee = 1000n // 10% in basis points
const protocolFeeDenominator = 10000n // 100% in basis points
const fee = (amountTransferred * protocolFee) / protocolFeeDenominator
```
## Calculating fees for Boost creation
For ERC20 incentives, you'll want to ensure your budget has enough assets to cover the full amount that can be claimed by users, as well as the protocol fee. The following is a simple code sample that can help ensure your new Boost does not revert with an `Insufficient Funds` error.
```ts theme={null}
// Initialize your incentive configuration
const erc20Incentive = core.ERC20Incentive({
asset: '0xERC20',
strategy: StrategyType.POOL,
reward: 100n,
limit: 10n,
manager: budget.assertValidAddress(),
});
// Acquire the total amount that can be distributed from the incentive
const totalBudget = await erc20Incentive.getTotalBudget()
// Calculate the protocol fee
const fee = await core.calculateProtocolFee(totalBudget)
// Calculate the total amount required to fund the incentive
const totalFundingAmount = totalBudget + fee
// Approve your erc20 to send the full funding amount to the budget
await writeContract(wagmiConfig, {
abi: erc20Abi,
address: '0xERC20', // The address of the ERC20 token you want to approve
functionName: 'approve',
args: [
budget.assertValidAddress(), // The address of the budget account
totalFundingAmount
],
})
// Allocate assets to the budget from your account
await budget.allocate({
amount: totalFundingAmount,
asset: '0xERC20',
target: "0xME",
})
// On create, `totalBudget` will be transferred to incentive, and `fee` will be transferred to the protocol
const boost = await core.createBoost({
...otherParams,
incentives: [erc20Incentive],
});
```
# Quick Start
Source: https://docs.boost.xyz/v2/boost-sdk/quick-start
The One-Time Actions docs are under active development and will be subject to changes.
This code snippet briefly illustrates a few core concepts of `@boostxyz/sdk`
```ts theme={null}
import { BoostCore, EventAction, SimpleAllowList } from '@boostxyz/sdk'
// or using subpath exports, if you prefer
import { BoostCore } from '@boostxyz/sdk/BoostCore'
import { EventAction } from '@boostxyz/sdk/Actions/EventAction'
import { SimpleAllowList } from '@boostxyz/sdk/AllowLists/SimpleAllowList'
import { SimpleDenyList } from '@boostxyz/sdk/AllowLists/SimpleDenyList'
// Peer dependencies
import { createConfig, http } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
// You'll likely want the following if you're in a Node environment
import { createWalletClient, http, publicActions } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'
// Create your Wagmi config. If using `React` or `Vue`, you can also use the [Wagmi `useConfig` API](https://wagmi.sh/react/api/hooks/useConfig)
let config = createConfig({
chains: [sepolia],
transports: {
[sepolia.id]: http('https://sepolia.example.com'),
},
})
// If you are in a Node environment and signing transactions, you'll additionally want to configure a [Viem Local Account](https://viem.sh/docs/accounts/local) and a [Viem client](https://viem.sh/docs/clients/intro)
const account = privateKeyToAccount('PRIVATE_KEY')
config = createConfig({
ssr: true,
chains: [sepolia],
transports: {
[sepolia.id]: http('https://sepolia.example.com'),
},
client: ({ chain }) => {
return createWalletClient({
account,
chain,
transport: http()
}).extend(publicActions)
},
})
// Instantiate a new Boost Core, supplying your config and local account, if needed.
const core = new BoostCore({ config, ...(!!account && { account }) })
// Get a single Boost, this will automatically instantiate the Boost's core components.
const boost = await core.getBoost(0n)
// or, if you'd like to avoid extra read calls or just want the on chain representation,
await core.readBoost(0n)
// a Boost is comprised of several components
const validator: SignerValidator = boost.validator
const budget: ManagedBudget = boost.budget
const allowList: SimpleAllowList | SimpleDenyList = boost.allowList
const action: EventAction = boost.action
const incentives: Array = boost.incentives
// Some Boost components can be of a few different types...
if(boost.allowList instanceof SimpleAllowList) {
await boost.setAllowed(
['0xdeadb33f'], [true]
)
}
// so you can infer it for dynamic functionality
if(boost.allowList instanceof SimpleDenyList) {
await boost.setDenied(
['0xdeadb33f'], [false]
// For every public method that reads or writes, the last parameter is an optional object where you can supply additional values for the operation if desired.
// For more information, see:
// https://wagmi.sh/core/api/actions/readContract
// https://wagmi.sh/core/api/actions/writeContract
{
chainId: 31137,
blockNumber: 1337n,
blockTag: 'latest',
dataSuffix: '0x',
account: privateKeyToAccount('DIFFERENT_KEY'),
gas: 0n,
nonce: 1,
value: parseEther('0.01'),
gasPrice: parseGwei('20'),
maxFeePerGas: parseGwei('20'),
maxPriorityFeePerGas: parseGwei('2'),
}
)
}
// if you need, you can access the ABI for any Boost component either of two ways
const abi = boost.allowList.abi
import { simpleAllowListAbi } from '@boostxyz/sdk/AllowLists/SimpleAllowList'
```
## Targeting Specific Chains
The Boost SDK allows you to interact with the `BoostCore` and `BoostRegistry` contracts across different chains. You can specify which chain you want to target by including a `chainId` in the params object of your method calls.
```ts theme={null}
// Target a boost with id of 3 on Base mainnet (chainId: 8453)
const boost = await core.getBoost(3n, { chainId: 8453 })
```
Always ensure that the protocol is deployed on the chain you're targeting. Specifying a `chainId` for a network where the protocol isn't deployed could result in degraded UX if, in the browser, the SDK attempts to switch chains to its default network and there's no supporting chain configuration in your `Wagmi/Viem` client.
By specifying the `chainId` in the params, you ensure that the SDK targets the correct contracts for the specific network you are targetting.
The SDK uses the following process to determine which chain to interact with:
1. It first attempts to use the contract address associated with the specified `chainId` passed in the params.
2. If no address is found for the given chainId, it falls back to using the address for the connected account's chain.
3. If that also fails, it uses the address for the default chain set in the SDK config.
# Usage With React
Source: https://docs.boost.xyz/v2/boost-sdk/usage-with-react
Learn how to use with React or Next.js
The One-Time Actions docs are under active development and will be subject to changes.
While the Boost SDK does not currently offer a React adapter, it's simple to integrate the One-Time Actions protocol into your application.
As a prerequisite, you should have a basic [Wagmi](https://wagmi.sh/) integration set up.
Learn how to integrate `Wagmi` into your React application.
Afterwards, you can use the following basic snippets as a reference.
```tsx app.tsx theme={null}
'use client';
import { type ReactNode, useEffect, useState } from 'react';
import { RainbowKitProvider } from '@rainbow-me/rainbowkit';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { WagmiProvider } from 'wagmi';
import { BoostProvider } from '@/components/BoostContext';
import { wagmiConfig } from '@/wagmi';
export function Providers({ children }: Readonly<{ children: ReactNode }>) {
const queryClient = new QueryClient();
const appInfo = {
appName: 'Boost SDK Examples',
};
return (
{children}
);
}
```
```tsx BoostContext.tsx theme={null}
'use client';
import { wagmiConfig } from '@/wagmi';
import {
BoostCore,
type BoostCoreConfig,
BoostRegistry,
type BoostRegistryConfig,
} from '@boostxyz/sdk';
import { createContext, useContext, useMemo } from 'react';
import {
WagmiProviderNotFoundError,
getAccount,
useAccount,
useConfig,
} from 'wagmi';
export interface IBoostContext {
registry: BoostRegistry;
core: BoostCore;
}
export const BoostContext = createContext({
core: new BoostCore({ config: wagmiConfig }),
registry: new BoostRegistry({ config: wagmiConfig }),
});
export type BoostProviderProps = {
core?: Omit;
registry?: Omit;
};
export function useBoost() {
return useContext(BoostContext);
}
export function BoostProvider({
children,
core,
registry,
}: React.PropsWithChildren) {
const config = useConfig();
const chainId = useChainId();
if (!config) throw new Error('No Wagmi config provided');
// we'll want to re-instantiate whenever the chain changes to ensure we're always targeting the correct protocol addresses
const value = useMemo(() => {
return {
core: new BoostCore({ ...core, config }),
registry: new BoostRegistry({ ...registry, config }),
};
}, [core, registry, chainId]);
return (
{children}
);
}
```
```ts useBoostCoreInfo.ts theme={null}
import { useBoost } from '@/components/BoostContext';
import { useQuery } from '@tanstack/react-query';
import { zeroAddress } from 'viem';
export function useBoostCoreInfo() {
const { core } = useBoost();
return useQuery({
queryKey: ['useBoostCoreInfo', core],
initialData: {
protocolFee: 0n,
protocolFeeReceiver: zeroAddress,
},
queryFn: async () => {
const [protocolFee, protocolFeeReceiver] = await Promise.all([
core.protocolFee(),
core.protocolFeeReceiver(),
]);
return {
protocolFee,
protocolFeeReceiver,
};
},
});
}
```
```ts useBoostCount.ts theme={null}
import { useBoost } from '@/components/BoostContext';
import { useQuery } from '@tanstack/react-query';
export function useBoostCount() {
const { core } = useBoost();
const res = useQuery({
queryKey: ['getBoostCount', core],
initialData: 0n,
queryFn: async () => {
return await core.getBoostCount();
},
});
return res;
}
```
```ts useBoostById.ts theme={null}
import { useBoost } from '@/components/BoostContext';
import { useQuery } from '@tanstack/react-query';
export function useBoostById({ boostId }: { boostId: number }) {
const { core } = useBoost();
const res = useQuery({
queryKey: ['getBoostById', core, boostId],
queryFn: async () => {
return await core.getBoost(boostId);
}
});
return res;
}
```
# Working with Events
Source: https://docs.boost.xyz/v2/boost-sdk/working-with-events
Learn how to get logs and listen for new events emitted by the protocol
The One-Time Actions docs are under active development and will be subject to changes.
See technical documentation for `Contract.getLogs`
See technical documentation for `Contract.subscribe`
Any accessible `Contract` exposed by the SDK exposes 2 public methods to help retrieve typed event information.
```ts getLogsExample.ts theme={null}
import { BoostCore, BoostCoreLog } from '@boostxyz/sdk'
// instantiate a protocol contract
const core = new BoostCore({ config })
// you can import typed log types for your own utility
const anyLogs: BoostCoreLog[]
const boostCreatedLogs: BoostCoreLog<'BoostCreated'>[]
boostCreatedLogs = await core.getLogs({
// If you provide a specific event name, the returned log type will have typesafe values
eventName: 'BoostCreated'
// you can also pass an array of event names
eventNames: ['BoostCreated'],
// you can pass any parameters you would give to https://viem.sh/docs/actions/public/getLogs#getlogs
fromBlock: 16330000n,
toBlock: 16330050n
})
```
```ts subscribeExample.ts theme={null}
import { BoostCore, BoostCoreLog } from '@boostxyz/sdk'
// instantiate a protocol contract
const core = new BoostCore({ config })
// you can import typed log types for your own utility
const anyLogs: BoostCoreLog[]
const boostCreatedLogs: BoostCoreLog<'BoostCreated'>[]
// if you provide an eventName to listen to, you don't need to explicitly type the callback, this is just a reference
const unsubscribe = core.subscribe(
(log: BoostCoreLog<'BoostCreated'>) => {
boostCreatedLogs.push(log)
},
{
eventName: 'BoostCreated',
// you can pass any parameters you would give to https://wagmi.sh/core/api/actions/watchContractEvent#watchcontractevent
batch: true,
chainId: 31337,
onError: (e: Error) => {},
poll: false,
pollingInterval: 1000,
strict: true,
syncConnectedChain: false,
}
)
// call unsubscribe to clean up your listener
unsubscribe()
```
# Understanding the Action Section
Source: https://docs.boost.xyz/v2/documentation/action-creation/action-section
Learn how to interpret the main transaction details on Etherscan
The Action section on Etherscan provides key information about the transaction. Let's break down the main components:
## From
* **Where to find it**: Look for "From:" near the top of the Etherscan page
* **What it is**: The address that sent the transaction
* **Example**: From: 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
* **How to use it**: Enter this address in the "From" field of the Boost Deploy flow if you want to target transactions from a specific address or type of address (like a specific DEX router)
## To
* **Where to find it**: Look for "To:" just below "From:"
* **What it is**: The address of the contract being interacted with
* **Example**: To: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB480xfc1f3296458f9b2a27a0b91dd7681c4020e09d05 (USDCWETH:OP UniswapV3Pool token contract)
* **How to use it**: Enter this address in the "To" field to target interactions with a specific contract (like swaps involving USDCWETH/OP)
## Value
* **Where to find it**: Look for "Value:" in the transaction details
* **What it is**: The amount of ETH sent with the transaction
* **Example**: Value: 0.1 Ether (\$183.58)
* **How to use it**: You can use this to set a value filter in the Boost Deploy flow, e.g., to target only transactions above 0.1 ETH
## Signature
1. Scroll down to the "Input Data" section on Etherscan
2. If the contract is verified, you'll see a decoded function name
3. If not verified, you'll see a long string of numbers and letters
* **Example**: Function: transfer(address recipient, uint256 amount)
* **How to use it**: Select this function (e.g., "transfer") in the "Signature" dropdown in the Boost Deploy flow
Understanding these components will help you accurately define the action you want to boost.
# Advanced Techniques and Best Practices
Source: https://docs.boost.xyz/v2/documentation/action-creation/advanced-techniques
Learn advanced strategies for creating effective Boost actions
As you become more comfortable with creating Boost actions, consider these advanced techniques and best practices.
## Advanced Strategies
1. **Combining Filters**: Use both function parameters and log events for very specific targeting
2. **Time-Based Boosts**: Create time-limited promotions using transaction timestamps
3. **Multi-Step Actions**: Reward users for completing a series of actions
4. **Dynamic Adjustments**: Regularly review and adjust your filters based on performance
## Best Practices
1. **Start Simple**: Begin with basic filters and gradually add complexity
2. **Balance Specificity and Reach**: Very specific filters might result in few qualifying transactions
3. **Consider the User**: Design filters that align with desired user behavior
4. **Stay Informed**: Keep up with developments in the protocols you're targeting
5. **Learn from Others**: Study successful boosts in similar categories
6. **Consider Chain Specifics**: Be aware of differences across multiple chains
7. **Align with Project Goals**: Ensure your chosen actions support overall objectives
## Troubleshooting Tips
* If your boost isn't performing as expected, review your filters for potential issues
* Use the "Filter Preview" feature extensively to test your configurations
* Consider the current market conditions and user behavior when analyzing performance
Remember, creating effective Boost actions is an iterative process. Don't be afraid to experiment and refine your approach based on the results you see.
# Identifying Claimant Information
Source: https://docs.boost.xyz/v2/documentation/action-creation/claimant-information
Learn how to determine the Claimant for your Boost action
The Claimant is typically the address that performed the action you want to reward with your boost. Understanding how to identify the Claimant is crucial for ensuring your Boost rewards the correct users.
## Finding the Claimant
* **Where to find it**: In most cases, the Claimant will be the same as the "From" address we identified earlier in the Action section.
* **For complex cases**: Look in the "Logs" section for events that indicate a specific action was performed.
## Examples
### Simple Transfer
For a simple transfer, the Claimant would be the "From" address:
```
From: 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
```
### Complex DeFi Interaction
For more complex interactions, you might need to look at specific events:
```
Event: Deposit
user (address): 0x123... (This coul
```
# Effective actions
Source: https://docs.boost.xyz/v2/documentation/action-creation/effective-actions
# Getting Started with Etherscan
Source: https://docs.boost.xyz/v2/documentation/action-creation/getting-started
Learn how to navigate Etherscan to find transaction information
To begin creating your Boost action, you'll need to gather information from Etherscan. Here's how to get started:
1. Go to [Etherscan.io](https://etherscan.io/)
2. In the search bar at the top, paste a transaction hash (a long string of letters and numbers)
3. Press Enter or click the search icon
You'll now see a page full of information about the transaction. In the following sections, we'll go through each part step by step, with examples to help you recognize what you're looking at.
Remember, the goal is to understand the transaction details so you can accurately define the on-chain behavior you want to incentivize with your Boost.
# Introduction to Boost Action Creation
Source: https://docs.boost.xyz/v2/documentation/action-creation/introduction
Learn the basics of creating effective Boost actions
Boost actions define the on-chain behavior you want to incentivize. This guide will help you create effective Boost actions using Etherscan data.
## What You'll Learn
1. Navigating Etherscan to find relevant transaction information
2. Understanding key components of a transaction
3. Setting up filters for precise targeting
4. Best practices for creating effective Boost actions
## Action vs. Reward
Before we dive in, it's important to understand the distinction between actions and rewards in the Boost ecosystem:
* **Action**: The on-chain behavior you want to incentivize (e.g., "Swap at least 100 USDC for ETH on Uniswap V3")
* **Reward**: What users receive for completing the action (e.g., "5 PROJECT\_TOKEN for each qualifying swap")
This guide focuses on defining actions. Reward setup is a separate process that comes after action definition.
Let's begin by learning how to navigate Etherscan to find the information you need.
# Analyzing the Logs Section
Source: https://docs.boost.xyz/v2/documentation/action-creation/logs-section
Learn how to use transaction logs to create more precise Boost actions
The Logs section on Etherscan provides valuable information about events that occurred during the transaction. This information can be used to create more precise Boost actions.
## Finding the Logs Section
Scroll down to the "Logs" section on Etherscan. Each log represents an event that happened during the transaction.
## Components of a Log
### Address
* The contract address is shown at the top of each log
* **Example**: Address: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 (USDC token contract)
* **How to use it**: Enter this address in the "Address" field of the Log Filter in the Boost Deploy flow
### Signature
* The event name is shown if the contract is verified
* **Example**: Event: Transfer(address indexed from, address indexed to, uint256 value)
* **How to use it**: Select this event (e.g., "Transfer") in the "Signature" dropdown of the Log Filter
### Parameter Filters
Below the event name, you'll see a list of parameters and their values.
#### Example for a Transfer event:
```
from (address): 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
to (address): 0x6B175474E89094C44Da98b954EedeAC495271d0F
value (uint256): 1000000000000000000 (1 Token)
```
#### Example for a Swap event:
```
sender (address): 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
recipient (address):
```
# Working with Parameter Filters
Source: https://docs.boost.xyz/v2/documentation/action-creation/parameter-filters
Learn how to use parameter filters to refine your Boost action
Parameter filters allow you to further refine your Boost action based on specific values passed to the contract function. Here's how to find and use this information:
1. In the "Input Data" section on Etherscan, look below the function name.
2. You'll see a list of parameters and their values.
## Examples
### Token Transfer
```
recipient (address): 0x6B175474E89094C44Da98b954EedeAC495271d0F
amount (uint256): 1000000000000000000 (1 Token)
```
### Uniswap Swap
```
amountIn (uint256) : 1000000000000000000 (1 Token)
amountOutMin (uint256) : 2275562684821677038 (2.275 Tokens)
path
```
# Practical tips
Source: https://docs.boost.xyz/v2/documentation/action-creation/practical-tips
# Understanding Etherscan Data
Source: https://docs.boost.xyz/v2/documentation/action-creation/understanding-etherscan-data
Learn how to interpret transaction data on Etherscan for Boost actions
To create effective Boost actions, you need to understand the key components of a transaction on Etherscan.
## Navigating Etherscan
1. Go to [Etherscan.io](https://etherscan.io/)
2. Paste a transaction hash in the search bar
3. Press Enter or click the search icon
## Key Transaction Components
### 1. Action Section
* **From**: The address that sent the transaction
* **To**: The contract address being interacted with
* **Value**: The amount of ETH sent (if any)
* **Function Name**: Found in the "Input Data" section
### 2. Input Data
* Contains function parameters and their values
* Helps refine your Boost action
### 3. Logs Section
* Shows events emitted during the transaction
* Includes:
* Address: Contract that emitted the event
* Event Name: Type of event (e.g., Transfer, Swap)
* Event Parameters: Specific details of the event
### 4. Claimant Information
* Usually the "From" address
* For complex interactions, may be found in specific event logs
Understanding these components will help you accurately define the actions you want to boost.
# One-Time Actions Architecture
Source: https://docs.boost.xyz/v2/documentation/architecture/core-boost-v2
An overview of the One-Time Actions protocol architecture and its components
The One-Time Actions protocol architecture is designed to enable flexible, secure, and efficient onchain incentivization. It consists of several key components that work together to validate actions, generate proofs, and distribute rewards.
## Components
* **User**: The end-user interacting with the system.
* **ExternalContract**: The smart contract with which the user interacts to perform the incentivized action.
* **BackendValidator**: An off-chain component that validates the user's action.
* **Core**: The central smart contract of the Boost protocol.
* **SignerValidator**: An on-chain component that verifies the proof of the user's action.
## Process Flow
1. **Action Execution**:
* The User executes an action on the ExternalContract.
* This action is the behavior that Boost aims to incentivize.
2. **Off-chain Validation**:
* The BackendValidator observes and validates the action off-chain.
* This allows for complex validation logic without incurring high gas costs.
3. **Signature Generation**:
* Upon successful validation, the BackendValidator generates a cryptographic signature.
* This signature serves as a proof that the user performed the specified action.
4. **Signature Submission**:
* The User submits the signature to the Core contract.
* This step initiates the on-chain portion of the claiming process.
5. **On-chain Verification**:
* The Core contract calls the SignerValidator with the provided signature.
* The SignerValidator uses EIP-712 signature validation to verify the proof.
* EIP-712 provides a secure method for signing and verifying structured data.
6. **Reward Distribution**:
* If the signature is valid, the Core contract distributes the incentive to the User.
## Choosing a Reward
When setting up a Boost, it's crucial to choose an appropriate reward structure that aligns with your project's goals and tokenomics. Here are some considerations:
1. **Token Type**:
* Native tokens (e.g., ETH)
* Project-specific tokens
* Stablecoins (e.g., USDC, DAI)
2. **Reward Amount**:
* Fixed amount per action
* Percentage-based rewards
* Tiered rewards based on action value or user status
3. **Reward Frequency**:
* Immediate distribution upon action completion
* Periodic distributions (e.g., daily, weekly)
* Milestone-based rewards
4. **Reward Caps**:
* Per-user caps to prevent exploitation
* Total reward caps for budget control
* Time-based caps (e.g., daily limits)
5. **Vesting or Lock-up Periods**:
* Immediate availability
* Gradual vesting to encourage long-term engagement
* Lock-up periods for larger rewards
When choosing a reward structure, consider factors such as:
* The perceived value of the incentivized action
* Your project's token supply and inflation rate
* User behavior and motivation
* Regulatory compliance and tax implications
A well-designed reward structure can effectively drive desired user actions while maintaining the economic balance of your ecosystem.
## Key Capabilities
* **Flexibility**: The architecture allows for incentivizing actions on any external contract, greatly expanding the use cases for Boost.
* **Gas Efficiency**: By moving complex validation off-chain and only verifying a compact signature on-chain, gas costs are significantly reduced.
* **Security**: The use of EIP-712 for structured data signing ensures that the proofs are secure and tamper-resistant.
* **Scalability**: The off-chain validation allows for handling complex conditions and high volumes of actions without congesting the blockchain.
* **Modularity**: The separation of concerns between different components (ExternalContract, BackendValidator, Core, SignerValidator) allows for easier upgrades and maintenance.
## How it works
One-Time Actions include the core flows for deploying, managing, and interacting with action-based incentives:
1. [Deploying Campaigns](/v2/documentation/getting-started/deploying-a-boost)
Learn how to set up and deploy new Boost incentive programs.
2. [Claiming Rewards](/v2/boost-sdk/boost-core/claim-incentive)
Explore the user journey for interacting with a Boost and claiming earned rewards.
3. [Action Creation](/v2/documentation/action-creation/introduction)
Learn how to define the onchain behavior a campaign should reward.
Each of these topics is covered in detail in its respective page.
# Deployed Contracts
Source: https://docs.boost.xyz/v2/documentation/contract-deployments/contracts
Overview of deployed contract addresses across all supported networks
## Active Contracts
### Base
| Name | Address |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| BoostCore | [0x378632819F39C74c4F56B1429E760739c5fb51b7](https://basescan.org/address/0x378632819F39C74c4F56B1429E760739c5fb51b7#code) |
| BoostRegistry | [0x54aBB00F12755Be99729E50dade1BA62c75F8879](https://basescan.org/address/0x54aBB00F12755Be99729E50dade1BA62c75F8879#code) |
| ManagedBudgetWithFees | [0x368245F14cF3F579f5d2B53AcB3bAcA4f6AC0ca6](https://basescan.org/address/0x368245F14cF3F579f5d2B53AcB3bAcA4f6AC0ca6#code) |
| ManagedBudget | [0xf109BAFC51aA2a78C831e5460dEDbDf68E866976](https://basescan.org/address/0xf109BAFC51aA2a78C831e5460dEDbDf68E866976#code) |
| EventAction | [0xa50b6F908a48c9486Bc10801fEC187cfc8ad4224](https://basescan.org/address/0xa50b6F908a48c9486Bc10801fEC187cfc8ad4224#code) |
| AllowListIncentive | [0xCB74EdD24917238c98e7c6d20B047C6D7e0feDdc](https://basescan.org/address/0xCB74EdD24917238c98e7c6d20B047C6D7e0feDdc#code) |
| CGDAIncentive | [0x9Ed41B9834d6865092520D34ee75292242d3fA56](https://basescan.org/address/0x9Ed41B9834d6865092520D34ee75292242d3fA56#code) |
| ERC20Incentive | [0x4b62424bbEbcF33Ab25d109E68AC1E8F0832dd8A](https://basescan.org/address/0x4b62424bbEbcF33Ab25d109E68AC1E8F0832dd8A#code) |
| ERC20PeggedIncentive | [0xCc167F40c9802817d1De42152913553816BFb776](https://basescan.org/address/0xCc167F40c9802817d1De42152913553816BFb776#code) |
| ERC20PeggedVariableCriteriaIncentive | [0x6D28feD9181533a9A638949f12958686baAA886a](https://basescan.org/address/0x6D28feD9181533a9A638949f12958686baAA886a#code) |
| ERC20VariableCriteriaIncentive | [0x7F1f7A55db96BDc2CBF9Dc96cBb25A8f98e8C30c](https://basescan.org/address/0x7F1f7A55db96BDc2CBF9Dc96cBb25A8f98e8C30c#code) |
| ERC20VariableIncentive | [0xB6705B489bcE045137f0ca7283d5b75297f34cF7](https://basescan.org/address/0xB6705B489bcE045137f0ca7283d5b75297f34cF7#code) |
| PointsIncentive | [0xC94a7785fDA51B849f2E781e112A4353F315e057](https://basescan.org/address/0xC94a7785fDA51B849f2E781e112A4353F315e057#code) |
| LimitedSignerValidator | [0x283A297C21D32Ff8e8BA616E5e32De5e05e03aa4](https://basescan.org/address/0x283A297C21D32Ff8e8BA616E5e32De5e05e03aa4#code) |
| SignerValidator | [0x1FC4208467B485f841D1B8AaDbDBBa987bD81a82](https://basescan.org/address/0x1FC4208467B485f841D1B8AaDbDBBa987bD81a82#code) |
| OpenAllowList | [0xEe55C17D2EDBaB27603eB0d0C753c8A173ba7Ed6](https://basescan.org/address/0xEe55C17D2EDBaB27603eB0d0C753c8A173ba7Ed6#code) |
| SimpleAllowList | [0xa75E05550D6D3A7C6E960Ac8f8c4155fC64c12eF](https://basescan.org/address/0xa75E05550D6D3A7C6E960Ac8f8c4155fC64c12eF#code) |
| SimpleDenyList | [0x01B1dB35a5f91080c7518d917aE124E863abB0EB](https://basescan.org/address/0x01B1dB35a5f91080c7518d917aE124E863abB0EB#code) |
### Sepolia
| Name | Address |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| BoostCore | [0x378632819F39C74c4F56B1429E760739c5fb51b7](https://sepolia.etherscan.io/address/0x378632819F39C74c4F56B1429E760739c5fb51b7#code) |
| BoostRegistry | [0x54aBB00F12755Be99729E50dade1BA62c75F8879](https://sepolia.etherscan.io/address/0x54aBB00F12755Be99729E50dade1BA62c75F8879#code) |
| ManagedBudgetWithFees | [0x368245F14cF3F579f5d2B53AcB3bAcA4f6AC0ca6](https://sepolia.etherscan.io/address/0x368245F14cF3F579f5d2B53AcB3bAcA4f6AC0ca6#code) |
| ManagedBudget | [0xf109BAFC51aA2a78C831e5460dEDbDf68E866976](https://sepolia.etherscan.io/address/0xf109BAFC51aA2a78C831e5460dEDbDf68E866976#code) |
| EventAction | [0xa50b6F908a48c9486Bc10801fEC187cfc8ad4224](https://sepolia.etherscan.io/address/0xa50b6F908a48c9486Bc10801fEC187cfc8ad4224#code) |
| AllowListIncentive | [0xCB74EdD24917238c98e7c6d20B047C6D7e0feDdc](https://sepolia.etherscan.io/address/0xCB74EdD24917238c98e7c6d20B047C6D7e0feDdc#code) |
| CGDAIncentive | [0x9Ed41B9834d6865092520D34ee75292242d3fA56](https://sepolia.etherscan.io/address/0x9Ed41B9834d6865092520D34ee75292242d3fA56#code) |
| ERC20Incentive | [0x4b62424bbEbcF33Ab25d109E68AC1E8F0832dd8A](https://sepolia.etherscan.io/address/0x4b62424bbEbcF33Ab25d109E68AC1E8F0832dd8A#code) |
| ERC20PeggedIncentive | [0xCc167F40c9802817d1De42152913553816BFb776](https://sepolia.etherscan.io/address/0xCc167F40c9802817d1De42152913553816BFb776#code) |
| ERC20PeggedVariableCriteriaIncentive | [0x6D28feD9181533a9A638949f12958686baAA886a](https://sepolia.etherscan.io/address/0x6D28feD9181533a9A638949f12958686baAA886a#code) |
| ERC20VariableCriteriaIncentive | [0x7F1f7A55db96BDc2CBF9Dc96cBb25A8f98e8C30c](https://sepolia.etherscan.io/address/0x7F1f7A55db96BDc2CBF9Dc96cBb25A8f98e8C30c#code) |
| ERC20VariableIncentive | [0xB6705B489bcE045137f0ca7283d5b75297f34cF7](https://sepolia.etherscan.io/address/0xB6705B489bcE045137f0ca7283d5b75297f34cF7#code) |
| PointsIncentive | [0xC94a7785fDA51B849f2E781e112A4353F315e057](https://sepolia.etherscan.io/address/0xC94a7785fDA51B849f2E781e112A4353F315e057#code) |
| LimitedSignerValidator | [0x283A297C21D32Ff8e8BA616E5e32De5e05e03aa4](https://sepolia.etherscan.io/address/0x283A297C21D32Ff8e8BA616E5e32De5e05e03aa4#code) |
| SignerValidator | [0x1FC4208467B485f841D1B8AaDbDBBa987bD81a82](https://sepolia.etherscan.io/address/0x1FC4208467B485f841D1B8AaDbDBBa987bD81a82#code) |
| OpenAllowList | [0xEe55C17D2EDBaB27603eB0d0C753c8A173ba7Ed6](https://sepolia.etherscan.io/address/0xEe55C17D2EDBaB27603eB0d0C753c8A173ba7Ed6#code) |
| SimpleAllowList | [0xa75E05550D6D3A7C6E960Ac8f8c4155fC64c12eF](https://sepolia.etherscan.io/address/0xa75E05550D6D3A7C6E960Ac8f8c4155fC64c12eF#code) |
| SimpleDenyList | [0x01B1dB35a5f91080c7518d917aE124E863abB0EB](https://sepolia.etherscan.io/address/0x01B1dB35a5f91080c7518d917aE124E863abB0EB#code) |
### Base Sepolia
| Name | Address |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| BoostCore | [0x378632819F39C74c4F56B1429E760739c5fb51b7](https://sepolia.basescan.org/address/0x378632819F39C74c4F56B1429E760739c5fb51b7#code) |
| BoostRegistry | [0x54aBB00F12755Be99729E50dade1BA62c75F8879](https://sepolia.basescan.org/address/0x54aBB00F12755Be99729E50dade1BA62c75F8879#code) |
| ManagedBudgetWithFees | [0x368245F14cF3F579f5d2B53AcB3bAcA4f6AC0ca6](https://sepolia.basescan.org/address/0x368245F14cF3F579f5d2B53AcB3bAcA4f6AC0ca6#code) |
| ManagedBudget | [0xf109BAFC51aA2a78C831e5460dEDbDf68E866976](https://sepolia.basescan.org/address/0xf109BAFC51aA2a78C831e5460dEDbDf68E866976#code) |
| EventAction | [0xa50b6F908a48c9486Bc10801fEC187cfc8ad4224](https://sepolia.basescan.org/address/0xa50b6F908a48c9486Bc10801fEC187cfc8ad4224#code) |
| AllowListIncentive | [0xCB74EdD24917238c98e7c6d20B047C6D7e0feDdc](https://sepolia.basescan.org/address/0xCB74EdD24917238c98e7c6d20B047C6D7e0feDdc#code) |
| CGDAIncentive | [0x9Ed41B9834d6865092520D34ee75292242d3fA56](https://sepolia.basescan.org/address/0x9Ed41B9834d6865092520D34ee75292242d3fA56#code) |
| ERC20Incentive | [0x4b62424bbEbcF33Ab25d109E68AC1E8F0832dd8A](https://sepolia.basescan.org/address/0x4b62424bbEbcF33Ab25d109E68AC1E8F0832dd8A#code) |
| ERC20PeggedIncentive | [0xCc167F40c9802817d1De42152913553816BFb776](https://sepolia.basescan.org/address/0xCc167F40c9802817d1De42152913553816BFb776#code) |
| ERC20PeggedVariableCriteriaIncentive | [0x6D28feD9181533a9A638949f12958686baAA886a](https://sepolia.basescan.org/address/0x6D28feD9181533a9A638949f12958686baAA886a#code) |
| ERC20VariableCriteriaIncentive | [0x7F1f7A55db96BDc2CBF9Dc96cBb25A8f98e8C30c](https://sepolia.basescan.org/address/0x7F1f7A55db96BDc2CBF9Dc96cBb25A8f98e8C30c#code) |
| ERC20VariableIncentive | [0xB6705B489bcE045137f0ca7283d5b75297f34cF7](https://sepolia.basescan.org/address/0xB6705B489bcE045137f0ca7283d5b75297f34cF7#code) |
| PointsIncentive | [0xC94a7785fDA51B849f2E781e112A4353F315e057](https://sepolia.basescan.org/address/0xC94a7785fDA51B849f2E781e112A4353F315e057#code) |
| LimitedSignerValidator | [0x283A297C21D32Ff8e8BA616E5e32De5e05e03aa4](https://sepolia.basescan.org/address/0x283A297C21D32Ff8e8BA616E5e32De5e05e03aa4#code) |
| SignerValidator | [0x1FC4208467B485f841D1B8AaDbDBBa987bD81a82](https://sepolia.basescan.org/address/0x1FC4208467B485f841D1B8AaDbDBBa987bD81a82#code) |
| OpenAllowList | [0xEe55C17D2EDBaB27603eB0d0C753c8A173ba7Ed6](https://sepolia.basescan.org/address/0xEe55C17D2EDBaB27603eB0d0C753c8A173ba7Ed6#code) |
| SimpleAllowList | [0xa75E05550D6D3A7C6E960Ac8f8c4155fC64c12eF](https://sepolia.basescan.org/address/0xa75E05550D6D3A7C6E960Ac8f8c4155fC64c12eF#code) |
| SimpleDenyList | [0x01B1dB35a5f91080c7518d917aE124E863abB0EB](https://sepolia.basescan.org/address/0x01B1dB35a5f91080c7518d917aE124E863abB0EB#code) |
***
## Inactive Contracts
### Base
| Name | Address |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| BoostCore | [0xDCFFcE9d8185706780A46Cf04D9c6b86b3451497](https://basescan.org/address/0xDCFFcE9d8185706780A46Cf04D9c6b86b3451497#code) |
| EventAction | [0xAADe59E7B473c4f03b47C6CEd4931ae8B5e5F2bA](https://basescan.org/address/0xAADe59E7B473c4f03b47C6CEd4931ae8B5e5F2bA#code) |
| CGDAIncentive | [0xAE1B4db43206c20B611329452f0281DF155EfbF7](https://basescan.org/address/0xAE1B4db43206c20B611329452f0281DF155EfbF7#code) |
| ERC20Incentive | [0x3Ecf866C33BcB2DEc4A85b3298391c0f70d645AA](https://basescan.org/address/0x3Ecf866C33BcB2DEc4A85b3298391c0f70d645AA#code) |
| ERC20VariableCriteriaIncentive | [0x7cdb385Ce8F0A35008aaA8B776b566044bca8Ca7](https://basescan.org/address/0x7cdb385Ce8F0A35008aaA8B776b566044bca8Ca7#code) |
| ERC20VariableIncentive | [0x8cD24299D40c76f33065dE6eD4ffAB3d86Db6950](https://basescan.org/address/0x8cD24299D40c76f33065dE6eD4ffAB3d86Db6950#code) |
### Sepolia
| Name | Address |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| BoostCore | [0xDCFFcE9d8185706780A46Cf04D9c6b86b3451497](https://sepolia.etherscan.io/address/0xDCFFcE9d8185706780A46Cf04D9c6b86b3451497#code) |
| EventAction | [0xAADe59E7B473c4f03b47C6CEd4931ae8B5e5F2bA](https://sepolia.etherscan.io/address/0xAADe59E7B473c4f03b47C6CEd4931ae8B5e5F2bA#code) |
| CGDAIncentive | [0xAE1B4db43206c20B611329452f0281DF155EfbF7](https://sepolia.etherscan.io/address/0xAE1B4db43206c20B611329452f0281DF155EfbF7#code) |
| ERC20Incentive | [0x3Ecf866C33BcB2DEc4A85b3298391c0f70d645AA](https://sepolia.etherscan.io/address/0x3Ecf866C33BcB2DEc4A85b3298391c0f70d645AA#code) |
| ERC20VariableCriteriaIncentive | [0x7cdb385Ce8F0A35008aaA8B776b566044bca8Ca7](https://sepolia.etherscan.io/address/0x7cdb385Ce8F0A35008aaA8B776b566044bca8Ca7#code) |
| ERC20VariableIncentive | [0x8cD24299D40c76f33065dE6eD4ffAB3d86Db6950](https://sepolia.etherscan.io/address/0x8cD24299D40c76f33065dE6eD4ffAB3d86Db6950#code) |
### Base Sepolia
| Name | Address |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| BoostCore | [0xDCFFcE9d8185706780A46Cf04D9c6b86b3451497](https://sepolia.basescan.org/address/0xDCFFcE9d8185706780A46Cf04D9c6b86b3451497#code) |
| EventAction | [0xAADe59E7B473c4f03b47C6CEd4931ae8B5e5F2bA](https://sepolia.basescan.org/address/0xAADe59E7B473c4f03b47C6CEd4931ae8B5e5F2bA#code) |
| CGDAIncentive | [0xAE1B4db43206c20B611329452f0281DF155EfbF7](https://sepolia.basescan.org/address/0xAE1B4db43206c20B611329452f0281DF155EfbF7#code) |
| ERC20Incentive | [0x3Ecf866C33BcB2DEc4A85b3298391c0f70d645AA](https://sepolia.basescan.org/address/0x3Ecf866C33BcB2DEc4A85b3298391c0f70d645AA#code) |
| ERC20VariableCriteriaIncentive | [0x7cdb385Ce8F0A35008aaA8B776b566044bca8Ca7](https://sepolia.basescan.org/address/0x7cdb385Ce8F0A35008aaA8B776b566044bca8Ca7#code) |
| ERC20VariableIncentive | [0x8cD24299D40c76f33065dE6eD4ffAB3d86Db6950](https://sepolia.basescan.org/address/0x8cD24299D40c76f33065dE6eD4ffAB3d86Db6950#code) |
See all contract deployment addresses on GitHub
# Deploying a One-Time Action
Source: https://docs.boost.xyz/v2/documentation/getting-started/deploying-a-boost
Select an action, configure rewards, and launch your boost campaign
These docs are under active development and will be subject to changes.
This guide walks you through the steps to deploy a boost: selecting your action, choosing a budget and token, configuring rewards, and launching your campaign.
## Step 1: Select Your Action
Before configuring rewards, choose which action to incentivize.
Select from your approved actions. Only actions that have passed review can be used for boosts.
If you don't have an approved action yet, you'll need to [set one up first](/v2/documentation/getting-started/setting-up-an-action).
Review the action configuration to make sure it targets the right activity:
* **Contract and event** being monitored
* **Who receives rewards** (the claimant)
* **Any filters** applied to qualifying transactions
## Step 2: Choose Your Budget
Select the budget account that will fund this boost and set your spend limit.
### Selecting a Budget Account
Choose from budget accounts where you have Admin or Manager access. Make sure the account:
* Is on the chain where you want to run the campaign
* Has sufficient token balance for your planned rewards
### Understanding Protocol Fees
**Boost charges a 10% protocol fee on all rewards distributed.** This fee is automatically added to your spend limit when you deploy.
When you set a spend limit, the protocol fee is calculated on top:
| You Set | Protocol Fee (10%) | Total Required |
| ------------- | ------------------ | -------------- |
| 1,000 tokens | 100 tokens | 1,100 tokens |
| 5,000 tokens | 500 tokens | 5,500 tokens |
| 10,000 tokens | 1,000 tokens | 11,000 tokens |
**Example:** If you want to distribute 1,000 USDC in rewards, you'll need 1,100 USDC in your budget account (1,000 for rewards + 100 for fees).
The fee is only charged on rewards that are actually claimed. If you end a campaign early and withdraw unused funds, you only pay fees on what was distributed.
## Step 3: Select Your Reward Token
Choose which token to distribute as rewards.
### Token Whitelist
Only whitelisted tokens can be used for boost rewards. This protects users from receiving spam tokens or malicious contracts.
The token whitelist includes popular, verified tokens on each supported chain. If you don't see your token:
1. **Check what's available** in the token dropdown when deploying
2. **Request a new token** by contacting the Boost team on [Discord](https://discord.gg/gdu3EpeqsD)
When requesting a token, provide:
* Token contract address
* Chain (Base, Optimism, etc.)
* Token name and symbol
* Brief description of why you need it
Token requests are typically reviewed within 24-48 hours. Plan ahead if you need a specific token for your campaign.
## Step 4: Configure Rewards
Choose how much to reward participants. Boost supports two reward types:
Everyone who qualifies receives the **same token amount**.
**You configure:**
* **Reward Amount**: Number of tokens per claim (e.g., 10 USDC per mint)
* **Spend Limit**: Maximum tokens to distribute before the boost ends
**Example:**
* Reward: 50 tokens per action
* Spend limit: 5,000 tokens
* Result: Up to 100 people can claim (5,000 ÷ 50)
**Best for:** Simple campaigns where all participants should be treated equally.
Rewards scale based on **a percentage of the on-chain value**.
**You configure:**
* **Reward Percentage**: Percent of the transaction value to reward (e.g., 5% of swap amount)
* **Max Reward**: Cap on tokens per claim to control costs
* **Spend Limit**: Maximum tokens to distribute overall
**Example:**
* Reward: 5% of swap value
* Max reward: 100 tokens
* User swaps 1,000 USDC → Receives 50 tokens (5%)
* User swaps 5,000 USDC → Receives 100 tokens (capped at max)
**Best for:** Incentivizing larger transactions or scaling rewards proportionally.
### Choosing the Right Reward Amount
Consider these factors when setting rewards:
* **Action difficulty**: Harder or higher-commitment actions warrant larger rewards
* **Target audience**: New users may need more incentive than existing users
* **Competition**: Check what similar campaigns are offering
* **Your budget**: Start conservative and top up if the campaign performs well
## Step 5: Configure Allowlists (Optional)
Allowlists restrict who can claim rewards to a specific set of addresses.
### When to Use Allowlists
* Targeting a specific community or holder group
* Running exclusive campaigns for partners
* Preventing bot/sybil farming
* Rewarding existing users only
* You want maximum reach and participation
* Running open acquisition campaigns
* The action itself is restrictive enough
* You want organic discovery
### Allowlist Tradeoffs
| Approach | Pros | Cons |
| --------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------- |
| **With Allowlist** | Targeted distribution, reduced farming risk, predictable audience | Limited reach, requires maintaining address list, may miss organic users |
| **Without Allowlist** | Maximum participation, organic growth, simpler setup | Higher farming risk, less predictable costs, may attract non-target users |
### Setting Up an Allowlist
Create a list of wallet addresses that should be eligible. Common sources:
* NFT holders (snapshot of a collection)
* Token holders above a threshold
* Previous campaign participants
* Partner community members
Upload your address list when configuring the boost. The system will validate the addresses.
Check that your allowlist covers your intended audience. You can update the list later if needed.
Start without an allowlist to gauge organic interest, then add one if you see farming behavior.
## Step 6: Review and Deploy
Before launching, verify your configuration:
The preview shows recent transactions that would qualify for rewards. Verify:
* The right transactions are being captured
* Claimant addresses look correct
* Reward amounts match your expectations
Confirm your total budget including the 10% protocol fee:
| Item | Amount |
| ------------------ | ------------------------------ |
| Spend Limit | Your configured amount |
| Protocol Fee (10%) | Added automatically |
| **Total Required** | Must be in your budget account |
Click "Deploy Boost" and approve the transaction. On supported chains (Base, Optimism, Celo, World Chain), the transaction is sponsored—no gas needed.
## After Deployment
Once live, your boost will:
* Start accepting claims immediately
* Show up in your dashboard for monitoring
* Distribute rewards as users complete the action
Monitor performance, top up budget, or end campaigns.
## Quick Reference
| Setting | Fixed Rewards | Variable Rewards |
| ------------------ | ---------------------- | ---------------------- |
| **Reward** | Token amount per claim | Percentage of tx value |
| **Cap** | N/A (fixed amount) | Max reward per claim |
| **Best for** | Equal treatment | Scaling with activity |
| **Predictability** | High | Medium |
## Troubleshooting
You need to create and get an action approved before deploying a boost. See [Setting up an Action](/v2/documentation/getting-started/setting-up-an-action).
Remember to account for the 10% protocol fee. If your spend limit is 1,000 tokens, you need 1,100 tokens in your budget.
Only whitelisted tokens are available. Request your token on [Discord](https://discord.gg/gdu3EpeqsD) with the contract address and chain.
This could mean your action parameters are too restrictive, or no matching transactions have occurred recently. This doesn't prevent deployment—new qualifying transactions will be captured once you're live.
Start without one unless you have a specific audience in mind. You can always create a new boost with an allowlist if you see unwanted behavior.
# Managing One-Time Actions
Source: https://docs.boost.xyz/v2/documentation/getting-started/managing-boosts
Monitor, modify, and manage your active boost campaigns
These docs are under active development and will be subject to changes.
Once your boosts are deployed, you can monitor their performance, add funds, end campaigns, or duplicate successful configurations. This guide covers all aspects of managing your active campaigns.
## Dashboard Overview
Your dashboard provides a centralized view of all your boost campaigns.
### Boosts Table
The main boosts table displays:
| Column | Description |
| ------------------ | --------------------------------------------- |
| **Boost** | Campaign name, reward token, and status badge |
| **Budget Claimed** | Amount distributed vs. total budget |
| **Avg. CPC** | Average cost per claim |
| **Claims** | Total number of successful claims |
| **Actions** | Quick actions menu |
Click on any row to view detailed information about that boost.
## Viewing Boost Details
Click on a boost to see comprehensive campaign information:
### Performance Metrics
* **Total Claims**: Number of successful reward claims
* **Budget Used**: Amount of tokens distributed
* **Remaining Balance**: Tokens still available for claims
* **Average CPC**: Cost per claim for campaign efficiency analysis
### Activity Feed
View recent claim activity including:
* Claimant addresses
* Claim timestamps
* Reward amounts
* Transaction links
## Managing Active Campaigns
### Top Up Budget
Add more funds to an active campaign to extend its reach.
Click the three-dot menu on the boost row, or navigate to the boost details page.
Choose "Top Up" from the actions menu.
Specify the amount of tokens to add to the campaign.
Top-up tokens are drawn from your budget account. Ensure sufficient balance.
Approve the transaction in your wallet to complete the top-up.
### End Campaign
Stop a campaign early and withdraw remaining funds.
Click the three-dot menu on the boost row.
Choose "End Campaign" from the actions menu.
Ending a campaign is irreversible. Make sure you want to stop accepting new claims.
Review the remaining balance that will be returned to your budget account, then confirm the transaction.
### Duplicate Boost
Quickly create a new campaign based on an existing configuration.
Click the three-dot menu on the boost you want to duplicate.
Choose "Duplicate" from the actions menu. This opens the deploy flow pre-filled with the existing configuration.
Adjust any settings for your new campaign:
* Update action parameters
* Change reward amounts
* Select a different budget account
Complete the deploy flow to launch your new campaign.
Duplicating is a great way to iterate on successful campaigns or run A/B tests with different reward amounts.
## Campaign Status
Boosts can have the following statuses:
| Status | Description |
| ------------ | ------------------------------------- |
| **Active** | Campaign is live and accepting claims |
| **Depleted** | Budget has been fully claimed |
| **Ended** | Campaign was manually ended |
## Analyzing Performance
Use these metrics to evaluate your campaigns:
Track how quickly claims are coming in to gauge campaign engagement.
Compare average CPC across campaigns to optimize reward amounts.
Monitor what percentage of your budget is being claimed.
See how many unique addresses are participating.
## Best Practices
Check your campaigns daily, especially in the first few days after launch. Early monitoring helps identify issues with action configuration or reward settings.
If a campaign is performing well, add funds before the budget runs out to maintain momentum.
Use the analytics to understand what works. Higher claim rates might indicate better-targeted actions or more attractive rewards.
Use the duplicate feature to quickly launch variations of successful campaigns with different parameters.
# Setting up Accounts
Source: https://docs.boost.xyz/v2/documentation/getting-started/setting-up-accounts
Create and configure your team and budget accounts to start deploying boosts
These docs are under active development and will be subject to changes.
Before you can deploy boosts, you need to set up your accounts. This guide walks you through authentication, creating a team, setting up a budget account, and inviting team members.
## Prerequisites
* An email address or Google account for authentication
* Tokens to fund your budget account (on a supported chain)
## Authentication with Privy
Boost uses [Privy](https://privy.io) for authentication, which provides a seamless onboarding experience with automatic wallet creation.
Navigate to [boost.xyz](https://boost.xyz) and click "Sign In" or "Get Started."
Select your preferred authentication method:
* **Email**: Enter your email and verify with a code
* **Google**: Sign in with your Google account
You don't need an existing crypto wallet to get started. Boost will create one for you automatically.
When you authenticate, Privy automatically creates an embedded Ethereum wallet linked to your account. This wallet is:
* **Secure**: Managed by Privy with enterprise-grade security
* **Gasless**: Transactions are sponsored, so you don't need ETH for gas
* **Portable**: Accessible across devices when you sign in
### Sponsored Transactions
Boost uses [Alchemy Account Kit](https://www.alchemy.com/account-kit) for smart account functionality. This means:
* **No gas fees**: Your transactions are sponsored on supported chains
* **Simplified UX**: No need to hold ETH in your wallet for gas
* **Supported chains**: Base, Optimism, Celo, and World Chain
While transactions are gasless, you still need to fund your budget account with tokens for boost rewards.
## Create a Team
Teams allow you to organize your boost campaigns and collaborate with others. Each team has a budget account that holds the tokens used to fund rewards.
After signing in, you'll be prompted to create a team. Click "Create Team" to begin.
Configure your team:
* **Team Name**: A name to identify your organization
* **Team Avatar** (optional): Upload an image for your team
Your team's budget account is deployed as a smart contract on the blockchain. This happens automatically during team creation.
Budget accounts use the `ManagedBudgetWithFeesV2` smart contract, which provides secure fund management and role-based access control.
## Understanding Budget Accounts
Budget accounts are smart contracts that securely manage your boost funds. They provide:
* **On-chain security**: Funds are held in audited smart contracts
* **Role-based access**: Control who can deploy boosts and manage funds
* **Multi-token support**: Hold multiple token types in one account
* **Withdrawal protection**: Only authorized users can withdraw funds
### Budget Account Structure
Each budget account has:
| Property | Description |
| ----------- | --------------------------------------------- |
| **Address** | The on-chain address where funds are held |
| **Owner** | The original creator with full control |
| **Chain** | The blockchain network (e.g., Base, Optimism) |
| **Members** | Team members with assigned roles |
## Team Roles
Boost supports two roles for team members, each with different permissions:
**Full Control**
Admins can:
* Deploy and manage boosts
* Fund and withdraw from the budget
* Invite and remove team members
* Change member roles
The team owner automatically has admin privileges that cannot be revoked.
**Deploy Only**
Managers can:
* Deploy boosts using the team's budget
* View boost performance and analytics
Managers cannot:
* Withdraw funds
* Invite or manage team members
* Access budget settings
### Role Permissions Summary
| Permission | Admin | Manager |
| -------------- | ----- | ------- |
| Deploy boosts | Yes | Yes |
| View analytics | Yes | Yes |
| Fund budget | Yes | No |
| Withdraw funds | Yes | No |
| Invite members | Yes | No |
| Remove members | Yes | No |
| Change roles | Yes | No |
## Inviting Team Members
Invite colleagues to collaborate on your boost campaigns. The invite system automatically creates wallets for new users.
Navigate to your team settings or click "Invite Members" from the dashboard.
For each person you want to invite:
* Enter their **email address**
* Select their **role** (Admin or Manager)
You can invite multiple people at once. Each will receive a personalized email invitation.
Review your invitations and confirm. This triggers:
1. **Wallet pre-generation**: A wallet is automatically created for each invitee
2. **On-chain role grant**: Their wallet address is granted the appropriate role on the budget contract
3. **Email notification**: Each invitee receives an email with instructions to join
Role grants are on-chain transactions. Thanks to sponsored transactions, this doesn't cost you gas.
### How Invites Work
When you invite someone:
1. **If they're new to Boost**: A Privy account and embedded wallet are created for them automatically
2. **If they already have an account**: Their existing wallet is linked to your team
The invitee receives an email with:
* Your team name
* Their assigned role
* A link to accept and get started
### Invite Status
Track the status of your invitations:
| Status | Description |
| ------------------ | ---------------------------------------- |
| **Wallet Created** | Wallet generated, email not yet sent |
| **Invited** | Email sent, awaiting acceptance |
| **Accepted** | Member has signed in and joined the team |
| **Cancelled** | Invitation was revoked |
## Funding Your Budget
After creating your team, you need to fund your budget account with tokens for boost rewards.
From your dashboard, go to your budget account and click "Fund Budget" or "Deposit."
Your budget account has a unique address on the blockchain. Copy this address to send tokens.
Make sure you're sending tokens on the correct network (the same chain as your budget account).
Send tokens to your budget address from any wallet. Only whitelisted tokens can be used for boost rewards.
### Supported Funding Methods
Send tokens directly to your budget address from any wallet or exchange.
Bridge tokens from another chain and send to your budget address.
## Managing Your Team
### Viewing Members
Access your team roster from the dashboard to see:
* All current members and their roles
* Pending invitations
* Member activity
### Modifying Roles
Admins can change member roles:
Click on a team member to view their details.
Choose "Edit Role" and select the new role (Admin or Manager).
Confirm the role change. This updates their permissions on the budget contract.
### Removing Members
To remove a team member:
1. Select the member from your team roster
2. Click "Remove Member"
3. Confirm the removal
Removing a member revokes their role on the budget contract. They will no longer be able to deploy boosts or access team resources.
## Managing Your Budget
Check your current token balances, reserved amounts for active boosts, and available funds.
Top up your budget at any time by sending additional tokens to your budget address.
Admins can withdraw unused funds back to any wallet address.
Monitor how your budget is being used across all active campaigns.
## Multiple Budget Accounts
You can create or join multiple budget accounts for different purposes:
* **Separate campaigns**: Different budgets for different projects
* **Multiple chains**: Budget accounts on different networks
* **Team separation**: Different teams with independent budgets
To switch between budget accounts, use the account selector in your dashboard.
## Troubleshooting
Check your spam folder. If you still don't see it, ask the team admin to resend the invitation. Make sure your email address was entered correctly.
This is rare since transactions are sponsored. If it happens:
* Ensure you have an active internet connection
* Try refreshing the page and attempting again
* Contact support if the issue persists
* Verify you sent tokens to the correct budget address
* Confirm you're on the same network as your budget account
* Wait a few moments for the transaction to confirm
* Refresh your dashboard
Check that:
* Their invitation was accepted (status shows "Accepted")
* They have at least Manager role
* They're connected with the correct account
Contact your team admin to have your role revoked. If you're the owner, you'll need to transfer ownership before leaving.
## Next Steps
Once your accounts are set up and funded, you're ready to create your first boost campaign.
Learn how to configure the on-chain action you want to incentivize.
# Setting up an Action
Source: https://docs.boost.xyz/v2/documentation/getting-started/setting-up-an-action
Configure the on-chain action you want to incentivize with your boost
These docs are under active development and will be subject to changes.
Actions define what onchain behavior you want to reward. With One-Time Actions, you can incentivize virtually any blockchain activity, from NFT mints to token swaps to governance votes.
## What is an Action?
An action tells Boost what to look for on the blockchain. When someone performs the activity you've defined, they become eligible for rewards.
Every action has three parts:
Which smart contract and which specific activity (like a mint or transfer) to monitor.
How to identify the person who performed the action so they can claim their reward.
Any specific conditions that must be met (like a minimum amount or specific token).
One-Time Actions can target custom onchain activity across supported contracts and chains.
## Creating an Action from a Transaction
The easiest way to set up an action is to show Boost an example transaction. Just paste a transaction hash, and Boost will automatically figure out the details.
### Step-by-Step Guide
Go to a block explorer like [Basescan](https://basescan.org) or [Etherscan](https://etherscan.io) and find a transaction that represents what you want to incentivize.
**Good examples:**
* Someone minting an NFT you want to promote
* A swap on your DEX
* A vote or delegation in your governance system
Copy the transaction hash (the long string starting with `0x...`).
Enter the transaction hash in the Boost action creator. The system will automatically:
* Pull up the transaction details
* Identify all the activities that happened
* Show you which ones can be used for rewards
You'll see a list of events from the transaction. Pick the one that best represents what you want to incentivize.
For example, if you want to reward NFT mints, you'd typically select a `Transfer` or `Mint` event.
Don't worry if you're not sure which to pick—the AI assistant can help you choose the right one.
## Let AI Help You Configure
Boost includes an AI assistant (powered by Claude) that can help you set up your action. Just describe what you want to reward in plain English, and the AI will suggest the best configuration.
### How It Works
Write a simple description of the behavior you want to incentivize:
* *"Reward anyone who mints an NFT from this collection"*
* *"Give rewards to users who swap at least \$100 worth of tokens"*
* *"Incentivize people who vote on governance proposals"*
Based on your description and the transaction you provided, the AI will:
* Pick the right event to track
* Figure out how to identify who performed the action
* Set up any filters you need
* Recommend whether to use fixed or variable rewards
* Explain its reasoning so you can verify it makes sense
Look over the AI's suggestions. You can accept them as-is or make changes if needed.
The AI will also warn you if something doesn't look right—for example, if your description asks for something the transaction doesn't support.
### Fixed vs Variable Rewards
The AI will recommend a reward type based on your description:
Everyone who qualifies gets the same reward amount.
**The AI suggests fixed rewards when you say things like:**
* *"Give 100 tokens for each mint"*
* *"Reward exactly 50 USDC per vote"*
* *"Pay 10 tokens to anyone who completes this action"*
**Best for:** Simple campaigns where all participants should be treated equally.
Rewards scale based on what the user did (like how much they swapped or how many NFTs they minted).
**The AI suggests variable rewards when you say things like:**
* *"Reward users who swap at least 100 USDC"*
* *"Give more rewards for larger purchases"*
* *"Scale rewards based on the number of NFTs minted"*
**Best for:** Campaigns where you want to incentivize bigger actions.
## Providing Project Information
When creating an action, you'll need to provide some information about your project:
Enter the URL where users can perform the action you're incentivizing. This helps the Boost team verify your action and helps users find where to participate.
**Examples:**
* `https://yourproject.xyz/mint`
* `https://app.yourdex.com/swap`
* `https://governance.yourprotocol.io`
The project URL is required. Make sure it's the actual page where users will perform the action.
Add any additional context that helps explain what your project does and why you're running this campaign.
## Preview Your Action
Before submitting, you can preview recent transactions that match your configuration:
This preview helps you verify that:
* The right transactions are being captured
* The correct people would receive rewards
* Your filters are working as expected
If the preview shows unexpected results, go back and adjust your configuration. It's much easier to fix issues now than after your action is live.
## Review and Approval Process
All new actions must be reviewed and approved by the Boost team before you can use them. This typically takes 24-48 hours.
### Why Review is Required
The review process ensures that:
* Your action is set up correctly and will work as intended
* The configuration captures the right transactions
* There are no security issues or ways to game the system
* Your project information is accurate
### What the Team Reviews
When you submit an action, the Boost team will:
| Review Item | What They Check |
| ------------------------- | -------------------------------------------------------------------------------- |
| **Test the action** | Run your configuration against real transactions to make sure it works correctly |
| **Verify the setup** | Confirm the right events, parameters, and filters are configured |
| **Check security** | Look for potential abuse vectors or gaming opportunities |
| **Validate project info** | Verify the project URL and details you provided are accurate |
### Action Status
After submitting, your action will have one of these statuses:
| Status | What It Means |
| ------------ | -------------------------------------------------------- |
| **Pending** | Waiting for the Boost team to review |
| **Approved** | Ready to use! You can now deploy boosts with this action |
| **Rejected** | Something needs to be fixed—check the feedback provided |
### If Your Action is Rejected
Don't worry—rejections usually just mean something needs a small fix. You'll receive feedback explaining:
* What the issue was
* How to fix it
You can then create a new action with the corrections. If you're unsure about the feedback, reach out on [Discord](https://discord.gg/gdu3EpeqsD) for help.
## Tips for Success
When choosing your example transaction, pick one that represents normal user behavior. Unusual or edge-case transactions might lead to configurations that don't work well for most users.
The more specific you are, the better the AI can help:
* ❌ *"Reward mints"*
* ✅ *"Reward users who mint at least 2 NFTs from the CoolCats collection"*
Always check the activity preview before submitting. It shows you exactly what transactions would qualify, so you can catch any issues early.
Make sure the URL you provide is correct and leads to where users can actually perform the action. This speeds up the review process.
## Next Steps
Once your action is approved, you're ready to configure your rewards and launch your campaign.
Learn how to set up rewards and deploy your boost campaign.
# Boost API
Source: https://docs.boost.xyz/v2/documentation/links/boost-api
Develop using the Boost API
# Boost API Reference
Source: https://docs.boost.xyz/v2/documentation/links/boost-api-reference
Reference for the Boost API
# Boost SDK
Source: https://docs.boost.xyz/v2/documentation/links/boost-sdk
Develop using the Boost SDK
# One-Time Actions
Source: https://docs.boost.xyz/v2/documentation/overview/introduction
Reward users once when they complete a qualifying onchain action
One-Time Actions (*also known as **Boosts***) let teams reward users for completing discrete onchain activity, such as mints, swaps, votes, delegations, and contract interactions.
A One-Time Action defines what onchain activity qualifies, who should be treated as the claimant, and what reward the user can claim after the action is validated.
**One-Time Actions are also known as *Boosts*.** You'll see both terms throughout the docs, SDK, and contracts (e.g., "deploy a boost", `BoostCore`). They refer to the same thing — an individual One-Time Actions program.
## What You Can Do
Create token incentive programs that reward users for qualifying onchain actions.
Integrate One-Time Actions into your app using the SDK or API.
## Getting Started
If you're looking to deploy a One-Time Action, start here:
Create your team and fund your budget account.
[Set up Accounts](/v2/documentation/getting-started/setting-up-accounts)
Define what onchain activity you want to reward.
[Set up an Action](/v2/documentation/getting-started/setting-up-an-action)
Configure rewards and launch your One-Time Action.
[Deploy a One-Time Action](/v2/documentation/getting-started/deploying-a-boost)
Monitor performance, top up, or end One-Time Actions.
[Manage One-Time Actions](/v2/documentation/getting-started/managing-boosts)
## For Developers
TypeScript SDK for deploying and managing One-Time Actions programmatically.
REST API guides for querying One-Time Actions, claims, and activity.
Understand the One-Time Actions protocol flow.
# Frequently Asked Questions
Source: https://docs.boost.xyz/v2/documentation/support/faqs
Common questions about Boost campaigns, fees, supported chains, and how rewards reach users
## Choosing a Product
Use **Time-Based Incentives** when you want users to be rewarded for *how long* and *how much* they keep capital in your protocol — for example, depositors holding a position in a vault, lenders, LPs, or stakers. Rewards accrue continuously over the campaign window.
Use **One-Time Actions** when you want to reward a *discrete* onchain action — a swap, mint, vote, delegation, or contract call. Each qualifying user can claim once.
If you're not sure, [reach out to the team](mailto:support@boost.xyz) and we can help you pick.
Yes. The two products are independent and serve different goals, so it's common for a protocol to use both at different times.
## Onboarding & Setup
* **One-Time Actions** are self-serve. Sign in at [boost.xyz](https://boost.xyz) and follow [Setting up Accounts](/v2/documentation/getting-started/setting-up-accounts) to create your team and deploy your first campaign.
* **Time-Based Incentives** are partnership-driven. [Email the Boost team](mailto:support@boost.xyz) to scope a campaign — see [Launch a Campaign](/tbi/launch-a-campaign) for what onboarding looks like.
No. The platform uses [Privy](https://privy.io) for authentication and creates an embedded wallet automatically when you sign in with email or Google.
* **One-Time Actions**: Action review takes 24-48 hours. Once approved, deploying the campaign takes a few minutes.
* **Time-Based Incentives**: Onboarding timelines depend on integration scope. Plan to start the conversation a couple of weeks before you want to go live.
## Fees & Funding
Boost charges a **10% protocol fee** on rewards distributed through One-Time Actions. The fee is added on top of your spend limit when you deploy — if you want to distribute 1,000 USDC, you'll need 1,100 USDC in your budget account. The fee is only charged on rewards that are actually claimed.
Time-Based Incentives campaign economics are scoped during onboarding.
The protocol running the campaign funds it. Rewards are sent from a budget account (One-Time Actions) or a campaign-funded reward pool (Time-Based Incentives) that you control.
Yes. If a campaign ends with rewards left undistributed, the unused balance is recoverable by the team that funded it.
## Chains & Tokens
One-Time Actions sponsored transactions are supported on **Base, Optimism, Celo, and World Chain**. Other EVM chains may be available depending on the campaign — [reach out](mailto:support@boost.xyz) if you need a specific chain.
For One-Time Actions, only whitelisted ERC-20 tokens can be used. The whitelist includes popular, verified tokens on each supported chain. If your token isn't listed, request it on [Discord](https://discord.gg/gdu3EpeqsD) with the contract address and chain — requests are typically reviewed within 24-48 hours.
For Time-Based Incentives, the reward token is configured during campaign setup with the team.
## For Your Users
Both products surface campaigns to end users on [rabbithole.gg](https://rabbithole.gg) — Boost's consumer app. Users browse active campaigns, track their progress, and claim rewards directly from there. You don't need to host a claim UI yourself.
On supported chains, transactions are sponsored, so users don't need ETH to claim. Time-Based Incentives also has a gasless [earning activation](/tbi/core-concepts/earning-activation) flow for opting in.
Yes:
* **One-Time Actions** support allowlists — restrict claims to a specific set of addresses (NFT holders, token holders, partner communities, etc.).
* **Time-Based Incentives** support eligibility filters like minimum balance, new capital only, and earning activation, which limits rewards to users who actually came through Boost.
If you see unwanted behavior, you can also end a campaign early.
## Running Campaigns
Yes. The team that owns the campaign can stop it at any time. Remaining funds are returned to the funding account.
For One-Time Actions, you can top up the budget at any time from the dashboard. Time-Based Incentives campaigns are sized at launch and don't currently support mid-flight top-ups.
* **One-Time Actions**: Your dashboard at [boost.xyz](https://boost.xyz) shows claims, budget used, average cost per claim, and recent activity.
* **Time-Based Incentives**: Your campaigns appear on [rabbithole.gg](https://rabbithole.gg) with TVL, participants, and distributed rewards visible alongside the user-facing view.
Anything unclaimed at the end of the campaign's claim window is recoverable by the funding team.
## Still Have Questions?
[support@boost.xyz](mailto:support@boost.xyz)
Community support and faster questions
# Joining the Boost Community
Source: https://docs.boost.xyz/v2/documentation/support/joining-boost-community
Where to find the Boost team, ask questions, and connect with other protocols running campaigns
Whether you're scoping a campaign, troubleshooting a deploy, or just exploring what Boost can do, here's where to find help.
## Channels
**[support@boost.xyz](mailto:support@boost.xyz)**
Best for partnership inquiries, Time-Based Incentives onboarding, and anything that needs a longer-form conversation.
**discord.gg/gdu3EpeqsD**
Best for quick questions, token whitelist requests, and chatting with other teams running campaigns.
**@boost\_xyz**
Product announcements, campaign highlights, and what's shipping next.
**boost.xyz**
The marketing site, with case studies and the latest on what Boost is building.
## What to Ask Where
| Topic | Best channel |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| Launching a Time-Based Incentives campaign | [support@boost.xyz](mailto:support@boost.xyz) |
| Adding a token to the One-Time Actions whitelist | [Discord](https://discord.gg/gdu3EpeqsD) |
| Action review feedback or rejection | [Discord](https://discord.gg/gdu3EpeqsD) |
| Bug reports or platform issues | [Discord](https://discord.gg/gdu3EpeqsD) or [support@boost.xyz](mailto:support@boost.xyz) |
| Press, partnerships, business development | [support@boost.xyz](mailto:support@boost.xyz) |
When asking about a specific campaign or action, include the campaign ID, chain, and any relevant transaction hashes — it speeds up triage a lot.
## For Users
End users participating in campaigns should head to [rabbithole.gg](https://rabbithole.gg) — that's the consumer app where rewards are discovered and claimed. Rabbithole has its own support channels for user-side questions.