distribute-tokens
Two-phase contributor rewards - plan builds a tier-priced payout from the repo's merged-PR ranking; send executes it on-chain via Bankr Wallet API with per-recipient idempotency and dry-run.
git clone --depth 1 https://github.com/aeonfun/aeon /tmp/distribute-tokens && cp -r /tmp/distribute-tokens/skills/distribute-tokens ~/.claude/skills/distribute-tokensSKILL.md
<!-- autoresearch: variation C — robustness via per-recipient idempotency state, two-phase resolve→execute, dry-run, retries, 403/429 handling, recovery. Merged: contributor-reward's tier-priced reward-computation folded in as the plan/input phase; the on-chain distribution stays the execute phase. -->
> **${var}** — Phase + target selector. Grammar: `[plan:|all:][dry-run:]<target>`
> - `` (empty) / `<label>` / `dry-run:<label>` → **send** phase: distribute a list from `memory/distributions.yml` (empty = first list). *[default — no prefix]*
> - `plan:` / `plan:<week>` / `plan:dry-run` / `plan:dry-run:<week>` → **plan** phase only: compute rewards from the repo's merged PRs and write the list into `memory/distributions.yml`.
> - `all:` / `all:<week>` / `all:dry-run` / `all:dry-run:<week>` → **plan then send** in one run.
>
> `<label>` is a distribution-list label (e.g. `contributors-2026-W17`). `<week>` is an ISO week (`2026-W17`); empty `<week>` = most recent completed ISO week. `dry-run:` previews without side effects (no yml/state writes in plan; no transfers in send).
## Why this design
This skill owns the whole contributor flywheel: **who deserves what** (plan) and **moving the money** (send). It is split into two phases that can run independently or chained.
**Plan phase — the wiring the project was missing.** Merged PRs already name the people moving the project, but shipped work had no path to a wallet credit. The plan phase is that wiring: it ranks contributors by the PRs they merged in the target week (straight from the GitHub API), prices each eligible contributor against a tier table, and writes a labelled list into `memory/distributions.yml` — the exact file the send phase reads. Keeping a human-visible diff on `memory/distributions.yml` between plan and execution is the cheapest possible audit trail when real money is involved: the plan lands in git, and the operator (or `all:` mode, or a chained step) runs the send next.
**Send phase — this moves real money.** The biggest failure mode is double-sending (re-runs, retries after partial failures, day-rollover bypass of "skip if today" logic) or sending into a black hole (no preflight balance, deprecated API path, missing handle resolution). The send phase therefore:
1. **Persists per-recipient idempotency state** in `memory/state/distributions.json` keyed on `(list, recipient, date_utc)` with the txHash. A successful transfer is *never* re-sent within the same UTC day, even across re-runs or workflow restarts.
2. **Two-phase execution**: RESOLVE (validate config, key, balance, resolve all handles → addresses, build plan) → EXECUTE (send each transfer, persist state after each one). RESOLVE failures abort before any send.
3. **Dry-run mode** outputs the full plan with no transfers.
4. **Wallet API only** for actual transfers — Bankr's docs deprecate the Agent API for transfers. Agent API is used only for handle→address resolution.
The two phases stay decoupled by design: the send phase is the only sanctioned transfer path and owns the idempotency state file, so the plan phase never touches transfer state. `all:` mode simply runs plan then send in sequence (plan writes the list, send reads it), so nothing is re-implemented.
## Config
Reads two independent config/state surfaces depending on phase:
- `memory/distributions.yml` — the distribution lists (read by **send**, written by **plan**).
- The **plan** phase computes its ranking live from the repo's merged PRs via the GitHub API — no input file (see Phase A).
- `memory/state/distributions.json` — per-recipient send idempotency (read/written by **send**).
- `memory/state/contributor-reward-state.json` — plan idempotency + first-PR-bonus history (read/written by **plan**).
If `memory/distributions.yml` is missing when the **send** phase needs it, bootstrap with a commented template (see Send Step 1) and exit cleanly with `DISTRIBUTE_TOKENS_OK — bootstrapped distributions.yml; edit and re-run`.
```yaml
# memory/distributions.yml
defaults:
token: USDC # USDC | ETH (Base only)
amount: "5"
chain: base
lists:
contributors:
description: "Weekly contributor rewards"
token: USDC
amount: "10"
recipients:
- handle: "@alice_dev" # Twitter/X — resolved via Bankr Agent API
amount: "15"
- handle: "@bob_builder"
- address: "0x742d...5678" # direct EVM address — preferred path
label: "Charlie"
amount: "20"
```
### Required secrets
| Secret | Phase | Purpose |
|--------|-------|---------|
| `BANKR_API_KEY` | send (and any dry-run send, which still preflights) | Bankr API key (`bk_...`). Must be **read-write** with **Wallet API** enabled. Read-only keys → 403. **Not needed for `plan:` (pure local file I/O).** |
### Token addresses on Base
- USDC: `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913`
- ETH (native): `tokenAddress: "0x0000000000000000000000000000000000000000"`, `isNativeToken: true`
### Tier pricing (plan phase)
| Rank in leaderboard | Reward (USDC) |
|---------------------|---------------|
| 1 | 25 |
| 2 | 15 |
| 3 | 10 |
| 4 | 5 |
| 5 | 5 |
**First-PR bonus:** +5 USDC, additive, applied once-ever per login (tracked in `memory/state/contributor-reward-state.json`). Rewards landing your first merged upstream PR — the highest-leverage signal in the leaderboard scoring.
**Eligibility floor:** score ≥ 10 AND the contributor must own a non-empty `@handle` (logins without `@` prefix in the table are skipped — bots and parsing artifacts). A single merged upstream PR (+10) qualifies — the goal is to reward shipped work, not gate on volume.
Default `token: USDC` on Base. Operator can override per-recipient amounts in `memory/distributions.yml` after the plan is written if a special bonus is warranted.
---
Read `memory/MEMORY.md` and scan the last ~3 dSet up and run an Aeon agent instance — get started from scratch, pick which skills to turn on or install more from packs, reschedule or change what runs, edit what an existing skill does, fix a skill that isn't firing, set the STRATEGY.md north star and soul/ voice, turn a coding-agent chat into a scheduled Aeon skill, and mine past coding-agent conversations for recurring work worth automating as a skill. Use when the user mentions Aeon, aeon.yml, an Aeon skill / instance / routine / pack, asks to schedule, enable, edit, or debug an agent that runs on a cron, or asks what of their repeated/manual work Aeon could take over.
Mention/keyword sweep on social platforms for [REPLACE: KEYWORDS] — trends, sentiment, top posts
5 concrete real-life actions, leverage-scored against open loops with specificity and anti-fluff gates
Static config-correctness linter for this instance - catches the silent-failure class (unquoted schedules, duplicate keys, unconfigured skills, mode typos, broken requires/MCP refs) that no run-based health skill can see. Notifies only on problems.
Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.
Write a publication-ready article in one of three angles - a trending long-form piece, a watched-repo thesis, or a project-through-a-lens essay. Optional Replicate hero image with --visual.
Automatically merge open PRs that have passing CI, no blocking reviews, and no conflicts
Two-mode aeon.yml workflow builder - analyze inspects URLs and emits a tiered, signal-verified skill-enablement plan plus an aeon.yml diff; enable flips slugs to enabled:true and opens a PR.