Create Your Campaign from a Smart Contract

Automate recurring Merkl campaigns by deploying a middleman contract that hardcodes the parameters and only takes the amount each cycle

If you run recurring campaigns — weekly liquidity incentives, biweekly emissions, monthly rewards — you don't want a human (or a multisig) clicking through Merkl Studio every cycle. The clean institutional pattern is to deploy a middleman smart contract that hardcodes every campaign parameter on deployment and exposes a single function to launch the next campaign with an amount.

From then on, your weekly operation is reduced to two steps:

  1. Transfer the reward tokens to the middleman.
  2. Have a bot call the middleman with the amount.

The middleman handles the approval, calls Merkl's DistributionCreator, and creates the campaign with the pre-set rules. No multisig signing, no parameter drift between cycles, no human in the loop, and no risk that the wrong campaign config gets pushed.

We recommend using the StandardMiddleman contract:

📄 StandardMiddleman.sol

It's audited (alongside the rest of the Merkl contracts) and supports the full set of Merkl campaign features.

How it works

The contract holds three pieces of state:

  • defaultParams — the full CampaignParameters struct used for every campaign created through it (campaign type, target pool, duration, hooks, blacklist/whitelist, etc.).
  • executors — an allowlist of addresses authorized to trigger campaign creation (e.g. your bot's hot wallet, or your gauge contract).
  • startTimestampOffset — how far in the past the campaign should start, useful for retroactive distributions.

The runtime entrypoint is a single function:

function notifyReward(uint256 amount) external;

When called by an authorized executor, it:

  1. Verifies the caller is in executors.
  2. Approves the DistributionCreator for amount of the reward token (idempotent — handled internally).
  3. Calls DistributionCreator.createCampaign(params) with defaultParams, the supplied amount, and block.timestamp - startTimestampOffset as the start time.

That's it. The campaign launches with the exact parameters baked in at deployment.

One-time setup

Performed by the contract owner:

  1. Deploy StandardMiddleman with the Merkl DistributionCreator address.
  2. Configure the campaign template via setDefaultParameters(...) — this is where you bake in the pool, the duration, the customization hooks, and any other Merkl options for your recurring campaigns.
  3. Whitelist the bot wallet (or the calling contract) via setExecutor(...).

After this, the contract is ready. The owner can update parameters or rotate executors at any time, and recoverToken(...) is available as an emergency escape hatch.

Recurring operation

Every cycle (e.g. each Monday):

  1. Transfer the cycle's reward budget to the middleman.
  2. Bot calls notifyReward(amount).

A new Merkl campaign is created with the hardcoded rules and the amount you just funded. Nothing else changes between cycles unless the owner explicitly updates the parameters.

Use cases

Recurring institutional emissions

A foundation, treasury, or protocol DAO that pays out weekly liquidity incentives can deploy one middleman per pool (or one shared middleman with a more advanced parameter mapping) and cut the operational burden to "send tokens, run bot." Every campaign is verifiably identical to the previous one — auditable, reproducible, no human discretion required.

Onchain gauge systems

Many protocols rely on gauge systems where users vote onchain to determine reward allocations. The middleman pattern slots in naturally: the gauge calls the middleman's notifyReward(amount) function (the standard interface gauge systems already use), and the middleman creates the corresponding Merkl campaign.

This combines Merkl's flexibility (campaign types, hooks, customization options, reporting) with the trustlessness of a fully onchain emission flow.

Several teams have shipped variations of this pattern:

If your gauge system calls a different function signature than notifyReward(uint256), you'll want a thin wrapper or a forked middleman with the matching interface — the rest of the logic stays the same.

Prerequisites

You'll need the campaign parameters in the format expected by DistributionCreator.createCampaign(...) — the same CampaignParameters struct used when creating campaigns from a multisig.

The easiest way to get the right payload is to:

  1. Walk through the campaign in Merkl Studio using our campaign creation guide, or programmatically via the API guide.
  2. Copy the JSON payload Studio produces for multisig flows — it maps directly to the struct fields you'll pass to setDefaultParameters(...).

See Campaign Configuration for the full field reference.

Need help?

If your setup needs custom parameter logic (per-pool allocation, dynamic durations, multiple reward tokens), reach out to the Merkl team — we can help you adapt the template or design a fit-for-purpose middleman.