Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions skills/zest-repay-primitive/AGENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
name: zest-repay-primitive-agent
skill: zest-repay-primitive
description: "Repay a selected Zest V2 debt asset with explicit confirmation, postcondition-deny safety, and proof-ready JSON. One amount, one repay transaction, one proof."
---

# Zest Repay Primitive β€” Agent Rules

## Purpose

This agent isolates the Zest V2 repay action so a controller (or a human operator) can verify exactly one debt-reducing transaction at a time. It does not borrow, supply collateral, withdraw collateral, swap, or unwind. It does not refresh oracle prices.

## What the agent does

- Reads live state: Zest V2 market / market-vault / assets / egroup canonicality, market `repay` ABI presence, wallet position, repay-asset registry, debt index, next debt index, wallet balance of the repay asset, available STX, and mempool depth for the wallet.
- Validates `--repay-asset` exists in the Zest V2 registry as a borrowable asset and the wallet has an existing debt position for it.
- Validates `--amount` (or `--max`) against the wallet's balance of the repay asset (STX vs SIP-010 path).
- Builds a postcondition-deny transaction calling `v0-4-market.repay(ft, amount, on-behalf-of)`.
- Broadcasts only after `--confirm=REPAY`.
- Polls Hiro for `tx_status`, then reads the post-repay position to confirm scaledDebt decreased.

## What the agent MUST NOT do

- Broadcast without `--confirm=REPAY`.
- Sign with a key that doesn't match `--wallet`.
- Bypass `PostConditionMode.Deny`.
- Repay using a legacy borrow-helper contract; this primitive targets `v0-4-market.repay` directly.
- Submit when the wallet has pending mempool transactions.
- Refresh Pyth oracle prices; the V2 `repay` path does not require them.
- Repay more than the wallet can cover (the skill blocks before broadcast if `--amount` exceeds the wallet's repay-asset balance).

## Decision order

1. `doctor` β€” verify Zest V2 contracts are canonical, the repay ABI exists, and the wallet has gas and no pending tx.
2. `status` β€” confirm the wallet has a tracked Market-Vault position with non-zero scaledDebt for `--repay-asset`.
3. `plan` β€” preview args + postconditions + proof obligations. Use `--max` when the intent is to clear all debt for that asset; the skill sizes the amount as `scaledDebt Γ— next-index / 1e12`.
4. `run --confirm=REPAY` β€” broadcast; on success, the JSON output includes the txid, explorer URL, post-repay position, and proof obligations satisfied.

## Refusal codes (non-exhaustive)

- `CONFIRMATION_REQUIRED` β€” `--confirm=REPAY` missing.
- `V2_CONTRACT_NOT_CANONICAL` β€” one or more of MARKET / MARKET_VAULT / ASSETS / EGROUP is not canonical on Hiro.
- `REPAY_ABI_MISSING` β€” `v0-4-market.repay` not present in the contract interface.
- `INSUFFICIENT_GAS` β€” wallet uSTX is below `--min-gas-reserve-ustx`.
- `NO_V2_POSITION` β€” wallet has no tracked Market-Vault position.
- `NO_DEBT_FOR_ASSET` β€” wallet has no V2 debt for `--repay-asset`.
- `REPAY_ASSET_NOT_BORROWABLE` β€” the asset is not enabled as a Zest V2 debt asset in the live registry.
- `INSUFFICIENT_REPAY_BALANCE` β€” non-STX wallet balance of `--repay-asset` is below `--amount`.
- `INSUFFICIENT_STX_FOR_REPAY` β€” STX-repay path: `--amount + min gas reserve` exceeds available uSTX.
- `AMBIGUOUS_AMOUNT` β€” both `--amount` and `--max` passed.
- `UNKNOWN_NEXT_INDEX` β€” `--max` requested but the borrow vault did not return a usable next-index.
- `PENDING_STX_TX` β€” wallet has pending STX transactions.
- `TX_NOT_SUCCESSFUL` β€” broadcast landed but `tx_status` is not `success` within `--wait-seconds`.

## Proof obligations

The `run` output is suitable for inclusion in a write-skill submission proof:

- `tx_status: success`
- sender matches `--wallet`
- contract/function is `v0-4-market.repay`
- post-condition mode is `deny`
- post-repay Market-Vault position shows reduced `scaledDebt` for the repay asset

## Composition guidance

This primitive is intended to be composed via subprocess (`Bun.spawn`) by leverage / unwind controllers. The controller passes the wallet, asset, amount, and confirmation token in as flags. The primitive's JSON envelope is the controller's success/failure signal.
129 changes: 129 additions & 0 deletions skills/zest-repay-primitive/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
name: zest-repay-primitive
description: "Repays a selected borrowed asset on Zest V2 against an existing debt position with explicit confirmation and proof-ready safety checks."
metadata:
author: "macbotmini-eng"
author-agent: "Hex Stallion"
user-invocable: "false"
arguments: "doctor | status | plan | run"
entry: "zest-repay-primitive/zest-repay-primitive.ts"
requires: "wallet, signing, settings"
tags: "defi, write, mainnet-only, requires-funds, infrastructure, l2"
---

# Zest Repay Primitive

## What it does

`zest-repay-primitive` repays a selected Zest V2 debt asset against an existing V2 debt position on Stacks mainnet. It is a primitive write skill: one operator-selected amount (or `--max` for full repay), one Zest V2 `repay` transaction, and proof-ready JSON output.

The skill is market-aware but not strategy-specific: callers choose `--repay-asset` and either `--amount` or `--max`, and the skill validates those choices against the live Zest registry, the wallet's balance of the repay asset, and the tracked Market-Vault debt position before planning or broadcasting. It does not supply collateral, withdraw collateral, swap, borrow, or unwind a position.

## Why agents need it

Leveraged sBTC unwind workflows and routine debt management both need a proven repay leg before any controller can safely compose withdraw, swap, and repay steps. This skill isolates the debt-reducing action so it can be reviewed, proved, and reused without hiding repay risk inside a larger strategy.

## Safety notes

- This is a write skill and reduces debt by transferring the repay asset from the wallet to the Zest market.
- It requires an existing Zest V2 debt position for the selected asset before repaying.
- It requires explicit `--confirm=REPAY` before broadcast.
- It verifies the configured Zest V2 market, vault, assets, and egroup contracts before a write.
- It reads selected asset, debt index, next index, oracle, wallet balance, gas, and pending transaction state before planning or running.
- It uses `PostConditionMode.Deny` with a wallet-side asset transfer postcondition for the repay asset.
- For non-STX repay assets, the postcondition uses the SIP-010 asset name, not the contract name.
- It calls `v0-4-market.repay` directly; legacy helper contracts are not the submission target.
- It blocks when the sender has pending STX transactions.
- It checks the signer address against `--wallet`.
- It resolves the existing AIBTC wallet/runtime first and does not create or import a wallet.
- It does not hardcode proof wallets, amounts, or proof-only signer behavior.
- It does not refresh oracle prices; the V2 `repay` path does not require Pyth updates.

## Commands

### doctor

Checks live Zest contract/interface readiness, wallet gas, pending transaction depth, and selected asset support. If `--repay-asset` is omitted, `doctor` uses a baseline supported asset (STX) only for dependency readiness; that default pass does not prove readiness for every supported asset.

```bash
bun run skills/zest-repay-primitive/zest-repay-primitive.ts doctor --wallet <stacks-address>
```

If `doctor` returns `UNSUPPORTED_*` for any dependency, stop. Re-verify the Zest contract identifier against live protocol sources or Hiro before planning a repay.

### status

Reads current debt state for the selected repay asset, including `scaledDebt`, the current debt index, the next debt index, and the wallet's balance of the repay asset. Requires explicit `--repay-asset`.

```bash
bun run skills/zest-repay-primitive/zest-repay-primitive.ts status --wallet <stacks-address> --repay-asset STX
```

### plan

Builds a read-only repay preview, including contract arguments, postcondition plan, pending depth, current debt data, wallet balance, and proof obligations. Requires explicit `--repay-asset` and either `--amount` (specific base-unit amount) or `--max` (repay the full current debt, sized using `scaledDebt Γ— next-index / 1e12`).

```bash
bun run skills/zest-repay-primitive/zest-repay-primitive.ts plan --wallet <stacks-address> --repay-asset STX --amount <base-units>
```

```bash
bun run skills/zest-repay-primitive/zest-repay-primitive.ts plan --wallet <stacks-address> --repay-asset STX --max
```

`--amount` is always in base units for the selected repay asset. `STX` is accepted as an operator-facing alias for Zest `wSTX`, and other supported assets use their live registry token, asset name, vault, and decimals.

### run

Rechecks live state, resolves a signer, broadcasts only after explicit confirmation, and returns proof JSON. Requires explicit `--repay-asset`, either `--amount` or `--max`, and `--confirm=REPAY`.

```bash
bun run skills/zest-repay-primitive/zest-repay-primitive.ts run --wallet <stacks-address> --repay-asset STX --amount <base-units> --confirm=REPAY
```

Without `--confirm=REPAY`, `run` refuses before signer resolution or transaction construction.

## Output contract

All commands print one JSON object to stdout with this envelope:

```json
{ "status": "success|blocked|error", "action": "doctor|status|plan|run", "data": {}, "error": null }
```

Success:

```json
{ "status": "success", "action": "plan", "data": {}, "error": null }
```

Blocked:

```json
{
"status": "blocked",
"action": "run",
"data": {},
"error": {
"code": "CONFIRMATION_REQUIRED",
"message": "This write skill requires explicit confirmation.",
"next": "Re-run with --confirm=REPAY."
}
}
```

Error:

```json
{ "status": "error", "action": "run", "data": {}, "error": { "code": "ERROR", "message": "...", "next": "Inspect the error and rerun doctor." } }
```

## Known constraints

- Mainnet only.
- `STX` is treated as the Zest wrapped STX asset (`wSTX`) for the repay contract; the wallet sends uSTX and the protocol wraps internally.
- This skill only repays; it does not enable collateral, supply collateral, swap, borrow, withdraw, or rebalance.
- The verified V2 repay target is `SP1A27KFY4XERQCCRCARCYD1CC5N7M6688BSYADJ7.v0-4-market.repay`.
- The V2 repay ABI is positional: `ft`, `amount`, `on-behalf-of`. `on-behalf-of` defaults to the wallet but can be overridden via `--on-behalf-of <stacks-address>` to repay another account's debt.
- `scaledDebt` is index-scaled principal, not the exact repayment amount. `--max` sizes the repay as `scaledDebt Γ— next-index / 1e12` to anticipate index drift between plan and broadcast; the protocol caps over-repays at the actual debt.
- Single-repay nonce safety is in scope: the skill blocks when the sender already has pending STX transactions. Standalone/cross-skill nonce serialization belongs in the existing nonce runtime primitives and later composed controllers.
Loading
Loading