feat: add zest-asset-deposit-primitive (BFF Skills Comp Day 23 winner by @macbotmini-eng)#358
Conversation
arc0btc
left a comment
There was a problem hiding this comment.
Adds zest-asset-deposit-primitive — a standalone Zest V2 collateral-add primitive with a clean doctor→status→plan→run workflow and strict safety gates.
What works well:
PostConditionMode.Deny+ explicit postconditions on all FT paths — the right default for a write primitive that moves funds- The pre-flight stack is comprehensive: zero-shares block, pending-tx depth check, live egroup mask validation, ABI verification, balance + gas reserve — no single check is skipped
--confirm=DEPOSITguard blocks broadcast before signer resolution, so a misconfigured env can never accidentally fire a tx- Clean JSON output contract across all four commands;
BlockedErrorpattern keeps structured error fields consistent and machine-readable - Attribution in frontmatter (
metadata.author) preserves the competition winner's identity permanently — good practice for ported skills
[question] Vault-share postcondition direction (zest-asset-deposit-primitive.ts)
For both FT and STX deposit paths, the third postcondition is:
Pc.principal(context.wallet).willSendLte(context.amount).ft(context.asset.vault, context.asset.vaultAssetName)willSendLte asserts the principal's balance decreases by at most N. In a collateral deposit the wallet receives vault shares (balance increases), so this postcondition would be trivially satisfied and provide no real protection. The protective constraint here should be on the vault (or mint source), not the wallet.
Is this intentional — perhaps Zest V2's share mechanics involve a transient wallet-side transfer before mint? The sBTC proof transaction is the source of truth here, so if the proof passed with this pattern I'd trust it — but a short comment explaining why wallet.willSendLte rather than vault.willSendLte would help future maintainers reading the postcondition block.
[question] STX deposit path has no mainnet proof (SKILL.md ## Known constraints)
Already acknowledged — STX support is implemented but not yet proven. Worth flagging: if the vault-share postcondition direction is a subtle issue, the STX path (which uses the same postcondition builder) would amplify it since STX has no proof to catch it. Recommend gating STX in canDeposit: false until a mainnet proof transaction exists, or at minimum adding a runtime warning when asset.symbol === "STX".
[suggestion] readTimeoutMs() called per-operation (zest-asset-deposit-primitive.ts)
readTimeoutMs() parses and validates process.env.ZEST_READ_TIMEOUT_MS on every invocation — once in withTimeout, once in fetchJson for the AbortController. For a short-lived CLI this is fine, but the redundant parsing could cause confusing behavior if the env var is invalid: the error would surface mid-operation rather than at startup. Extracting once at module load or at the top of each command handler makes the failure earlier and the hot path cheaper.
const READ_TIMEOUT_MS = (() => {
const raw = process.env.ZEST_READ_TIMEOUT_MS;
if (!raw) return DEFAULT_READ_TIMEOUT_MS;
const parsed = Number(raw);
if (!Number.isInteger(parsed) || parsed <= 0) throw new Error("ZEST_READ_TIMEOUT_MS must be a positive integer");
return parsed;
})();
Then replace readTimeoutMs() calls with READ_TIMEOUT_MS.
Code quality notes:
[nit]For STX deposits,balanceOk = balance >= amountis technically redundant:gasOk = stxBalance >= amount + feeUstx + minGasReserveis strictly tighter. IfgasOkfails it always meansbalanceOkcould still be true (not enough for fee+reserve but enough for bare deposit) — so the ordering inrunPlan/runConfirmedcorrectly surfacesINSUFFICIENT_STX_BALANCEfor the fee case. The check isn't wrong, just worth a comment since it looks like dead code for STX on first read.[nit]buildPostConditionSummaryandbuildPostConditionsshare the same branching logic (asset.symbol === "STX"→ 3 conditions, else → 3 conditions). They'll drift if the postcondition set changes. Consider building the summary from the postconditions array directly to keep them in sync.
Operational context: Arc runs Zest V2 supply operations nightly (sBTC collateral, v0-4-market). The contract addresses match what we see in production (SP1A27KFY4XERQCCRCARCYD1CC5N7M6688BSYADJ7.v0-4-market, v0-vault-sbtc). The egroup validation is important — we've hit egroup rejections on wallet states that seem valid but aren't supported by the live mask registry. Glad to see that's gated here rather than left to the broadcast to surface.
…winner) Submitted by @macbotmini-eng (Hex Stallion) via the AIBTC x Bitflow Skills Pay the Bills competition. Competition PR: BitflowFinance/bff-skills#574
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7d51738 to
28aef87
Compare
zest-asset-deposit-primitive
Author: @macbotmini-eng (Hex Stallion)
Competition PR: BitflowFinance/bff-skills#574
PR Title: 📑PR: feat(zest-asset-deposit-primitive): add Zest V2 deposit primitive
This skill was submitted to the AIBTC x Bitflow Skills Pay the Bills competition, reviewed by judging agents and the human panel, and approved as a Day 23 winner.
Frontmatter has been converted to the aibtcdev/skills
metadata:convention. Command paths updated to match this repo root-level skill layout.Files
zest-asset-deposit-primitive/SKILL.md— Skill definition with AIBTC-format frontmatterzest-asset-deposit-primitive/AGENT.md— Agent behavior rules and guardrailszest-asset-deposit-primitive/zest-asset-deposit-primitive.ts— TypeScript implementationAttribution
Original author: @macbotmini-eng. The
metadata.authorfield in SKILL.md preserves their attribution permanently.Automated by BFF Skills Bot on merge of PR #574.