Creating Incentives
Step-by-step guide to creating any type of incentive
Every incentive follows the same pattern: set up your data source → generate a query → preview results → create the incentive. The setup step varies by action type.
Choose Your Action Type
| Action | What It Tracks | Setup Required |
|---|---|---|
| Swap | Token trades on DEXs (Jupiter, Raydium, etc.) | None — already indexed |
| Bonding Curve | Trades on launchpads (Raydium Launchlab, Metaplex Genesis) | None — already indexed |
| Hold | Token balance at evaluation time | None — already indexed |
| Custom Event | Off-chain activity (signups, social actions, purchases) | Event schema + data ingestion |
| IDL Instruction | On-chain Solana program interactions | IDL upload |
| Dune Query | Activity tracked by a saved Dune Analytics query | Dune MCP + query registration |
Action-Measure Compatibility
Not all measures work with all actions:
| Action | volume | count | duration | balance |
|---|---|---|---|---|
| Swap | yes | yes | — | — |
| Bonding Curve | yes | yes | — | — |
| Hold | — | yes | yes | yes |
| Custom Event | via valueExpression | yes | — | — |
| IDL Instruction | via valueExpression | yes | — | — |
| Dune Query | via valueExpression or rawQuery | yes | yes | yes |
Token Incentives (Zero Setup)
Token incentives reward on-chain token activity using already-indexed data. No setup required.
Generate the query
Use generate_incentive_query with the appropriate source:
Swap — track DEX trades:
source: "swap"
tokenMint: "your-token-mint"
direction: "buy"
measure: "volume"Bonding Curve — track launchpad trades. Each token launched on a launchpad has a unique identifier that locates its bonding curve on-chain: a pool state address on Raydium LaunchLab, or a genesis account address on Metaplex Genesis. Ask the user for the relevant identifier (they can get it from the launchpad UI) — no separate token mint needed; the pool/genesis account already uniquely identifies that token's curve.
For Raydium LaunchLab, ask the user for the pool state address:
source: "bonding_curve"
launchpad: "raydium_launchlab"
poolState: "pool-state-address"
direction: "buy"
measure: "volume"For Metaplex Genesis, ask the user for the genesis account address:
source: "bonding_curve"
launchpad: "metaplex_genesis"
genesisAccount: "genesis-account-address"
direction: "buy"
measure: "volume"Hold — track token holders:
source: "hold"
tokenMint: "your-token-mint"
measure: "balance"
minBalance: 1000Preview results
Use preview_incentive_query to validate against real data:
sqlQuery: "<the generated SQL>"Create the incentive
Use create_recurring_incentive with the query and reward configuration:
name: "Weekly Swap Leaderboard"
type: "leaderboard"
emissionType: "TOKENS"
tokenAddress: "your-token-mint"
tokenDecimals: 9
totalFundAmount: 1000
interval: "WEEKLY"
startDate: "2026-01-01T00:00:00Z"
sqlQuery: "<the generated SQL>"
customFormula: "RANK == 1 ? 500 : RANK <= 3 ? 200 : RANK <= 10 ? 50 : 0"Always call with confirmed: false first to preview the 5% protocol fee (added on top of totalFundAmount — the creator pays totalFundAmount × 1.05) and the 7-day claim window.
For leaderboards, always ask the user how they want rewards distributed and build customFormula from
their intent. The fallback N pays each user their raw metric value — rarely intended.
Token Incentive Examples
| Recipe | Source | Measure | Direction | Type |
|---|---|---|---|---|
| Top Buyers by Volume | swap | volume | buy | leaderboard |
| Hold Rewards | hold | balance | — | leaderboard or rebate |
| Bonding Curve Early Buyers | bonding_curve | volume | buy | leaderboard |
| Swap Raffle | swap | count | buy | raffle |
Custom Event Incentives
Custom event incentives track off-chain activity — social actions, content creation, purchases, game events.
Create the event schema
Use create_custom_event:
eventName: "content_share"
name: "Content Share"
fields: [
{ fieldName: "platform", type: "string" },
{ fieldName: "reach", type: "number" },
{ fieldName: "verified", type: "boolean" }
]Attach to your project
Schemas are project-independent. Use attach_custom_event to make the schema available on the active project:
customEventId: "<the event ID from step 1>"Send event data
Use the ingestion API with your API key (see API Keys):
curl -X POST https://ingest.torque.so/events \
-H "Content-Type: application/json" \
-H "x-api-key: your-api-key" \
-d '{
"userPubkey": "wallet-address",
"timestamp": 1234567890000,
"eventName": "content_share",
"data": {
"platform": "twitter",
"reach": 1500,
"verified": true
}
}'Generate, preview, and create
Generate the query:
source: "custom_event"
customEventId: "<the event ID>"
valueExpression: "SUM(reach)"
filters: ["verified = true"]Preview with preview_incentive_query, then create with create_recurring_incentive (include customEventId in the creation call).
Events must be ingested at least once before they become query-ready. Use list_custom_events with scope: "project" to check which events are ready.
IDL Instruction Incentives
IDL instruction incentives reward users for interacting with specific instructions in your Solana program.
Preview the IDL
Call create_idl with confirmed: false on the IDL file — this parses locally and lists available instructions, fields, and accounts. No auth or active project required in this phase.
filePath: "/path/to/your/idl.json"
confirmed: falseUpload the IDL
Call create_idl again with confirmed: true and the instructions you want to track:
filePath: "/path/to/your/idl.json"
confirmed: true
displayName: "My Program"
selectedInstructions: [
{
instructionName: "buy_exact_in",
fields: [
{ fieldName: "amount_in", type: "number" },
{ fieldName: "min_amount_out", type: "number" }
],
accounts: ["payer", "pool"]
}
]Generate, preview, and create
Generate the query:
source: "idl_instruction"
instructionId: "<the instruction ID from step 2>"
valueExpression: "SUM(amount_in)"Preview with preview_incentive_query, then create with create_recurring_incentive (include instructionId in the creation call).
Use list_idls to see uploaded IDLs and their tracked instructions. Use create_instruction to add a
single instruction to an already-uploaded IDL without re-uploading the whole IDL.
Dune Query Incentives
Dune query incentives reward users based on activity tracked by a saved Dune Analytics query. Use this when the data you want to incentivize is easier to express in Dune SQL than through an IDL or custom event pipeline.
Requires the Dune MCP installed alongside the Torque MCP. The Dune query must
be saved (non-temp), return exactly one day of results per run, and be scoped by a single string
parameter cast to DATE.
Register the Dune event source
Use register_dune_event_source with the column mapping:
duneQueryId: 1234567
queryName: "Daily Active Traders"
fields: [
{ duneColumn: "volume_usd", fieldName: "volume_usd", type: "number" },
{ duneColumn: "trade_count", fieldName: "trade_count", type: "number" }
]
userPubkeyField: "wallet_address"
eventTypeField: "trade_type"
timestampField: "trade_time"
dateParamName: "date"
txHashField: "tx_hash"
usdValueField: "volume_usd"Call once with confirmed: false to preview, then again with confirmed: true. The tool returns a duneEventQueryId — save it for the generate-preview-create step below.
txHashField and usdValueField are nullable but strongly recommended. If you omit either (e.g. for an aggregated query with no per-row tx hash), you must also pass acknowledgeIncompleteConfig: true on the confirmed: true call — the tool will refuse to proceed otherwise. Incomplete configs make incentive query building significantly harder, so fill these in whenever your Dune query exposes them.
Validate via the Dune MCP
Through the Dune MCP: executeQueryById with a recent date for dateParamName, then getExecutionResults. Confirm the execution succeeds and returns rows with the expected columns. If it errors or returns no rows, fix the Dune query (via the Dune MCP) before proceeding.
Wait for the first daily ingestion
Torque ingests Dune query results on a daily schedule. generate_incentive_query rejects Dune sources until their column mapping is populated by the first ingestion run — expect up to 24 hours.
Generate, preview, and create
Generate the query:
source: "dune_query"
duneEventQueryId: "<the UUID from step 1>"
valueExpression: "SUM(volume_usd)"Note: pass the Torque-side duneEventQueryId UUID — NOT the numeric Dune duneQueryId.
Preview with preview_incentive_query, then create with create_recurring_incentive.
Daily ingestion means results lag by at most one day. Schedule your incentive intervals (e.g. WEEKLY) to
align with this cadence.
Reward Types
Once you have a query, choose how rewards are distributed:
| Type | How It Works | Key Params |
|---|---|---|
| Leaderboard | Rank users by metric, distribute via formula | customFormula (ask the user!) |
| Rebate | Return a % of each user's metric value | rebatePercentage |
| Raffle | Random draw from qualifying users | raffleBuckets, raffleWeighting |
| Direct | Fixed amounts to specific wallets | allocations |
See Incentive Types for full details on each type, formulas, and examples.