Skip to content

[AIBTC Skills Comp Day 23] HODLMM Yield Router — Autonomous HODLMM + Zest APY Capital Router#517

Closed
Terese678 wants to merge 3 commits into
BitflowFinance:mainfrom
Terese678:clean-hodlmm-yield-router
Closed

[AIBTC Skills Comp Day 23] HODLMM Yield Router — Autonomous HODLMM + Zest APY Capital Router#517
Terese678 wants to merge 3 commits into
BitflowFinance:mainfrom
Terese678:clean-hodlmm-yield-router

Conversation

@Terese678
Copy link
Copy Markdown

Skill Submission

Skill name: hodlmm-yield-router
Category: Yield
HODLMM integration? Yes

What it does

Autonomously monitors a Bitflow HODLMM liquidity position and compares its fee APY against Zest Protocol STX supply APY in real time. Routes capital to whichever protocol is earning more — emitting Zest deposit instructions when Zest pays 2%+ more, and re-entry instructions when HODLMM recovers. Rebalances bins when drift exceeds 5 bins from position center.

On-chain proof

Skill reads live APY data from Zest pool-0-reserve via Hiro read-only API. Doctor and status commands verify all APIs and wallet reachable. Live wallet execution requires WALLET_SECRET and ENCRYPTION_KEY.

Registry compatibility checklist

  • SKILL.md uses metadata: nested frontmatter (not flat keys)
  • AGENT.md starts with YAML frontmatter (name, skill, description)
  • tags and requires are comma-separated quoted strings, not YAML arrays
  • user-invocable is the string "false", not a boolean
  • entry path is repo-root-relative (no skills/ prefix)
  • metadata.author field is present with your GitHub username
  • All commands output JSON to stdout
  • Error output uses { "error": "descriptive message" } format

Security notes

Writes to chain only via explicit instruction emission. Maximum gas spend enforced at 10 STX per cycle. Never moves capital unless Zest APY exceeds HODLMM by 2%+ threshold. Mainnet only. Clean branch from upstream/main — 3 files only.

Reference

Clean resubmission of #481. Same skill, clean branch, correct validator detection.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 19, 2026

✅ Validation Passed

Skill: hodlmm-yield-router
Errors: 0
Warnings: 0

All checks passed. This submission is ready for review.

@Terese678
Copy link
Copy Markdown
Author

@arc0btc @macbotmini-eng, just requesting review on PR #517. CI green, 0 errors. Thank you.

Copy link
Copy Markdown

@arc0btc arc0btc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good architecture overall — instruction-emission pattern is the right approach for a skills-comp entry, documentation is thorough, and the three-command contract (doctor / status / run) is clean. CI green. Requesting changes on three correctness issues before approve.


[blocking] ROUTER_ADDR uses testnet SM prefix

const ROUTER_ADDR = "SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD";

SM is the Stacks testnet address prefix. Mainnet contracts use SP. The PR description says "Mainnet only" but all routing instructions will reference a testnet contract. Users following the emitted instruction will target a contract that doesn't exist on mainnet. Needs the correct mainnet SP deployer address for dlmm-liquidity-router-v-1-1.


[blocking] APY scaling assumption in fetchHodlmmApyPct

if (apy !== null) return Number(apy) * 100;

This assumes the API returns a decimal fraction (e.g., 0.08 → 8%). If the Bitflow API returns a percentage directly (e.g., 8.0 for 8%), this returns 800%, making every Zest comparison look trivial. The field probing covers many aliases but none validate the magnitude. The fallback fee-based calculation (fees24h / tvl * 365 * 100) does not multiply by 100 — the two paths produce APYs on incompatible scales. Recommend: log the raw apy field value during status output so operators can verify the scale against known pool APYs before trusting routing decisions.


[suggestion] Zest deployer address — V1 vs V2

const ZEST_DEPLOYER  = "SP2VCQJGH7PHP2DJK7Z0V48AGBHQAW3R3ZW1QF4N";
const ZEST_RESERVE   = "pool-0-reserve";

SP2VCQJGH... is the Zest V1 deployer. The live Zest V2 deployer is SP1A27KFY4XERQCCRCARCYD1CC5N7M6688BSYADJ7. If pool-0-reserve exists on V1 but the reserve-state interface differs from V2, the liquidity-rate parsing will silently fail and fall back to the hardcoded 4.5%. Since that fallback is below the 2% threshold needed to trigger routing, the router would never move capital to Zest even when it's genuinely paying more. Verify which deployer is intended and document the version explicitly.


[nit] CENTER_BIN_ID and fetchNonce are unused

CENTER_BIN_ID = 500 (line 182) is defined but never referenced — buildSnapshot computes center from actual user positions. fetchNonce is defined but never called (routing is instruction-only, no on-chain execution). Dead code is low risk but worth a cleanup pass.


[nit] doctor balance check passes on empty wallet

const allOk = Object.values(checks).every((v) => v === true || typeof v === "number");

stx_balance_ustx = 0 satisfies typeof v === "number" and passes allOk. A wallet with zero balance should fail the doctor since gas cannot be covered. Consider: checks.stx_sufficient = bal >= MAX_GAS_STX * 1_000_000.


Fix the SM address and document the APY scale assumption and this is solid work. The instruction-emission-only design is a good guardrail for a competition entry.

@Terese678
Copy link
Copy Markdown
Author

@arc0btc I have addressed all three blocking issues fixed. SP mainnet router address corrected, APY scaling fixed with magnitude check (raw > 1 ? raw : raw * 100), Zest V2 deployer updated to SP1A27KFY4XERQCCRCARCYD1CC5N7M6688BSYADJ7. Ready for re-review.

@TheBigMacBTC
Copy link
Copy Markdown
Contributor

TheBigMacBTC commented Apr 27, 2026

Closing -- write-skill-in-disguise + malformed router contract

Hey @Terese678, this PR is being closed. There are 2 specific reasons. They cascade in severity: the first is that the skill claims write behavior in tags + PR body but the code contains zero on-chain write calls, the second is that the core router contract the skill references is malformed and cannot be queried on Hiro mainnet.

Each reason has the same shape below: what your PR did, what the rule says, and what you need to do to fix it.


Reason 1 — Write-skill-in-disguise. metadata.tags includes write and PR body claims wallet execution, but hodlmm-yield-router.ts contains zero broadcastTransaction / makeContractCall / openContractCall call sites. (REQUIRES REBUILD)

What you did:

Your metadata.tags field at HEAD 83f12eb2 reads:

tags: "defi, write, hodlmm, zest, yield, mainnet-only"

Your PR body claims:

"Live wallet execution requires WALLET_SECRET and ENCRYPTION_KEY." / "Writes to chain only via explicit instruction emission. Maximum gas spend enforced at 10 STX per cycle. Never moves capital unless Zest APY exceeds HODLMM by 2%+ threshold."

We pulled skills/hodlmm-yield-router/hodlmm-yield-router.ts at HEAD 83f12eb2. The file imports makeContractCall and broadcastTransaction (lines 23-24), but contains zero call sites for either:

$ grep -nE 'broadcastTransaction\s*\(' skills/hodlmm-yield-router/hodlmm-yield-router.ts
[empty — no matches]
$ grep -nE 'makeContractCall\s*\(' skills/hodlmm-yield-router/hodlmm-yield-router.ts
[empty — no matches]

Same result for openContractCall, PostConditionMode, or any TX-construction pattern. The skill emits instruction payloads via the emit() helper (line 67) — console.log(JSON.stringify({ status, action, data, error })) and instruction: blocks (line 468+) — for an external caller to interpret. The two surfaces (claimed write skill / actual read-and-emit code) are inconsistent.

What the rule says — verbatim from #484 §8 — Write-skill safety: Actually broadcast:

The skill must call broadcastTransaction (or equivalent) and wait for inclusion. Returning an unsigned tx payload and saying "user can sign and submit" is NOT a write skill — it's a read skill in disguise.

What you need to do to fix it:

Pick one of two paths:

  1. Rebuild as an in-skill executor — replace the instruction-emission paths with direct broadcastTransaction calls from @stacks/transactions, await inclusion via Hiro, return the resulting txid in the JSON output, and capture an on-chain proof TX per #484 §5. Keep metadata.tags including write, mainnet-only, requires-funds.
  2. Reclassify as a read-only router/planner — drop write from metadata.tags; replace with read-only. Rewrite PR body and SKILL.md to describe the skill as an instruction emitter that an external orchestrator broadcasts. Remove the WALLET_SECRET / ENCRYPTION_KEY framing from the PR body.

Open a fresh PR after rebuild or reclassification.


Reason 2 — Malformed ROUTER_ADDR constant. The Stacks address the skill uses as the Bitflow DLMM router cannot be parsed by Hiro mainnet. (BLOCKS ACCEPTANCE)

What you did:

At HEAD 83f12eb2, line 44 of your entry script defines:

const ROUTER_ADDR    = "SP1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD";
const ROUTER_NAME    = "dlmm-liquidity-router-v-1-1";

We pulled the canonical Hiro mainnet contract-source endpoint for that principal:

$ curl -s -o /dev/null -w "%{http_code}\n" \
    "https://api.hiro.so/v2/contracts/source/SP1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD/dlmm-liquidity-router-v-1-1"
400

HTTP 400 is the canonical signal that the principal address is malformed — Hiro cannot parse SP1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD as a valid Stacks address. Your routing instructions will reference a contract that cannot be located on mainnet.

For comparison, the canonical Bitflow DLMM router on Stacks mainnet is SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD.dlmm-liquidity-router-v-1-1 and returns HTTP 200 from the same endpoint (verified 2026-04-27). Both SP and SM are mainnet prefixes (standard vs multisig); only ST and SN are testnet prefixes.

What the rule says — verbatim from #484 §6 — Never fabricate identifiers:

Skills must reference real, deployed contracts. Fabricated identifiers (typos, testnet-only contracts treated as mainnet, or made-up addresses) cannot execute against real on-chain state.

What you need to do to fix it:

  1. Restore ROUTER_ADDR = "SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD" (the canonical Bitflow DLMM mainnet deployer).
  2. Verify on Hiro before pushing:
    curl -s -o /dev/null -w "%{http_code}\n" \
        "https://api.hiro.so/v2/contracts/source/<your_router_addr>/dlmm-liquidity-router-v-1-1"
    # Expect: 200
  3. Open a fresh PR after fixing.

AIBTC x Bitflow Skills Pay the Bills Competition

@secret-mars
Copy link
Copy Markdown
Contributor

Hey — your BFF skills work caught my eye. Heads up: the AIBTC trading comp is live with a thin field (4 agents, 1-2 trades each on the leaderboard). Scoring is unrealized P&L (USD) + volume across allowlisted Bitflow swaps — exactly the surface area BFF skills cover. If your agent is verified on aibtc.com, competition_submit_trade is the entry point. Plenty of room while the field is small. — Secret Mars (SP20GPDS5RYB2DV03KG4W08EG6HD11KYPK6FQJE1)

@Terese678
Copy link
Copy Markdown
Author

Terese678 commented May 19, 2026 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants