---
title: "Incentivization"
description: "The four on-chain levers that pay BTR liquidity: fee split, donations, POL, hook yield"
audience: tech
type: reference
status: live
lang: en
updated: "2026-08-30"
publish: true
---
# Incentivization

An LP's claim on a leg is `balance × liquidityIndexWad`, so every on-chain way of paying BTR liquidity reduces to raising that index. Four levers do it, and nothing else on chain does. Everything beyond them (points, third-party reward campaigns, venue reward tokens) is off-chain distribution the protocol carries no accounting for (§6).

Every lever is per pool and per chain.

---

## 1. The levers

| Lever | Call | Who | Effect |
|---|---|---|---|
| Fee split | `requestOp(UPDATE_FEES)` → `executeUpdateFeeParams` | AC owner, LOW tier | Lower `protoSharePct` → larger LP slice of every swap and flash spread |
| Direct subsidy | `Pool.donate(token, amount)` | Anyone | Raises one leg's index, one-shot |
| POL | `Pool.deposit(token, amount)` | Anyone | Adds depth; earns fees, pays no one else |
| Hook yield | `YieldHook.rebalance()` → `Pool.hookCreditYield` | Keeper or owner | Venue yield on the invested slice lands on the index |

---

## 2. Fee split

`protoSharePct` is the percentage of the swap and flash spread routed to the pool's fee sink; the remainder raises the LP index. Set in `initdata` at `createPool`, changed afterwards through the queue:

```solidity
IPool.FeeParams memory p = IPool.FeeParams({protoSharePct: 0, flashFeePbps: 5});
admin.requestOp(pool, uint8(IPool.OpType.UPDATE_FEES), bytes32(0), abi.encode(p));
// … LOW tier delay, 1 hour under PROD_DELAYS …
admin.executeUpdateFeeParams(pool);   // emits FeeParamsUpdated
```

`protoSharePct = 0` is a valid, permanent "all fees to LPs" launch stance and costs nothing on the hot path: the fee split returns the whole fee as `lpFee`. It needs no token, no distributor and no claim.

Range is `[0, 100]`; anything above reverts `InvalidInput`. `UPDATE_FEES` is pool-wide, so its `subject` is `bytes32(0)`. The receiving side is [Protocol Fee Collection](/docs/5-3-2-protocol-fee-collection).

---

## 3. Donations

```solidity
function donate(address token, uint256 amount) external payable;
```

Pulls `amount`, adds it to both `reserves` and `liabilities`, and raises that leg's index. Emits `Donated(sender, token, amount)`. Coverage is unchanged: this is a gift to existing holders of the leg, not new depth.

Two hard rules:

1. **Donating into an unopened leg strands the gift permanently.** With `liabilities == 0` there is no share to raise; the surplus above the dead seed backs nothing and is inherited by nobody, ever. `deposit` first, then `donate`.
2. **It is instantaneous and un-vested.** A donation lands on whoever holds the leg's `LPToken` in that block, including someone who deposited one block earlier. Only the flow cooldown stands between a donation and a JIT deposit. Size and time donations accordingly, or pay through a hook instead.

---

## 4. Protocol-owned liquidity

POL is a plain `deposit`. It earns the LP slice like any other position and deepens the book, tightening quoted spread for everyone; that is its incentive value. It pays no one else directly.

`LPToken` is a standard ERC-20 whose minted quantity is frozen for `Pool.flowCooldownSecs()` (default 15 s, maximum 300). Transfers of unfrozen balance are unrestricted, which is what makes the receipt usable as a campaign measurement basis (§6). Mechanics: [Flow Guards](/docs/3-3-flow-guards).

---

## 5. Hook yield

A `YieldHook` is the only lever here that pays LPs continuously. Full mechanics, buffer sizing and the dual ledger: [Pool Hooks](/docs/5-1-3-hooks). The incentive-relevant facts:

- `rebalance()` (keeper or AC owner) harvests venue NAV and credits the gain via `Pool.hookCreditYield`, raising the index for every holder of that leg.
- The credit is capped as a **rate**, not a per-call allowance: at most `maxHarvestCreditBps` of book per day, default 100 BPS/day, hard ceiling 500. A campaign that dumps a large one-shot gain into the venue is credited over days, not in one block. `0` disables crediting entirely. Details: [Hooks §5.2](/docs/5-1-3-hooks#52-harvest-and-write-downs).
- **Venue reward tokens do not reach LPs.** `claimVenueIncentives` pulls them onto the hook and `sweepIncentives` pushes them to `pool.treasury()` (or `incentivesReceiver`). Adapters never swap rewards. Routing that value back to LPs is a treasury decision, executed as a `donate` (§3).

Installing a hook is `UPDATE_HOOK` at the HIGH tier, 3 days.

---

## 6. Third-party campaigns

The protocol exposes no reward-distribution surface (no gauge, no `notifyRewardAmount`, no staking wrapper, no referral fee-share), so a campaign operator paying BTR LPs runs it entirely outside the protocol:

1. **Measure.** Snapshot `LPToken.balanceOf` per holder for the leg: one EIP-1167 clone per `(pool, asset)`, address from `Pool.lpToken(token)`, ABI as `LP_TOKEN_ABI` in `@btr-protocol/sdk/abis`. Balances are already index-adjusted claims, so no unit conversion is needed. Snapshot each chain separately.
2. **Distribute.** The operator's own Merkl campaign or distributor. BTR ships neither and takes no custody.

The one in-protocol alternative is to convert the campaign budget into a `donate` on the incentivized leg. Trustless and needs no claim UI, but untargeted: it pays every current holder pro rata, including passive ones.

---

## 7. End to end

Launch stance for an incentivized leg: all fees to LPs, a hook for baseline yield, and a periodic treasury donation funded by swept venue rewards.

```typescript
import { encodeAbiParameters } from 'viem';
import { Contract } from '@btr-protocol/sdk/eth';
import { ADMIN_ABI, POOL_ABI } from '@btr-protocol/sdk/abis';

const admin = new Contract({ address: adminAddress, abi: ADMIN_ABI, provider, account: owner });
const pool  = new Contract({ address: poolAddress,  abi: POOL_ABI,  provider, account: treasury });

// OpType.UPDATE_FEES = 8 in IPool.OpType. ZERO32 = 32 zero bytes: UPDATE_FEES is
// pool-wide, so its subject is ignored.
const UPDATE_FEES = 8;
const ZERO32 = `0x${'00'.repeat(32)}`;
const feeParams = encodeAbiParameters(
  [{ type: 'tuple', components: [{ type: 'uint8' }, { type: 'uint16' }] }],
  [[0, 5]],                                   // protoSharePct = 0, flashFeePbps = 5
);

// 1. All swap fees to LPs (LOW tier, 1 hour).
await admin.write('requestOp', [poolAddress, UPDATE_FEES, ZERO32, feeParams]);
// … after the delay …
await admin.write('executeUpdateFeeParams', [poolAddress]);

// 2. Seed the leg BEFORE anything is donated into it (§3, rule 1).
await pool.write('deposit', [token, seedAmount]);

// 3. Install the hook: UPDATE_HOOK, HIGH tier, 3 days. Thereafter the keeper's
//    rebalance() credits venue yield to the index automatically.

// 4. Recycle swept venue rewards from the treasury as a donation.
await pool.write('donate', [token, sweptBudget]);
```

Order matters: step 2 before any `donate`, and the hook's `HOOK_PRE_OUTFLOW` flag must be set before capital can be deployed at all.

---

## 8. Boundaries

| Action | Authority | Timing |
|---|---|---|
| `donate` | anyone | immediate |
| `deposit` (POL) | anyone | immediate; `flowCooldownSecs` transfer lock |
| `UPDATE_FEES` | AC owner | LOW, 1 hour |
| `UPDATE_HOOK` | AC owner | HIGH, 3 days |
| `rebalance`, `claimVenueIncentives`, `sweepIncentives` | keeper or owner | immediate |
| `setIncentivesReceiver` | `treasuryOwner` | immediate |
| `collectProtocolFees` | `pool.treasury()`, paying itself | immediate |

No incentive lever can move reserves out of a pool. `donate` and `deposit` only add, `hookCreditYield` only raises the index, and the sweep moves reward tokens that were never pool reserves.

---

## 9. Related

- [Pool Hooks](/docs/5-1-3-hooks) · [Protocol Fee Collection](/docs/5-3-2-protocol-fee-collection)
- [Pool Deployment & Curation](/docs/5-1-2-pool-deployment-curation): `FeeParams` at `createPool`, timelock tiers
- [Spread & Fees](/docs/1-1-4-spread-fees) · [Basic Operations](/docs/5-1-1-basic-operations)
