# Attested incentive allocator — specification

**Report date and source-read date: 2026-10-05 UTC.**

## Recommendation

Launch an immutable, quote-token-funded trading-reward allocator for one Uniswap v4 pool, with a small authenticated swap gateway and an observation hook that logs the gateway's actual calling account. Buy one chain-evidence ranking and one independently bound sum for each winner per epoch; store the signed list on chain and pay capped pro-rata rewards through permissionless claims to those exact accounts. Start with ten winners and nominal weekly epochs. This removes the distributor's discretion over recipients, amounts and question selection, but still trusts the oracle service key and its re-execution process; it does not prove that wallets represent distinct people or that trades are organic. Use three new contracts, or four including an optional launch token. Refused or incomplete attestations release no rewards. Economic protection is conditional on an independently defensible lower bound on unrecoverable trading costs; absent that bound, use a zero reward rate rather than claiming a positive budget is wash-proof.

## Decisions table

| Question | Choice | Figure | Main reason |
|---|---|---|---|
| 1. Attribution | Hook event keyed by caller of immutable gateway | `ParticipantVolume(poolId, participant, quoteVolume)`; two hook flags | Authentic account authorization; arbitrary router labels are spoofable |
| 2. Payout | Stored top-ten list plus sums; capped pro rata | 11 requests: **5.5 IMD/epoch**; approximately $46.75 only at the brief's assumed exchange value | Order alone cannot establish reward-per-volume bounds |
| 3. Washing/sybils | Same quote denomination for volume and rewards; conservative per-volume cap | Illustrative 3000 fee tier, 50% fee recapture: choose 7.5 bp rate; $750 maximum on $1m selected volume | Fees paid to attacker-controlled LPs are not an attacker expense |
| 4. Timing | Fixed contiguous block windows, sequential closure | 50,400 blocks; nominal seven days at 12 seconds/block; seven-day response grace | Avoid discretionary endpoint selection and partial payout |
| 5. Comparators | Separate distribution integrity from retention and identity | 2025 Uniswap proposal: $24m v4 and $21m initial Unichain requests; 2024/25 LTIPP evaluation: 95.95m ARB claimed across three programmes | Reproducibility does not turn subsidized activity into durable demand |

**Evidence legend.** FACT means directly supported by a cited primary document or a pinned input read in this assignment. DESIGN means a proposed rule, not observed production behavior. INFERENCE means reasoning from those facts. UNKNOWN means evidence was unavailable or insufficient. Every IdentityMD-plane assertion below is explicitly an **ASSUMPTION supplied by the brief**, unless identified as a directly inspected pinned-test fact. No live oracle request, signature, deployed consumer, price feed or mainnet transaction was independently exercised. Source pages were read on the date above; historical dates are identified separately. Comparator amounts are not live prices or audited effectiveness estimates.

## 1. Attribution: what the ranking reads

FACT: PoolManager `Swap` and `ModifyLiquidity` identify the account calling the manager, not necessarily the beneficial trader or LP. Ranking this field can reward a router or position manager. The ABI documents the sender fields and signed swap deltas. [Uniswap PoolManager interface](https://raw.githubusercontent.com/Uniswap/v4-core/main/src/interfaces/IPoolManager.sol)

| Candidate | Honest meaning | Spoofing/measurement failure | Decision |
|---|---|---|---|
| PoolManager event sender | Immediate manager caller | Common routers aggregate many users | Reject as participant key |
| Any router's `msgSender()` | Whatever that router reports | A malicious router can implement the same selector and return anyone | Accept only reviewed immutable integrations |
| Address in `hookData` | Caller-supplied label | Anyone can name a victim, distributor or fresh sybil | Never authenticate this alone |
| ERC-20 Transfer recipient | Token recipient | Transfers, self-transfers, routers, fee settlements and gifts are not unique pool trades | Reject as trade metric |
| Position NFT owner | Owner at a particular moment | Transfers, operators, mint timing and tokenId-to-position linkage matter | Suitable only for a separate carefully defined LP scheme |
| Immutable gateway caller | Account authorizing this gateway execution | Contract accounts and sybils remain possible | Selected trading metric |

FACT: Universal Router's `Lock` exposes `msgSender()` using its active locker. This is a useful convention, not an authentication standard applicable to every contract. [Universal Router Lock source](https://raw.githubusercontent.com/Uniswap/universal-router/main/contracts/base/Lock.sol)

DESIGN: Gateway `swap` accepts no participant argument. It locks against reentry, records `msg.sender`, calls PoolManager through its unlock callback, settles the trade and clears the account before returning. Hook `afterSwap` requires PoolManager as its own caller. If the hook's `sender` is the gateway, it reads the gateway's active account and emits exactly one event for a nonzero quote-side delta. If the sender is another router, the pool trade still succeeds but earns no reward and emits no qualifying event. Unknown integrations therefore cannot forge rewarded identities or halt ordinary routing. `tx.origin` is never used. A Safe or other account contract earns as that contract; an aggregator earns as the aggregator unless a future separately reviewed integration preserves authenticated user authorization.

```solidity
event ParticipantVolume(
    bytes32 indexed poolId,
    address indexed participant,
    uint256 quoteVolume
);
```

`quoteVolume` is the absolute executed quote-currency balance delta, in the quote token's native integer units, obtained from the callback's final swap delta. It is not the requested input, a dollar estimate or the sum of both token legs. Both directions count the quote leg once. The specification excludes zero-volume events and any other pool. The gateway permits one swap per invocation, exact-input or exact-output with bounded input/output and a deadline; partial execution counts the actual result. Failed settlement reverts the entire transaction and its logs. No extra accounting delta or hook fee is introduced. Quote delta may include input fee effects; the economic lower bound must use this exact measured-volume denominator rather than an approximate gross-volume dashboard definition.

**ASSUMPTION (IdentityMD):** `log-rank` can group this event by `participant`, sum `quoteVolume`, apply `topN`, and return an ordered `address[]` without sums. Each subsequent `log-sum` filters the same pool and one participant over the exact same inclusive block window. Required rank semantics are descending aggregate sum, then ascending numeric address for ties, exclusion of zero totals, and fewer than N results when fewer participants exist. These are proposed definitions; actual tie and filter support require developer confirmation. Refusal is preferable to a service silently implementing different semantics.

LP incentives are deliberately a different metric. FACT: PositionManager contains NFT position operations, liquidity modifications and ownership checks; its internal call sequencing must be examined before attempting to recover owners in callbacks. [PositionManager source](https://raw.githubusercontent.com/Uniswap/v4-periphery/main/src/PositionManager.sol)

INFERENCE: summing liquidity additions rewards repeated deposit/withdrawal rather than sustained useful liquidity. An honest LP product needs position identity, historical ownership including transfers, in-range liquidity and time integration. An owner lookup at epoch close retroactively assigns earlier activity to buyers. A hook cannot assume every position manager exposes a callback-safe tokenId or that a minted NFT already exists when the callback runs. The supported recipe list supplied here does not establish an exact ownership-aware liquidity-time integral. Do not relabel deposit sums as liquidity mining; ship trading rewards first and commission an LP metric separately.

The hook **is one of the contract count**, using `BaseHook` behavior with **beforeInitialize and afterSwap only**, mask `0x2040` (8256). All other twelve flags, including every ReturnDelta flag, are false. FACT: v4 encodes permissions in the low fourteen address bits. [Hooks library](https://raw.githubusercontent.com/Uniswap/v4-core/main/src/libraries/Hooks.sol) The pinned Hook protected test independently requires an initialization callback, matching flags, restricted callers, runtime size at most 24,576 bytes and no executable SELFDESTRUCT, DELEGATECALL or CALLCODE. It does not run a pool lifecycle; that omission must never be reported as a lifecycle pass.

## 2. Payout shape and cost

**ASSUMPTION (IdentityMD):** a request costs 0.5 IMD, approximately $4.25 at the brief's reference value, and rank results contain order but no amounts. USD conversions below use that assumed value only; no current IMD market price was verified.

| Shape | Requests for N winners | Payout rule | Principal limitation |
|---|---:|---|---|
| Fixed rank tiers | 1 | N=10: 30%, 20%, 15%, 10%, 7%, 5%, 4%, 3%, 3%, 3% = 100% | Economically unsafe without volume floors; compare only |
| Pro rata among ranked winners | N+1 | `B * V_i / sum(V_j)` | Extra cost, partial-answer liveness and fee recapture |
| Selected capped pro rata | N+1 | `min(B * V_i / S, r * V_i)` | Can leave much of budget undistributed; still has a top-N boundary |

Tiers for N=50/100 would need separate immutable vectors. If fewer winners exist, selected pro rata uses those actual winners; fixed tiers should leave missing positions unspent rather than renormalizing upward. Avoid winner-takes-all lottery semantics.

| topN | Rank tiers | Rank plus N sums | Assumed USD tiers / pro rata |
|---:|---:|---:|---:|
| 10 | 0.5 IMD | 5.5 IMD | $4.25 / $46.75 |
| 50 | 0.5 IMD | 25.5 IMD | $4.25 / $216.75 |
| 100 | 0.5 IMD | 50.5 IMD | $4.25 / $429.25 |

Those are successful first-attempt request costs, excluding gas, operator work, funding and any retry charges. For k<N actual winners the selected design needs k+1 requests. An aggregate sum over all participants would add another 0.5 IMD and change the denominator; it is not silently included here. 

DESIGN: Store `ranked[epoch]`, verified volume per index, a received-sums bitmap/count and claim status. Let `S=sum(V_i)` with every V positive. Use full-precision multiplication/division and round both bounds down. Rate is a rational integer `ratePpm/1_000_000`, expressed in quote reward units per quote-volume unit. Finalize only after all actual winners' sums arrive; freeze each entitlement then. Empty ranking finalizes with zero rewards. Sum of claims is at most B and at most the sum of individual caps. Rounding dust and unused ceiling remain unallocated.

| Commitment | Verification required | Scale | Trust |
|---|---|---|---|
| Stored list, selected | Check signature, canonical answer bytes and per-winner sums | O(N) writes; claims O(1) | No discretionary distributor |
| Merkle root derived on chain | Same checks, then deterministic leaf/root construction | O(N) input/hash work; O(log N) proofs | Equivalent integrity if all leaves are derived |
| Root submitted off chain | Proof that root equals full signed list and amounts | Cheap root storage but proof machinery required | Without that proof restores distributor discretion |

A signed list does not sign an arbitrary distributor root. Any root variant needs leaves binding consumer chain, allocator, epoch, index, account and amount, plus unambiguous tree ordering and odd-node rules. For ten winners, storage is simpler. Publish the verified list in an event; membership also remains claim-accessible in storage. **ASSUMPTION (IdentityMD):** job delivery has no consumer-verifiable signature and cannot replace an oracle attestation.

## 3. Wash trading, sybils and budget

Define W as counted quote volume over all wash legs, f_eff as fees per measured quote volume irreversibly paid outside the attacker, I(W) as net price-impact/arbitrage loss, and G as gas/financing cost. For a round trip, both legs enter W, so do not multiply fW by two again. A simple attacker-profit model is:

`profit(W) = incremental_rewards(W) - f_eff * W - I(W) - G`.

The requested no-wash condition is `incremental_rewards(W)/W < f_eff + I(W)/W + G/W`. For an unconditional per-unit sufficient bound, use conservative lower bounds, not optimistic expected impact. With attacker LP share alpha and fee rebates d, an approximate model is `f_eff = f*(1-alpha)-d`; the implementation's denominator and trade direction require refining this approximation. Small repeated round trips can make net impact approach zero. A concentrated-liquidity attacker can capture most fees. Thus the advertised pool fee alone is not an expense floor.

DESIGN: Select B ceiling and r before activity starts; spend `B_spend <= min(B, r*S)`, with each entitlement `min(floor(B*V_i/S), floor(ratePpm*V_i/1e6))`. Same quote token for rewards and volume avoids needing a manipulable token-price oracle. Paying launch tokens instead requires an independently bounded conversion to quote value; a thin-pool spot is unsuitable. Assets are quote reserves; liabilities are unpaid entitlements. Sponsors bear emissions costs and unused locked funding. 

| Illustrative case, not observed pool data | Conservative cost floor | Suggested r | Budget consequence |
|---|---:|---:|---|
| 3000 tier, no recapture/rebate; zero impact floor | About 30 bp | 15 bp | $1,500 per $1m selected volume |
| 3000 tier, alpha at most 50%; zero impact floor | About 15 bp | 7.5 bp | $750 per $1m selected volume |
| 500 tier, no recapture/rebate | About 5 bp | 2.5 bp | $250 per $1m selected volume |
| Unbounded LP recapture or external rebate | Zero can be possible | Zero | No positive provably wash-proof spend |

DESIGN example: B=$1,000 quote units, r=750 ppm, S=$1m gives at most $750; S=$100,000 gives at most $75; S=$2m permits the full $1,000. Examples assume a dollar quote. In code they are raw quote units, with decimals accounted for. Actual fee-denominator effects need a safety margin in the reviewed r. Oracle expense is additional: the top-ten assumed $46.75 service cost is substantial beside a $75 payout. 

INFERENCE: splitting a fixed volume does not increase its sum. It can nevertheless change selected-set membership and redistribute pro-rata rewards; top-N is not mathematically sybil-neutral. Fee recapture, per-address floors and rank tiers make the issue worse. The selected design has no equal-wallet bonus, new-user multiplier or per-wallet cap that invites splitting. Its aggregate cap limits rewards to r times selected volume, but it does not prove negative marginal profit near the cutoff. A wallet with pre-existing organic volume can wash a small amount to enter top N and unlock rewards for that earlier volume. Avoid claiming the total-volume bound proves the incremental condition.

Therefore positive rates are a monitored, conditional incentive experiment, not a universal prevention theorem. Publish rank-boundary sensitivity, organic retention and fee-recipient concentration, and stop funding future epochs if assumptions fail. Changing rules requires a new deployment; nobody can blacklist signed winners midway. A strict anti-wash requirement would need a different mechanism, such as rewarding all authenticated participants at a linear fee-linked rate, or an additional verified cost metric. Neither is delivered by a ranking-only answer. Human identity or graph-based exclusions introduce separate evidence and appeal requirements; they must not be hidden in `chain` rankings.

## 4. Epochs, requests and attestation acceptance

DESIGN: This deployment observes its local Sepolia pool. Data chain equals consumer chain for the first version; cross-chain observations are explicitly unsupported rather than implicitly checked against the wrong chain. Define H as hook initialization block and L=50,400. Epoch e has inclusive bounds `[H+1+eL, H+(e+1)L]`. These are block epochs, nominal weekly at twelve seconds/block, not guaranteed seven-day UTC intervals. Initialization cannot occur twice. `beforeInitialize` validates immutable currencies, hook address, static fee 500/3000/10000 and tick spacing, then records H; a reverted initialization rolls that write back. No activity in block H is rewarded.

Anyone may fund an epoch before its starting block, up to immutable B. Anyone may prepare its fixed window after closing and a 128-block settlement delay. The delay is a chosen buffer, not a proof of consensus finality. Confirm finalized status before buying requests. Windows close sequentially; either finalize or expire the pending epoch before accepting the next ranking. Sums belong to the pending rank window and therefore reuse its closing block rather than advancing separately. A refusal must not permanently block later epochs: after seven days from preparation, anyone can expire it, advance the processed cursor and release its reservation. Existing finalized claims remain valid indefinitely.

**ASSUMPTION (IdentityMD):** answers take minutes; mainnet IMD funds requests, launches currently target Sepolia, schedules cost 0.5 IMD per run, recurring oracle requests are at least ten minutes apart, and recurring jobs are at least thirty minutes apart. These operational properties were supplied, not verified. A sponsor buys the rank after the window is finalized, then sums after winners are known. Other users may buy exactly equivalent questions with the allocator as consumer and relay valid answers. No mainnet IMD transfer is attempted by the Sepolia allocator and there is no invented bridge or keeper reimbursement. Off-chain request provisioning occurs after launch; the contracts are already fully linked without it.

**ASSUMPTION (IdentityMD):** service signer is `0x5598aa9146215bc13eb26f2c692ad1461fd32982`; EIP-712 domain is name `IdentityMD Oracle`, version `2`, consumer chainId and allocator verifyingContract. Signed fields are requestId, chainId, questionHash, answerType, answer, figure, fromBlock, toBlock, blockHash, panelJobId, panelSize, quorum, agreed, issuedAt, expiresAt. Their Solidity types, primary struct name, answer encoding and chainId semantics have not been established by a signed fixture. An interface envelope below is a consumer adapter design, not a claim about the existing wire ABI.

DESIGN: pin signer in source, with no setter, owner, rotation or upgrade. Reject wrong signer, malleable/high-s signatures and malformed bytes. Verify domain using `block.chainid` and `address(this)`, then require the signed data chain field equals the deployment's data chain. Check exact answer type and canonical decoding, `figure` consistency for sum answers, window equality, expiry, issuedAt not in the future, issuance after window closing, maximum permitted signature age of 48 hours, nonzero closing block hash and matching hash across rank and sums. Require unique requestIds for accepted answers. Do not equate a closing block hash carried in a signature with independent on-chain finality: local `blockhash` only covers a limited recent range, and remote hashes are unavailable. **ASSUMPTION (IdentityMD):** old-window re-execution/finality relies on the service.

**ASSUMPTION (IdentityMD):** `agreed` is signed and live answers have agreed=quorum. DESIGN floors are panelSize>=5 and agreed>=3, subject to developer confirmation that these counts can be bought. Also check `agreed<=panelSize`, `quorum<=panelSize`, and `agreed>=quorum`. Never enforce an agreement ratio. A one-signature service is still one cryptographic trust boundary, even when five agents participated. **ASSUMPTION (IdentityMD):** supported chain recipes are log-sum, log-count, univ4-spot, call-compare, log-rank and v4-volume-rank; other evidence is panel-voted and not re-run. This allocator accepts only its pinned chain-evidence documents; a voted price, identity judgement or arbitrary ranking is ineligible.

**ASSUMPTION (IdentityMD):** questionHash hashes the sorted-key canonical document `{v, question, chainId, window, answerType, head?, definitions?, evidence?}`, including window and evidence class. DESIGN: build exact canonical bytes in a reviewed serializer with immutable templates and replace only decimal fromBlock/toBlock, and the sum template's fixed-format address selected from the already verified list. Pin version, hook address, event signature, poolId, grouping, sum argument, order/tie rules, topN, units and zero handling inside question/definitions. **ASSUMPTION (IdentityMD):** window object keys and recipe nesting remain unverified. Never hash an ad hoc `abi.encode` surrogate and never let the relayer supply definitions. Store golden documents and hashes once the developer provides the real schema. EIP-712 hashes dynamic fields according to their declared types; it does not provide application replay protection automatically. [EIP-712 specification](https://eips.ethereum.org/EIPS/eip-712)

A refused rank creates no claim. A rank with one missing sum creates no claim for anyone; retry within grace using the same question/window. **ASSUMPTION (IdentityMD):** retry billing/refunds are unknown. Expired reservations become unallocated reserves, eligible for a later fixed-ceiling epoch through a permissionless allocation function; they never create an enlarged jackpot or let a sponsor recover accepted liabilities. Unclaimed entitlements have no sweep path. 

## 5. Comparator evidence and failures

The freshness window is **2024-10-05 through 2026-10-05**. Figures below are from documents within that period, or currently served technical parameters. Where a programme ended earlier, a later evaluation is identified as such. Historical airdrop policy is discussed for methodology without passing its old launch figures off as recent performance. 

| System | Dated evidence and figure, read 2026-10-05 | What went wrong or remains unproven | Design consequence |
|---|---|---|---|
| Merkl | Technical documentation served at read date: approximately two-hour computation, eight-hour average root updates (4–12 hour range), 1–2 hour dispute period | Documentation acknowledges data-feed/invariant delays; engine and root publication remain separate trust surfaces. Archived docs may lag deployed settings | Store actual verified lists; do not promise immediate claims |
| Uniswap incentives | February/March 2025 governance proposal: $24m v4 six-month budget, $21m initial Unichain request, two-week tranches | These are proposed budgets/goals, not realized organic growth. Subsequent attribution research shows retention weaknesses | Separate distribution correctness from growth measurement |
| Uniswap retention | Forse's 2025 research update: 27.8% of incentivized LPs subsequently used Aerodrome; 84.5% of that subset fully left Uniswap | Cohort observation, not causal proof that incentives made LPs leave | Measure continued activity after rewards end |
| Arbitrum LTIPP and related programmes | PYOR November 2024 evaluation: 101.86m ARB proposed and 95.95m claimed across STIP, Backfund and LTIPP; 88 LTIPP grantees | Initial report explicitly lacked sufficient post-LTIPP data; December 2024 Foundation note reports fund misuse and recovery | A signed activity metric cannot prove correct grant operations or sustainable returns |
| Optimism airdrops | Airdrop 5 rules read live: >=20 distinct contracts and >=10% contracts/transactions during March–September 2024; August 2026 budget reports 10,368,678 OP circulated in Year 3 and zero in Year 4 | Eligibility disputes exist; no verified recent false-positive or sybil-leakage rate found | Publish exact windows and explain exclusions; never invent accuracy figures |
| Arbitrum airdrop filtering | Currently served historical policy: 48-hour activity concentration and low-balance/low-diversity penalties, plus Hop sybil exclusions | Original distribution is outside the comparison period; no current efficacy estimate established | Behavioral heuristics can be reproduced without proving personhood |
| Blur points | Season 4 rules, ending June 2025: 500m BLAST allocation; top-100 rolling 24-hour boosts up to 2.5x; mythical packages worth 100x uncommon | Documented user complaints about inaccessible holder views in November 2024; rules expose concentration and loyalty incentives. No verified recent wash-share figure | Avoid discretionary boosts and probabilistic payout conversion |

Merkl's technical source describes published reward files, delayed availability and claims against roots. Its 1–2 hour dispute description is a documented parameter, not a live contract read; actual chain configuration should be read before integration. [Merkl technical overview](https://raw.githubusercontent.com/AngleProtocol/merkl-docs/main/merkl-mechanisms/technical-overview.md) The open dispute bot describes halting updates when data is wrong or insufficient and DAO resolution. No recent exploit or loss amount was independently established here. [Merkl dispute reference](https://github.com/AngleProtocol/merkl-dispute)

Uniswap's budgets and tranche arrangements come from the proposer's governance document, not a third-party estimate. Treat them as that dated plan. [Uniswap Unleashed proposal](https://gov.uniswap.org/t/governance-proposal-uniswap-unleashed-unichain-and-uniswap-v4-liquidity-incentives/25250) Forse's directly authored update supplies the retention percentages; the denominator of 84.5% is the Aerodrome-moving subset, not every incentivized LP. [Forse programme update](https://gov.uniswap.org/t/temp-check-forse-analytics-for-uniswap-revitalization-and-growth-program/24373?page=2)

PYOR's programme totals are grantee claims, not verified end-user receipts. Its January 2025 follow-up announces a final analysis; this report does not claim to reproduce that linked PDF's results. [PYOR preliminary evaluation and follow-up](https://forum.arbitrum.foundation/t/pyor-arbitrum-stip-backfund-stip-and-ltipp-incentive-efficacy-analysis-preliminary-report/27496) The Foundation says it identified misuse and retrieved nearly all funds, without a loss/recovery amount in the inspected passage. [Foundation ARDC recommendations, December 2024](https://forum.arbitrum.foundation/t/arbitrum-foundations-recommendations-for-ardc-deliverables/27686/1)

Optimism's activity criteria are direct programme rules, not a published identity classifier. [Airdrop 5 criteria](https://app.optimism.io/airdrops/5) The more recent budget distinguishes circulating tokens from newly committed spending; its Year 3 airdrop figure should not be interpreted as Year 4 emissions. [Optimism August 2026 budget update](https://gov.optimism.io/t/collective-year-4-budget-update-and-year-5-budget-outlook/10796) A dated eligibility complaint is evidence of a disputed experience, not proof of an erroneous exclusion. [Optimism October 2024 discussion](https://gov.optimism.io/t/at-this-point-it-is-just-a-fact-that-op-hates-my-wallet/8996)

Arbitrum's served policy documents transaction-window and balance heuristics plus imported sybil flags. No such filter is included in this allocator. [Arbitrum eligibility methodology](https://docs.arbitrum.foundation/airdrop-eligibility-distribution) Recent primary research demonstrates why accuracy claims need labelled datasets: a May 2025 paper evaluates 193,701 addresses including 23,240 labelled sybils and reports metrics above 0.9, which are dataset results, not production Arbitrum or Optimism accuracy. [Sybil detection research](https://arxiv.org/abs/2505.09313)

Blur's rules describe bidding/listing/lending rather than simply rewarding executed trade volume, so comparing all its points to a v4 volume ranking would be misleading. [Blur Season 4 rules](https://paragraph.com/@blurdao/season-4-rewards-loyalty) A November 2024 participant reports missing stake visibility despite network/cache changes; this is an unresolved report, not a verified stolen-funds finding. [Blur holder discussion](https://gov.blur.foundation/t/missing-blur-on-the-holder-foundation-airdrop/538) INFERENCE: rank boosts and loyalty rewards can encourage optimization for score rather than beneficial liquidity. UNKNOWN: no primary recent wash-trading percentage or proved protocol loss was verified; old sensational figures are excluded rather than recycled as current facts.

## 6. Contracts and constructor-only deployment

These are Solidity **interfaces and behavioral specifications, not implementations**. No proxies, administrative recovery, blacklist, upgrade, pause or signer setter exists. All public operational actions are permissionless with state checks. The only fixed externally privileged identity is the assumed oracle signer, written as the literal source constant above. PoolManager and token constructor inputs are infrastructural references, not configurable administrators. Gateway and allocator links refer to newly created contracts fixed during construction, never arbitrary operator addresses.

| Contract | Purpose and constructor | Storage | External access, events and invariants |
|---|---|---|---|
| `EpochAllocator` | Parent and oracle consumer. `constructor(IPoolManager manager, address launchToken, address quoteToken, uint24 fee, int24 tickSpacing, uint256 budgetRaw, uint32 ratePpm, bytes32 hookSalt)`; creates gateway, then CREATE2 hook | Immutable manager, currencies, key, N=10, ceilings, templates, signer; epoch state, reservations, lists, sums, request-use flags, liabilities, claims | Anyone funds/allocates reserves before start, prepares, submits, finalizes, expires or claims. Events Funded, EpochPrepared, RankAccepted, SumAccepted, EpochFinalized, EpochExpired, Claimed. Immutable recipient/formula; reserves cover liabilities |
| `ParticipantGateway` | Authenticated execution. `constructor(IPoolManager manager, IEpochAllocator allocator)` called by parent | Immutable links; execution lock and active account | Anyone swaps as self. Only manager enters unlockCallback. Public msgSender view. SwapExecuted event. Never arbitrary calls, participants or pools; settlement ends with zero manager debt |
| `ParticipantHook` | Observation. `constructor(IPoolManager manager, address gateway, address launchToken, address quoteToken, uint24 fee, int24 tickSpacing)` | Immutable links/config; initializedPoolId and H | Only manager callbacks; public configuration views. PoolBound and ParticipantVolume. Correct selectors; afterSwap zero hook delta; unknown routers produce no reward log |
| Optional `LaunchToken` | Standard immutable fixed-supply launch ERC-20. `constructor(string name, string symbol, uint256 supplyRaw)` | Balances, allowances, supply; decimals fixed in source | Standard ERC-20 methods; Transfer/Approval. Entire exact policy supply minted to deployer; no later mint, transfer tax, rebasing or upgrade |

Deploy launch token first if needed; otherwise use an existing reviewed token and deploy only three contracts. Deploy allocator with token and manager references. Allocator creates gateway with `address(this)`; gateway queries the parent's immutable pool key only during later trades, never during parent construction. Parent creates hook referencing that gateway and assigns its hook/key immutables. Thus no circular initializer or setter is needed. Mine hookSalt against the actual parent's CREATE2 address and exact creation code before deployment; allocator constructor checks the resulting flags and reverts on mismatch. Optional launch token funding is a later user/sponsor action, not a link required to complete deployment. **ASSUMPTION (IdentityMD):** outer launcher supports parent-created children within its four-contract count and supplies `$poolManager`/`$token`; confirm this before implementation. No helper verifier/factory is separately deployed.

```solidity
// Imports refer to pinned, vendored v4 types in the eventual implementation.
interface IParticipantGateway {
    function msgSender() external view returns (address);
    function swap(bool zeroForOne, int256 amountSpecified,
        uint160 sqrtPriceLimitX96, uint256 maxInput,
        uint256 minOutput, uint256 deadline) external returns (BalanceDelta);
    function unlockCallback(bytes calldata data) external returns (bytes memory);
}
interface IParticipantHook {
    function getHookPermissions() external pure returns (Hooks.Permissions memory);
    function initializedBlock() external view returns (uint256);
    function beforeInitialize(address sender, PoolKey calldata key,
        uint160 sqrtPriceX96) external returns (bytes4);
    function afterSwap(address sender, PoolKey calldata key,
        SwapParams calldata params, BalanceDelta delta,
        bytes calldata hookData) external returns (bytes4, int128);
}
interface IEpochAllocator {
    // Adapter bytes await a real signed ABI fixture; never guess wire types.
    function poolKey() external view returns (PoolKey memory);
    function fund(uint256 epoch, uint256 amount) external;
    function allocateReserve(uint256 epoch, uint256 amount) external;
    function prepare(uint256 epoch) external;
    function expectedQuestionHash(uint256 epoch, bool isRank,
        uint256 index) external view returns (bytes32);
    function submitRank(uint256 epoch, bytes calldata signedAttestation,
        bytes calldata signature) external;
    function submitSum(uint256 epoch, uint256 index,
        bytes calldata signedAttestation, bytes calldata signature) external;
    function finalize(uint256 epoch) external;
    function expire(uint256 epoch) external;
    function claim(uint256 epoch, uint256 index) external;
    function entitlement(uint256 epoch, uint256 index)
        external view returns (address participant, uint256 amount, bool claimed);
}
interface ILaunchToken {
    function totalSupply() external view returns (uint256);
    function decimals() external view returns (uint8);
    function balanceOf(address who) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
```

Claims always pay the stored participant, even when a third party submits them; no redirect argument exists. Apply checks-effects-interactions and a reentrancy guard before token transfer. Gateway uses exact bounded approvals/transfers and manager settlement, and refunds only its current trader's unused funds. The hook never takes or mints manager assets. Reject fee-on-transfer/rebasing quote tokens at configuration/review; funding measures received balances, and invariants assume the approved token remains non-rebasing. A blocklisted beneficiary may be unable to claim without affecting other claims. There is no arbitrary-token rescue function. Principal risks from the pinned security reference incorporated here are signature replay, decimal errors, rounding, token quirks, reentry and discretionary authority; its illustrative spot-price and approval snippets are not imported blindly.

## 7. Foundry test plan

The eventual suite uses local real PoolManager deployments and vendored dependencies, never a network-dependent test prerequisite. Pin solc and v4 versions compatible with the target chain; v4's transient-storage requirements mean a blanket Paris EVM target is inappropriate without checking the actual core version. This report specifies tests; **none of these behavioral tests ran**, since no implementation is requested.

| Named test | Required assertion/adversary |
|---|---|
| `test_ConstructorLinksAllChildren` | Deploy parent once; all references agree; no post-deploy initializer; optional token keeps total at four |
| `test_HookFlagsAndSelectors` | Address mask 0x2040 equals reported permissions; initialization and swap selectors match |
| `test_EveryEnabledCallbackRejectsNonManager` | Direct attacker calls to beforeInitialize and afterSwap revert, including authentic-looking sender/key |
| `test_InitRejectsWrongKeyAndSecondPool` | Wrong currencies, fees, spacing or second binding rejected; failed manager init rolls H back |
| `test_LifecycleWithRealManager` | Initialize, seed, buy/sell, partial fill, remove liquidity; no fabricated manager-code relocation |
| `test_FreshManagerTokenOnlySeed` | Ordinary valid funded swaps work without hook fee transfer or claim balance dependence |
| `test_UnknownRouterTradesWithoutRewards` | Pool remains usable; arbitrary msgSender convention produces no qualifying event |
| `test_HookDataCannotSpoofVictim` | Gateway hookData addresses do not alter recorded trader; direct fake hook calls fail |
| `test_GatewayContextAndSettlement` | Authentic caller recorded, both directions and exact modes bounded; failed settlement emits no surviving log |
| `test_GatewayReentryAndForgedUnlock` | Token callback reentry, direct unlock and nested execution fail; account context never leaks |
| `test_QuoteUnitsAndNoDoubleCounting` | Six/eighteen-decimal quote fixtures; only quote leg counted; zero and signed-min absolute conversion handled |
| `test_CanonicalGoldenQuestionHashes` | Real service golden JSON bytes/hash match Solidity serializer across decimal window/address boundaries |
| `test_SignatureWireGoldenVectors` | Real signed fixtures recover assumed service key; all dynamic fields encoded per actual schema |
| `test_RejectWrongQuestionAndEvidence` | Different event/pool/N/filter/window/evidence, bought for same consumer, cannot pass |
| `test_RejectWrongDomainAndChain` | Foreign consumer, domain name/version, consumer chain and data chain rejected |
| `test_RejectTamperedEverySignedField` | Mutate every field independently; invalid signatures cannot create claims |
| `test_PanelAbsoluteFloors` | 5/3 passes; 4/3 and 5/2 fail; 100/3 is not rejected merely for its ratio; invalid counts fail |
| `test_ExpiryAndWindowAdvancement` | Future issue, old/expired signatures, overlap, gap, repeated closing block and wrong epoch fail |
| `test_SumsReuseRankWindow` | Same closing block for distinct sums allowed; different participant/filter/hash rejected |
| `test_ListValidationAndTies` | Empty/short list supported; duplicate/zero accounts, oversize list, malformed/trailing answer bytes rejected |
| `test_VerifiedSumsCheckRankOrder` | Positive verified sums must be descending; equal sums require numeric address tie order; rank inconsistency fails finalization |
| `test_RequestAndClaimReplay` | Reused accepted request, repeated sum/index, second finalization and double claim fail |
| `test_RefusalMissingSumAndExpiry` | No partial payouts; grace expiry unreserves without harming old claims; next epoch proceeds |
| `test_FundingCannotChangeActiveBudget` | Late funding rejected; exact reserve ceilings; direct token donations do not change entitlements |
| `test_ClaimPaysOnlyStoredAccount` | Relayer cannot redirect; token failure preserves entitlement; one bad recipient cannot block others |
| `testFuzz_PayoutConservation` | Bound positive volumes/budgets; total payouts <=B and each <=rV; dust never increases liabilities |
| `invariant_ReservesCoverLiabilities` | Across funding, finalization, expiry and claims, quote balance covers reservations plus unpaid claims |
| `test_AdversarialSybilCutoffExample` | Demonstrate membership/pro-rata redistribution under splitting; do not assert nonexistent sybil neutrality |
| `test_AdversarialWashFeeRecapture` | Simulate LP-owned fees and near-zero impact; demonstrate why gross fee is not a guaranteed cost floor |
| `test_TokenProtectedFloor` | If token included, exact policy supply/decimals, exact transfers, no mint even by deployer, no forbidden runtime escape opcodes |

Offline implementation validation should include build, tests, format check and dry-run deployment without keys. Re-run meaningful fuzz invariants with a second seed and greater run count; do not assert exact gas or precomputed CREATE addresses. Retain the supplied protected floor tests unchanged in the independent verifier. Generate no fake signature fixtures signed by test keys and label them service interoperability evidence: mock-key tests prove only consumer logic. Static analysis and independent review remain future checks, not claimed completed work.

## 8. Risks ranked by severity

| Severity | Risk | Who bears it / mitigation / residual |
|---|---|---|
| Critical | Service-key compromise, dishonest re-execution or wrong question serialization | Winners/sponsors; immutable template and floors reduce unrelated-answer attacks, but cannot cryptographically verify logs or agent independence |
| Critical | Attribution forgery or unsafe gateway settlement | Traders/sponsors; manager-only callbacks, fixed gateway and no arbitrary execution; full local lifecycle review required |
| High | Wash farming, fee recapture, sybil cutoff manipulation | Sponsor; caps and no wallet bonuses bound spend; positive reward remains economically conditional |
| High | Unknown real attestation types and canonical schema | Everyone; implementation blocked until actual golden fixtures and recipe grammar exist |
| High | Oracle refusal/censorship, sequential-window delay | Winners; permissionless submissions and expiry restore progress, not the expired rewards |
| Medium | Reorg/finality mismatch and stale archived documentation | Sponsor/winners; finalized request practice and exact windows; service-signed hash alone does not prove finality |
| Medium | Quote token blocks, rebases or malformed transfer behavior | Sponsor/recipient; restrict assets, SafeERC20, balance checks; external token governance may remain |
| Medium | Locked unspent reserves and sponsor operations | Sponsor; document absence of recovery; future capped allocation is possible, withdrawal is not |
| Medium | Subsidy mistaken for retention | Sponsor/community; report fees, persistent accounts and post-incentive activity separately from gross volume |
| Low | Rounding dust, storage/gas and source drift | Sponsor; bounded N, down-rounding and pinned implementation revisions; no gas figures invented |

“No distributor can tamper” is an implementation invariant about fixed signed inputs and immutable formulas. “Nobody can tamper” is stronger than the evidence permits: the signing service and chain consensus remain trust boundaries. The design must advertise the former and disclose the latter.

## 9. Open questions for the IdentityMD developer

1. Can you provide the exact EIP-712 primary type string, every field type, answerType enum, dynamic answer encoding and real signed rank and sum fixtures addressed to a Sepolia consumer?
2. Does the signed `chainId` mean the data chain or consumer chain, and how does the service bind both when they differ?
3. Can you provide canonical question JSON and hash vectors, including window keys, evidence encoding, recipe placement, event filters and address formatting?
4. Does log-rank support poolId filtering, deterministic numeric-address tie-breaking, zero-total exclusion and short or empty results with the semantics specified here?
5. Can the service supply panelSize at least five and agreed at least three using absolute counts, and what are their run cost and availability guarantees?
6. What closing-block finality checks does the signer re-run, and can rank and sum requests require the same closing block hash?
7. How are refused, retried, duplicate and expired requests billed or refunded, and can a schedule generate participant-dependent sum requests after ranking?
8. Does the launch count include parent-created children, and can it mine a hook CREATE2 salt against the allocator's actual address and creation code?
9. How does the launch seed the allocator's quote-token reserve without post-deployment operator calls, if funding must be part of launch rather than later permissionless sponsorship?
10. Which quote token and actual pool fee will be used, and what defensible lower bound exists on attacker fee recapture and rebates before choosing a positive rate?
11. Is conditional economic protection acceptable, or must the top-N product be replaced by a mechanism that avoids ranking cliffs and verifies unrecoverable participant costs?
12. Is a trading-only first version acceptable, or will the service implement a re-runnable historical ownership-aware in-range liquidity-time recipe for LP rewards?
