agentleFS
MiroSharkMiroShark/miroshark-aeon/skills/holdings/SKILL.md

holdings

Skill20 starsChanged 24 days agoMITView on GitHub

Report your wallet holdings of the instance's token - amount held, % of total supply, and 7d/30d amount growth, via public RPC (no dollar value)

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

8 rules. About 0 are common, 0 are distinctive, and 8 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 8

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If ${var} is set, check only that one address (chain inferred: 0x-prefixed → base, else solana) against every token on that chain, ignoring …

    line 71
  2. Very specific:

    If memory/holdings.json is missing and ${var} is empty, abort silently — no notification, no article.

    line 74
  3. Very specific:

    If ${var} is set, check that one wallet against this instance's own tokens (read from memory/holdings.json) — only the wallet is …

    line 99
  4. Very specific:

    Write the ${var} address in literally for <ADDRESS>: one call, no ADDR= assignment, [ ] test, heredoc or > redirect (the run refuses those …

    line 107
  5. Very specific:

    If a row has "error", retry holdings.py once.

    line 113
  6. Very specific:

    If growth is missing (the helper failed before it), leave the growth columns blank and say so in the footer.

    line 140
  7. Very specific:

    Skip rule: if no token's amount changed since the last run (growth.<symbol>.last.amount_delta is 0 for every token), send a single line …

    line 209
  8. Very specific:

    Never invent a balance or price.

    line 224

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

${var} — Optional single wallet address to check (Base 0x… or Solana). If empty, uses every wallet in memory/holdings.json.

What this does

Reads a set of your wallets and the project tokens to track, then reports, per token:

  • Amount held.
  • % of total supply you hold (amount ÷ total_supply).
  • 7d and 30d holdings growth — the change in your token amount over the window (did you accumulate or sell).

No dollar value / price is reported — this is a holdings tracker, not a portfolio-value report. Amounts and supply share only.

On-chain balances come from public, keyless RPC (https://mainnet.base.org for Base, https://api.mainnet-beta.solana.com for Solana); total supply comes from GeckoTerminal. No API key required — override the RPC with BASE_RPC_URL / SOLANA_RPC_URL if you have an authenticated node.

Each instance tracks its own token: this config holds one token — aeon on the aeon instance, MiroShark on the MiroShark instance. Add a second tokens entry only if an instance should report both.

Growth = amount, not price. 7d/30d growth is the change in how many tokens you hold, measured against this skill's own prior snapshots (HOLDINGS_STATE: lines in memory/logs). There is no price-history fetch — the skill builds its own history, one snapshot per run. On the first runs the 7d/30d fields are simply absent (no snapshot that far back yet) and fill in as the log accumulates: once a snapshot ~7 days back exists the 7d figure appears, ~30 days back the 30d figure. This skill runs weekly, so 7d lands on the second run and 30d after ~4-5 runs.

Note: on-chain totalSupply() reverts on the DERC20 aeon contract, so total supply comes from GeckoTerminal (total_supply, 100,000,000,000 aeon).

Config

Reads memory/holdings.json:

{
  "wallets": [
    {"address": "0x…", "label": "personal", "chain": "base"},
    {"address": "8mjM…", "label": "sol", "chain": "solana"}
  ],
  "tokens": [
    {"symbol": "aeon", "contract": "0xbf8e8f0e8866a7052f948c16508644347c57aba3", "chain": "base", "decimals": 18}
  ]
}

A token is checked against every wallet on the same chain. Add the miroshark token as a second tokens entry once its contract is known.

If ${var} is set, check only that one address (chain inferred: 0x-prefixed → base, else solana) against every token on that chain, ignoring the wallet list.

If memory/holdings.json is missing and ${var} is empty, abort silently — no notification, no article.

Steps

1. Fetch balances + prices

python3 skills/holdings/holdings.py memory/holdings.json

The helper (Python stdlib only, sandbox-safe) prints JSON:

  • rows[] — one per (token, wallet): symbol, chain, wallet, label, amount (or error if a fetch failed).
  • per_symbol — totals per token: amount, total_supply, pct_supply (% of supply held).
  • growth - per token, the change vs prior snapshots for last (newest prior snapshot), 7d and 30d: each {date, gap_days, amount_then, amount_delta, amount_delta_pct} or null when no snapshot falls in the window (see step 2).

pct_supply may be null if GeckoTerminal lacked supply for that token — render n/a, do not abort. The helper reads the prior snapshots itself (HOLDINGS_STATE: lines in memory/logs). No dollar value is produced.

If ${var} is set, check that one wallet against this instance's own tokens (read from memory/holdings.json) — only the wallet is overridden, the token list stays instance-specific:

python3 skills/holdings/holdings.py memory/holdings.json --wallet <ADDRESS>

Write the ${var} address in literally for <ADDRESS>: one call, no ADDR= assignment, [ ] test, heredoc or > redirect (the run refuses those shapes). The helper infers the chain (0x-prefixed = base, else solana) and keeps only that chain's tokens. A single ${var} wallet has no snapshot series, so --wallet also drops growth; leave the 7d/30d columns out of that report.

If a row has "error", retry holdings.py once. If it still errors on the RPC (sandbox block / 403 / timeout), note rpc=fetch_fail in the footer for that wallet and continue — a partial report still beats nothing. If pct_supply is null (GeckoTerminal supply miss), report the raw amount and mark % Supply as n/a, do not abort.

2. Read holdings growth from the helper

holdings.py already picked the windows from the HOLDINGS_STATE: lines (written in step 4) in memory/logs and memory/logs/archive. Do not recompute them; render growth.<symbol>:

  • 7d - the snapshot closest to 7 days ago, within +/-2 days.
  • 30d - the snapshot closest to 30 days ago, within +/-4 days.
  • last - the newest prior snapshot at any age (used by the step 5 skip rule).

Each window carries amount_delta (tokens; + = accumulated, - = sold), amount_delta_pct and date / gap_days of the snapshot it compares against. Report growth as tokens and percent, e.g. 7d +1.20B (+10.3%) or 30d +0 (flat). This is holdings movement only, it does not move with price.

A null window (no snapshot in range, e.g. early runs or an off-schedule run) renders as building: never print +0 for "no data", and never fall back to a snapshot outside the range. +0 is reserved for a window that HAS a snapshot and the amount genuinely did not change. growth.<symbol>.partial = true means a wallet fetch failed, so the total is partial: show the windows as n/a for that token.

If growth is missing (the helper failed before it), leave the growth columns blank and say so in the footer. Never do the window math by hand.

3. Compile the report

Save to output/articles/holdings-${today}.md:

# Holdings — ${today}

| Token | Amount | % Supply | 7d | 30d |
|-------|--------|----------|-----|-----|
| aeon  | 12.82B | 12.82% | +1.20B (+10.3%) | +2.10B (+19.6%) |

7d/30d are the change in token **amount** held over the window (accumulation/sell),
from prior snapshots. A window with no snapshot yet is blank (`building`), not `0`.
`+0` means a real no-change.

## By wallet
| Wallet | Amount |
|--------|--------|
| aeon-safe (0xf1e9…158e) | 12.53B |
| aeon-deployer (0x6797…e3a2) | 285.32M |

---
*Balances: public RPC (base=mainnet.base.org). Supply: GeckoTerminal. Growth: own snapshots.*
*Sources: rpc=[ok|fetch_fail] · supply=[ok|na] · growth=[Nd history|building]*

Format large token counts compactly (12.82B, 285.3M, 1.20K), % Supply to two decimals. Omit the "By wallet" table if there is only one wallet (it duplicates the top table).

4. State log (powers tomorrow's deltas)

Append to memory/logs/${today}.md:

### holdings
- HOLDINGS_STATE: date=${today} aeon_amount=XXXX.XX aeon_pct_supply=XX.XX
- Article: output/articles/holdings-${today}.md
- Sources: rpc=ok supply=ok

The HOLDINGS_STATE: line is the snapshot store — this is the "history it builds at each run". Step 2 of every future run parses it with a key=value split. Include a date= field plus, per tracked token, <symbol>_amount + <symbol>_pct_supply (no dollar fields). No thousands separators, full-precision _amount (do not pre-round — the 7d/30d deltas subtract these). Keep key order stable. Always write this line, even on a flat/quiet run, so the series stays unbroken (a missing day just widens the nearest-snapshot search window).

5. Notify

*Holdings — aeon*

12.82B · 12.82% of supply
7d +1.20B (+10.3%) · 30d +2.10B (+19.6%)
_Balances: public RPC. Supply: GeckoTerminal. Growth: own snapshots (7d vs 2026-10-05). ${today}_

The title names the tracked token. The growth line shows token-amount change over the window; render building for a window with no snapshot yet, +0 for a real no-change. With more than one tracked token, repeat the two-line block per token. End with the one-line sources footer (where each number came from, the snapshot date the 7d figure compares against, the run date); drop the 7d date when 7d is building.

Skip rule: if no token's amount changed since the last run (growth.<symbol>.last.amount_delta is 0 for every token), send a single line aeon holdings flat — 12.82B (12.82% of supply). instead of the full block. On a total RPC failure (every wallet fetch_fail), log only — no notification.

Sandbox note

holdings.py uses only the Python stdlib and sends a browser User-Agent (the public mainnet.base.org endpoint 403s the default urllib UA). All reads are public and keyless; no secretcurl, no secrets. If the sandbox blocks the RPC POST, the row carries an error and the skill degrades to a partial report — it never invents a balance.

Constraints

  • Never invent a balance or price. Every number traces to an RPC response or a GeckoTerminal price. A failed fetch is reported as fetch_fail, not zero.
  • A zero balance (wallet holds none of the token) is real data — report 0, distinct from fetch_fail.
  • Preserve the HOLDINGS_STATE: log schema — tomorrow's deltas depend on it.
  • Public RPC only by default; only use BASE_RPC_URL/SOLANA_RPC_URL overrides, and put any key in the URL path, never a header (sandbox blocks env expansion in -H).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.