diff --git a/skill/bucket-sdk/SKILL.md b/skill/bucket-sdk/SKILL.md new file mode 100644 index 0000000..8d4204d --- /dev/null +++ b/skill/bucket-sdk/SKILL.md @@ -0,0 +1,142 @@ +--- +name: bucket-sdk +description: Use when integrating Bucket Protocol on Sui with @bucket-protocol/sdk: CDP position updates, USDB mint and repay, PSM swaps, saving pools, flash mint, oracle prices, and protocol state queries. +--- + +# Bucket SDK + +Use this skill whenever the task touches `@bucket-protocol/sdk`. + +Official docs: +- https://docs.bucketprotocol.io/ +- https://github.com/Bucket-Protocol/bucket-protocol-sdk + +## Trigger Scope + +Use for: +- Building PTBs with `buildManagePositionTransaction`, `buildPSMSwapInTransaction`, saving pool builders, or flash mint. +- Querying collateral, pool, oracle, position, account, or reward data. +- Debugging Bucket integration issues (`Unsupported collateral type`, price feed issues, pool balance checks). + +Do not use for: +- Generic Sui SDK tasks that do not involve Bucket Protocol. + +## Fast Workflow + +1. Initialize client: + +```ts +import { BucketClient } from '@bucket-protocol/sdk'; +const client = await BucketClient.initialize({ network: 'mainnet' }); +``` + +2. Resolve live types (never assume static lists): + +```ts +const usdbType = await client.getUsdbCoinType(); +const collateralTypes = await client.getAllCollateralTypes(); +const oracleTypes = await client.getAllOracleCoinTypes(); +const psmTypes = Object.keys(await client.getAllPsmPoolObjects()); +const savingLpTypes = Object.keys(await client.getAllSavingPoolObjects()); +``` + +3. Build PTB commands on `Transaction`, then sign and execute separately. +4. For unsupported user input, validate against live lists/maps first and return a clear error. +5. If config may be stale (protocol upgrade), call `await client.refreshConfig()`. + +## Coin Type Policy + +Keep context small. Do not paste long coin-type tables unless explicitly requested. + +Rules: +1. CDP `coinType` must come from `getAllCollateralTypes()`. +2. PSM `coinType` must come from keys of `getAllPsmPoolObjects()`. +3. Saving `lpType` must come from keys of `getAllSavingPoolObjects()`. +4. Oracle price queries should use `getAllOracleCoinTypes()` when possible. +5. Use `references/coin-types.md` only when the user asks for static literals. + +## Core Behavior Notes + +- All write methods are PTB builders (`build*`): they append Move calls, not execute transactions. +- Amounts are raw integers (smallest unit). No auto decimal scaling. +- Most config-dependent methods are async; always `await`. +- `buildManagePositionTransaction` auto-aggregates prices when borrow or withdraw is requested. +- `accountObjectOrId` switches from EOA mode to Bucket Account mode. + +## Default Implementation Skeleton + +Use this shape unless the user asks for another architecture: + +```ts +import { BucketClient } from '@bucket-protocol/sdk'; +import { Transaction } from '@mysten/sui/transactions'; + +const client = await BucketClient.initialize({ network: 'mainnet' }); +const tx = new Transaction(); + +// validate types from live config here +// append build* commands here + +tx.setSender(address); +await client.getSuiClient().signAndExecuteTransaction({ signer, transaction: tx }); +``` + +If user asks for simulation only, use `simulateTransaction` after setting sender. + +## Method Map + +Query methods: +- `getUsdbSupply`, `getAllVaultObjects`, `getAllPsmPoolObjects`, `getAllSavingPoolObjects` +- `getUserPositions`, `getUserSavings`, `getUserAccounts` +- `getOraclePrices`, `getAllOraclePrices` + +Build methods: +- CDP: `buildManagePositionTransaction`, `buildClosePositionTransaction`, `buildClaimBorrowRewardsTransaction` +- PSM: `buildPSMSwapInTransaction`, `buildPSMSwapOutTransaction` +- Saving: `buildDepositToSavingPoolTransaction`, `buildWithdrawFromSavingPoolTransaction`, `buildClaimSavingRewardsTransaction` +- Flash: `flashMint`, `flashBurn` + +## What To Open Next (Modular References) + +Read only what you need: +- `references/api-workflows.md` + - End-to-end PTB examples for CDP, PSM, saving, flash, and composition. +- `references/query-cheatsheet.md` + - Query API quick reference, return-shape expectations, and troubleshooting checks. +- `references/protocol-concepts.md` + - CDP mechanics, CR/MCR, liquidation model, PSM peg behavior, and yield layers. +- `references/coin-types.md` + - Dynamic type resolution patterns and a small literal snapshot. + +## Runtime Diagnostics + +Use the bundled script when you need a quick live state snapshot: + +```bash +# Run in a project directory that has @bucket-protocol/sdk installed +# is wherever you store this skill, for example: +# .github/skills/bucket-sdk +# .agents/skills/bucket-sdk +# .claude/skills/bucket-sdk +node /scripts/query-state.mjs +``` + +It prints supply, prices, vault stats, PSM pools, saving pools, and supported collateral types. + +## Response Expectations + +- Return code that is copy-paste runnable in TypeScript. +- Mention which live type source was used (`getAllCollateralTypes`, PSM keys, or saving LP keys). +- When rejecting input, include the checked list source and a short fix suggestion. +- Prefer small, composable PTBs over unrelated helper abstractions. + +## Quality Gates Before Finalizing + +1. Type validation done against live config (not hardcoded memory). +2. Amount units are raw integer units. +3. Transaction path is complete: build -> set sender -> sign+execute (or simulate). +4. Failure handling includes useful checks: + - Unsupported collateral or PSM or lp type + - Missing price feed + - Insufficient pool balance + - Insufficient user balance diff --git a/skill/bucket-sdk/references/api-workflows.md b/skill/bucket-sdk/references/api-workflows.md new file mode 100644 index 0000000..63bb00e --- /dev/null +++ b/skill/bucket-sdk/references/api-workflows.md @@ -0,0 +1,122 @@ +# API Workflows + +Use this file for write-path examples. Keep `SKILL.md` minimal and read this only when implementing flows. + +## Setup + +```ts +import { BucketClient } from '@bucket-protocol/sdk'; +import { Transaction } from '@mysten/sui/transactions'; + +const client = await BucketClient.initialize({ network: 'mainnet' }); +const tx = new Transaction(); +``` + +## CDP: Deposit and Borrow + +```ts +const [collateralCoin, usdbCoin] = await client.buildManagePositionTransaction(tx, { + coinType, + depositCoinOrAmount: 1_000_000_000, + borrowAmount: 800_000, +}); +``` + +## CDP: Repay and Withdraw + +```ts +await client.buildManagePositionTransaction(tx, { + coinType, + repayCoinOrAmount: 500_000, + withdrawAmount: 200_000_000, +}); +``` + +## CDP: Close Position + +```ts +const [allCollateral] = await client.buildClosePositionTransaction(tx, { + address, + coinType, +}); +``` + +## PSM: Stablecoin -> USDB + +```ts +const usdbCoin = await client.buildPSMSwapInTransaction(tx, { + coinType: psmCoinType, + inputCoinOrAmount: 1_000_000, +}); +``` + +## PSM: USDB -> Stablecoin + +```ts +const stableCoin = await client.buildPSMSwapOutTransaction(tx, { + coinType: psmCoinType, + usdbCoinOrAmount: 1_000_000, +}); +``` + +## Saving Pool: Deposit and Withdraw + +```ts +await client.buildDepositToSavingPoolTransaction(tx, { + address, + lpType, + depositCoinOrAmount: 1_000_000, +}); + +const usdbOut = await client.buildWithdrawFromSavingPoolTransaction(tx, { + lpType, + amount: 500_000, +}); +``` + +## Saving Pool: Claim Rewards + +```ts +const rewards = await client.buildClaimSavingRewardsTransaction(tx, { lpType }); +for (const coin of Object.values(rewards)) { + tx.transferObjects([coin], address); +} +``` + +## Flash Mint + +```ts +const [flashUsdb, receipt] = await client.flashMint(tx, { amount: 10_000_000 }); +// use flashUsdb in the same PTB +await client.flashBurn(tx, { usdbCoin: flashUsdb, flashMintReceipt: receipt }); +``` + +## Compose Operations Atomically + +Example: swap to USDB then deposit to saving pool in one PTB. + +```ts +const usdbCoin = await client.buildPSMSwapInTransaction(tx, { + coinType: psmCoinType, + inputCoinOrAmount: 1_000_000, +}); + +await client.buildDepositToSavingPoolTransaction(tx, { + address, + lpType, + depositCoinOrAmount: usdbCoin, +}); +``` + +## Execution + +```ts +tx.setSender(address); +await client.getSuiClient().signAndExecuteTransaction({ signer, transaction: tx }); +``` + +## Notes + +- Build methods append commands only; they do not execute on chain. +- Use raw integer units, not decimal strings. +- For dry run, set sender and call `simulateTransaction`. diff --git a/skill/bucket-sdk/references/coin-types.md b/skill/bucket-sdk/references/coin-types.md new file mode 100644 index 0000000..edc3d4b --- /dev/null +++ b/skill/bucket-sdk/references/coin-types.md @@ -0,0 +1,41 @@ +# Bucket Protocol - Coin Types (Lean Reference) + +Use runtime queries first. Do not rely on static tables unless the user explicitly asks for literals. + +## Live Type Discovery (Preferred) + +```ts +const usdbType = await client.getUsdbCoinType(); +const collateralTypes = await client.getAllCollateralTypes(); +const oracleTypes = await client.getAllOracleCoinTypes(); +const psmTypes = Object.keys(await client.getAllPsmPoolObjects()); +const savingLpTypes = Object.keys(await client.getAllSavingPoolObjects()); +``` + +Validation rules: +- CDP `coinType`: must be in `collateralTypes` +- PSM `coinType`: must be in `psmTypes` +- Saving `lpType`: must be in `savingLpTypes` + +If not present, return an explicit unsupported-type error. + +## Minimal Literal Snapshot (Mainnet, May Change) + +These are common values only. Treat as a convenience snapshot, not source of truth. + +| Token | Type | +| --- | --- | +| SUI | `0x2::sui::SUI` | +| USDB | `0xe14726c336e81b32328e92afc37345d159f5b550b09fa92bd43640cfdd0a0cfd::usdb::USDB` | +| USDC | `0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC` | +| USDT | `0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT` | +| BUCK | `0xce7ff77a83ea0cb6fd39bd8748e2ec89a3f41e8efdc3f4eb123e0ca37b184db2::buck::BUCK` | +| sUSDB LP | `0x38f61c75fa8407140294c84167dd57684580b55c3066883b48dedc344b1cde1e::susdb::SUSDB` | + +## When To Use Static Literals + +Use static literals only when: +- user requests a documented literal string, or +- runtime access is not available. + +Otherwise always resolve from live config. diff --git a/skill/bucket-sdk/references/protocol-concepts.md b/skill/bucket-sdk/references/protocol-concepts.md new file mode 100644 index 0000000..61745bb --- /dev/null +++ b/skill/bucket-sdk/references/protocol-concepts.md @@ -0,0 +1,342 @@ +# Bucket Protocol — Concepts & Decision Guide + +This document explains the core protocol concepts an agent needs to understand to make correct integration decisions. + +**Official docs**: + +## What is Bucket Protocol? + +Bucket Protocol is a **CDP-based stablecoin protocol on Sui, purpose-built for capital efficiency**. Users deposit supported on-chain assets as collateral to borrow **USDB**, a decentralized, over-collateralized stablecoin pegged to $1 USD. + +USDB serves two user needs: + +- **Volatile-asset holders** borrow USDB against collateral for long-term leverage or liquidity without selling core positions. +- **Stablecoin allocators** hold USDB and deposit into the sUSDB Savings Pool for competitive, flexible yield. + +### Key Advantages + +- **Fixed per-asset interest rates** — funding costs known upfront, no variable rate anxiety +- **High LTV** — per-asset CDP isolation enables more usable capital +- **Explicit risk boundaries** — simple CR/MCR rule with live liquidation price; no Recovery Mode +- **No one-time borrow fee** — only fixed interest rates apply +- **Derivative collateral support** — LSTs, sCoins, gCoins can be used as collateral + +## CDP (Collateralized Debt Positions) + +### How It Works + +1. User deposits collateral (e.g. SUI, BTC, ETH) into a vault +2. User borrows USDB against that collateral +3. The position must maintain a **minimum collateral ratio** (MCR) — typically 110% +4. Interest accrues on the debt over time +5. User can repay debt and/or withdraw collateral at any time (within CR constraints) + +### Collateral Ratio (CR) & Liquidation Price + +``` +CR = (Collateral Price × Collateral Amount) / (initial Borrow Amount + Accrued Interest) +``` + +Each collateral has a fixed **Minimum Collateral Ratio (MCR)** (e.g. 110%). If CR < MCR, the position is liquidatable. + +``` +Liquidation Price = (MCR × Debt) / Collateral Amount +``` + +**Example**: 1,000 SUI at $5/SUI ($5,000 collateral), borrow 4,000 USDB, MCR = 110% + +- Entry CR = 5,000 / 4,000 = 125% +- Liquidation Price = (1.10 × 4,000) / 1,000 = $4.40/SUI + +> After every action, the position must satisfy CR ≥ MCR. + +### When to Use Which CDP Method + +| Goal | Method | Needs Price? | +| -------------------------- | ----------------------------------------------------------------------- | :----------: | +| Deposit collateral only | `buildManagePositionTransaction({ depositCoinOrAmount })` | No | +| Borrow USDB | `buildManagePositionTransaction({ borrowAmount })` | Yes (auto) | +| Repay debt | `buildManagePositionTransaction({ repayCoinOrAmount })` | No | +| Withdraw collateral | `buildManagePositionTransaction({ withdrawAmount })` | Yes (auto) | +| Deposit + borrow in one tx | `buildManagePositionTransaction({ depositCoinOrAmount, borrowAmount })` | Yes (auto) | +| Close entire position | `buildClosePositionTransaction()` | No | +| Claim borrow incentives | `buildClaimBorrowRewardsTransaction()` | No | + +`buildManagePositionTransaction` is the all-in-one method. You can combine any of deposit/borrow/repay/withdraw in a single call. It handles zero-coin cleanup automatically. + +### Fees & Interest + +- **No one-time borrow fee** (removed in the v2 upgrade) +- **Fixed borrow interest rate** per collateral type (visible in `VaultInfo.interestRate`) +- Interest accrues in real time and is added to debt continuously +- Rates are protocol-set and not user-adjustable + +### Mint Caps + +Each vault has a maximum USDB supply (`VaultInfo.maxUsdbSupply`). Borrowing beyond this cap will fail on-chain. Mint caps help manage market depth during liquidations and reduce slippage risk in extreme conditions. + +### Liquidation + +- **Trigger**: CR < MCR → position becomes liquidatable +- **Scope**: **Full seizure** — ALL collateral in that CDP is taken +- **Executor**: Liquidations are protocol-run (not by users) + +**How liquidation is executed on-chain:** + +1. Flash-mint USDB to immediately repay the CDP's outstanding debt +2. Seize all collateral from the liquidated CDP +3. Sell the seized collateral for USDB using on-chain venues +4. Repay the flash-minted USDB from step 1 + +**User loss**: At liquidation threshold, `User loss ≈ (MCR - 1) × Debt`. The user keeps the USDB previously borrowed. + +**Bad-debt backstop**: If collateral proceeds < debt, the protocol's Insurance Fund (funded from protocol revenue) covers the gap. + +**Avoiding liquidation**: Deposit more collateral or repay part of the debt to raise CR. + +> The SDK does **not** execute liquidations — it only builds position management transactions. + +## USDB Peg Mechanism + +USDB targets $1 USD via two mechanisms: + +### Peg Stability Module (PSM) + +The PSM enables trustless 1:1 conversions between USDB and supported stablecoins (USDC, USDT, BUCK), subject to a PSM fee. + +- **Swap In**: Stablecoin → USDB (e.g. deposit 1 USDC, receive ~1 USDB minus fee) +- **Swap Out**: USDB → Stablecoin (e.g. deposit 1 USDB, receive ~1 USDC minus fee) + +Each PSM pool has `swapIn` and `swapOut` fee rates (visible in `PsmPoolInfo.feeRate`). Partner accounts may get lower fees via `partnerFeeRate`. + +### Price Stability & Arbitrage + +Let `price_usdb` = USDB market price on DEX, `fee_in` = PSM IN fee, `fee_out` = PSM OUT fee. + +**Upward depeg (USDB > $1)**: Acquire USDB via PSM IN at ~$1, sell on market at `price_usdb > 1`. Profitable when `price_usdb > 1 + fee_in + gas`. + +**Downward depeg (USDB < $1)**: Buy USDB on market at `price_usdb < 1`, exit via PSM OUT at ~$1. Profitable when `price_usdb < 1 - fee_out - gas`. + +**Flash-mint assisted arbitrage**: For capital efficiency, use flash mint to source/retire USDB within one transaction, pairing the opposite leg through PSM or market liquidity. + +### When to Use PSM vs CDP + +| Scenario | Use PSM | Use CDP | +| ------------------------------- | :-----: | :-----: | +| Quickly get USDB from USDC/USDT | ✓ | | +| Convert USDB back to USDC/USDT | ✓ | | +| Leverage crypto exposure | | ✓ | +| Earn borrow incentive rewards | | ✓ | +| Long-term USDB generation | | ✓ | +| Arbitrage USDB peg on DEX | ✓ | | + +## Saving System — Two Layers of USDB Yield + +Bucket provides two layers of yield for USDB holders. Total APR = BSR + SUI rewards APR. + +### Layer 1: sUSDB and BSR (Base Savings Rate) + +- **BSR** is Bucket’s fixed savings rate for USDB. It accrues in real time by increasing the exchange rate between sUSDB and USDB. +- **sUSDB** is the yield-bearing stablecoin you receive when you stake USDB. Yield does not change your sUSDB balance; instead, each unit of sUSDB represents more USDB over time. + +**Formulas**: + +``` +minted_sUSDB = staked_USDB / exchange_rate +unstaked_USDB = sUSDB_balance × exchange_rate +``` + +- No stake or unstake fee. No lockup. +- Balances and the exchange rate update in real time. + +### Layer 2: sUSDB Savings Pool + +- Deposit sUSDB into the sUSDB Savings Pool to earn **additional SUI rewards** on top of BSR. +- Savings rewards accrue continuously and are claimable at any time. +- Withdraw whenever — no fees, no lockup. + +### Key Parameters + +- `savingRate`: BSR yield rate (exchange rate growth) +- `rewardRate`: Additional token incentive rates (per reward type, e.g. SUI) +- `usdbDepositCap`: Maximum total USDB the pool accepts (null = no cap) +- `lpSupply`: Total sUSDB LP tokens outstanding + +### SDK Methods + +| Goal | Method | +| --------------------------------------------- | ------------------------------------------ | +| Deposit USDB → get sUSDB → earn BSR + rewards | `buildDepositToSavingPoolTransaction()` | +| Withdraw sUSDB → receive USDB | `buildWithdrawFromSavingPoolTransaction()` | +| Claim SUI rewards from savings pool | `buildClaimSavingRewardsTransaction()` | + +## Flash Mint + +### How It Works + +Flash minting allows borrowing USDB **without collateral** within a single atomic transaction. The borrowed USDB must be repaid (plus fee) before the transaction completes, or the entire transaction reverts. + +### Use Cases + +- Arbitrage between USDB price on DEXes +- Self-liquidation or position restructuring +- Leveraged operations composed in a single PTB + +### Fee + +A small fee rate applies (visible in `FlashMintInfo.feeRate`). The repayment must cover principal + fee. + +```typescript +const [usdbCoin, receipt] = await client.flashMint(tx, { amount: 10_000_000 }); +// ... do things with usdbCoin (arbitrage, etc.) ... +// Must return principal + fee: +await client.flashBurn(tx, { usdbCoin, flashMintReceipt: receipt }); +``` + +## Leverage + +Bucket supports two ways to build a leveraged long on supported assets: + +### One-Click Leverage (SUI/LST, Wrapped BTC) + +Uses flash minting to automatically create a leveraged position in one transaction: + +1. User deposits initial capital (e.g. 100 USDC) +2. Flash-mints USDB worth the leverage amount +3. Swaps initial capital + flash-minted USDB for target collateral (via Cetus aggregator) +4. Deposits all as collateral → borrows USDB +5. Repays the flash-mint with borrowed USDB + +**Result**: Leveraged position established atomically. Costs shown: flash-loan fee + price impact. + +### Manual Looping (All Collaterals) + +For assets without one-click support: + +1. Deposit collateral → Borrow USDB +2. Swap USDB → more collateral (via DEX) +3. Deposit the new collateral +4. Repeat until desired leverage + +### De-Leveraging + +To reduce leverage or raise CR: + +- **Repay with collateral**: sell portion of collateral into USDB to repay debt (atomically via DEX routing) +- **Repay with USDB**: repay from USDB in wallet +- After repayment, CR rises and liquidation price moves lower (safer) + +## Account System + +### EOA vs Account Objects + +Bucket supports two modes for holding positions: + +- **EOA (Externally Owned Account)**: Positions are tied directly to the wallet address. This is the default when `accountObjectOrId` is omitted. +- **Account Object**: A Sui on-chain object (`Account`) that holds positions on behalf of the owner. Useful for managing multiple independent positions from one wallet, or for protocol-level integrations. + +### When to Use Account Objects + +- Most integrations should **omit** `accountObjectOrId` and operate in EOA mode +- Use Account objects when: the user has existing Account-based positions (check with `getUserAccounts()`), or the integration needs separate position management + +### Checking for Existing Accounts + +```typescript +const accounts = await client.getUserAccounts({ address: walletAddress }); +// accounts is an array of Account objects +// If empty, the user only has EOA positions +``` + +## Oracle Price System + +### How Prices Work + +Bucket uses **Pyth Network** as its oracle. The SDK handles price feeds automatically: + +1. Fetches latest price data (VAA) from Pyth's Hermes REST API +2. Builds on-chain Wormhole verification + Pyth update calls +3. Passes the verified price to CDP/PSM operations + +### Basic vs Derivative Prices + +- **Basic**: Direct Pyth feed (SUI, BTC, ETH, USDC, etc.) +- **Derivative**: Price derived from basic price × exchange rate + - **sCoin** (Scallop): sSUI price = SUI price × Scallop exchange rate + - **gCoin** (Unihouse): gSUI price = SUI price × Unihouse exchange rate + - **BFBTC**: BFBTC price = BTC price × BFBTC exchange rate + +`aggregatePrices()` handles both basic and derivative prices transparently. The `build*` methods that need prices (borrow, withdraw, PSM swap) call it internally. + +### Querying Prices Without a Transaction + +```typescript +// Get live prices (simulates a PTB internally) +const prices = await client.getOraclePrices({ coinTypes: ['0x2::sui::SUI'] }); +console.log(prices['0x2::sui::SUI']); // e.g. 3.45 (USD) + +// Get all supported prices +const allPrices = await client.getAllOraclePrices(); +``` + +## Composing Operations in a Transaction + +Sui's PTB model allows combining multiple operations atomically. Common patterns: + +### Pattern 1: PSM → Saving (get USDB then earn yield) + +```typescript +const tx = new Transaction(); +const usdb = await client.buildPSMSwapInTransaction(tx, { + coinType: usdcType, + inputCoinOrAmount: 1_000_000, +}); +await client.buildDepositToSavingPoolTransaction(tx, { + address: myAddress, + lpType: susdbLpType, + depositCoinOrAmount: usdb, +}); +``` + +### Pattern 2: Borrow → PSM out (borrow USDB then convert to USDC) + +```typescript +const tx = new Transaction(); +const [, usdb] = await client.buildManagePositionTransaction(tx, { + coinType: '0x2::sui::SUI', + depositCoinOrAmount: 2_000_000_000, + borrowAmount: 1_000_000, +}); +const usdc = await client.buildPSMSwapOutTransaction(tx, { + coinType: usdcType, + usdbCoinOrAmount: usdb, +}); +tx.transferObjects([usdc], myAddress); +``` + +### Pattern 3: Flash mint → Arbitrage → Repay + +```typescript +const tx = new Transaction(); +const [usdb, receipt] = await client.flashMint(tx, { amount: 10_000_000 }); +// ... use usdb for arbitrage on a DEX ... +await client.flashBurn(tx, { usdbCoin: usdbAfterArb, flashMintReceipt: receipt }); +``` + +### Pattern 4: One-Click Leverage (Flash Mint → Swap → Deposit → Borrow → Repay) + +```typescript +const tx = new Transaction(); +// 1. Flash mint USDB for leverage +const [flashUsdb, receipt] = await client.flashMint(tx, { amount: leverageAmount }); +// 2. Swap USDB → target collateral via DEX (not part of Bucket SDK) +// ... DEX swap calls ... +// 3. Deposit all collateral, borrow USDB to cover flash mint +const [, borrowedUsdb] = await client.buildManagePositionTransaction(tx, { + coinType: '0x2::sui::SUI', + depositCoinOrAmount: totalCollateralCoin, + borrowAmount: leverageAmount + flashFee, +}); +// 4. Repay flash mint +await client.flashBurn(tx, { usdbCoin: borrowedUsdb, flashMintReceipt: receipt }); +``` diff --git a/skill/bucket-sdk/references/query-cheatsheet.md b/skill/bucket-sdk/references/query-cheatsheet.md new file mode 100644 index 0000000..d316fe8 --- /dev/null +++ b/skill/bucket-sdk/references/query-cheatsheet.md @@ -0,0 +1,85 @@ +# Query Cheatsheet + +Use this file for read-path tasks and debugging checks. + +## Protocol and Config + +```ts +const config = await client.getConfig(); +const usdbType = await client.getUsdbCoinType(); +``` + +## Supported Types + +```ts +const collateralTypes = await client.getAllCollateralTypes(); +const oracleTypes = await client.getAllOracleCoinTypes(); +const psmTypes = Object.keys(await client.getAllPsmPoolObjects()); +const savingLpTypes = Object.keys(await client.getAllSavingPoolObjects()); +``` + +## Market State + +```ts +const usdbSupply = await client.getUsdbSupply(); +const allPrices = await client.getAllOraclePrices(); +const vaults = await client.getAllVaultObjects(); +const psmPools = await client.getAllPsmPoolObjects(); +const savingPools = await client.getAllSavingPoolObjects(); +const flashInfo = await client.getFlashMintInfo(); +``` + +## User State + +```ts +const positions = await client.getUserPositions({ address }); +const savings = await client.getUserSavings({ address }); +const accounts = await client.getUserAccounts({ address }); +``` + +## Rewards + +```ts +const borrowRewards = await client.getAccountBorrowRewards({ + address, + coinTypes: collateralTypes, +}); + +const savingRewards = await client.getAccountSavingPoolRewards({ + address, + lpTypes: savingLpTypes, +}); +``` + +## Position Pagination + +```ts +const page1 = await client.getAllPositions({ coinType, pageSize: 50, cursor: null }); +const page2 = page1.nextCursor + ? await client.getAllPositions({ coinType, pageSize: 50, cursor: page1.nextCursor }) + : null; +``` + +## Error Checks + +If `Unsupported collateral type`: +- compare input with `getAllCollateralTypes()` + +If `Unsupported PSM coin type`: +- compare input with `Object.keys(await client.getAllPsmPoolObjects())` + +If missing price: +- ensure type appears in `getAllOracleCoinTypes()` + +If pool balance issue: +- inspect `getAllPsmPoolObjects()` or `getAllSavingPoolObjects()` + +## Refresh Strategy + +After protocol upgrade or stale data suspicion: + +```ts +await client.refreshConfig(); +``` + +Then re-read supported types and retry. diff --git a/skill/bucket-sdk/scripts/query-state.mjs b/skill/bucket-sdk/scripts/query-state.mjs new file mode 100644 index 0000000..f335514 --- /dev/null +++ b/skill/bucket-sdk/scripts/query-state.mjs @@ -0,0 +1,121 @@ +/** + * Diagnostic script: query Bucket Protocol on-chain state. + * + * Usage (from a project that has @bucket-protocol/sdk installed): + * node /scripts/query-state.mjs + * + * Examples of : + * .github/skills/bucket-sdk + * .agents/skills/bucket-sdk + * .claude/skills/bucket-sdk + * + * Requires: @bucket-protocol/sdk, @mysten/sui + * + * Prints a summary of: + * - USDB total supply + * - Oracle prices for all supported collateral + * - Vault stats (TVL, collateral ratio, interest rate, max supply) + * - PSM pool balances and fee rates + * - Saving pool stats (rate, deposit cap, LP supply) + * - Flash mint info (fee rate) + */ + +import { createRequire } from 'node:module'; + +// Resolve SDK from the caller project (cwd), not from the skill folder location. +const requireFromCwd = createRequire(`${process.cwd()}/package.json`); +const { BucketClient } = requireFromCwd('@bucket-protocol/sdk'); + +function printLine(message = '') { + process.stdout.write(`${message}\n`); +} + +function isDefined(value) { + return value !== null && value !== undefined; +} + +async function main() { + printLine('Initializing BucketClient (mainnet)...'); + printLine(); + const client = await BucketClient.initialize({ network: 'mainnet' }); + + // --- USDB Supply --- + const supply = await client.getUsdbSupply(); + printLine(`USDB Total Supply: ${(Number(supply) / 1e6).toLocaleString()} USDB`); + printLine(); + + // --- Oracle Prices --- + printLine('=== Oracle Prices ==='); + const prices = await client.getAllOraclePrices(); + const sortedPrices = Object.entries(prices).sort(([, a], [, b]) => b - a); + for (const [coinType, price] of sortedPrices) { + const shortName = coinType.split('::').pop() ?? coinType; + printLine(` ${shortName.padEnd(20)} $${price}`); + } + printLine(); + + // --- Vaults --- + printLine('=== Vaults ==='); + const vaults = await client.getAllVaultObjects(); + for (const [coinType, vault] of Object.entries(vaults)) { + const shortName = coinType.split('::').pop() ?? coinType; + printLine(` [${shortName}]`); + printLine(` Min CR: ${vault.minCollateralRatio}`); + printLine(` Interest Rate: ${vault.interestRate}`); + printLine( + ` Max USDB: ${isDefined(vault.maxUsdbSupply) ? (Number(vault.maxUsdbSupply) / 1e6).toLocaleString() : 'unlimited'}`, + ); + printLine(` USDB Minted: ${(Number(vault.usdbSupply) / 1e6).toLocaleString()}`); + printLine(` Collateral: ${vault.collateralBalance}`); + printLine(` Positions: ${vault.positionTableSize}`); + } + printLine(); + + // --- PSM Pools --- + printLine('=== PSM Pools ==='); + const psmPools = await client.getAllPsmPoolObjects(); + for (const [coinType, pool] of Object.entries(psmPools)) { + const shortName = coinType.split('::').pop() ?? coinType; + printLine(` [${shortName}]`); + printLine(` Balance: ${pool.balance}`); + printLine(` Swap-In Fee: ${pool.feeRate.swapIn}`); + printLine(` Swap-Out Fee:${pool.feeRate.swapOut}`); + } + printLine(); + + // --- Saving Pools --- + printLine('=== Saving Pools ==='); + const savingPools = await client.getAllSavingPoolObjects(); + for (const [lpType, pool] of Object.entries(savingPools)) { + const shortName = lpType.split('::').pop() ?? lpType; + printLine(` [${shortName}]`); + printLine(` Saving Rate: ${pool.savingRate}`); + printLine(` LP Supply: ${pool.lpSupply}`); + printLine( + ` Deposit Cap: ${isDefined(pool.usdbDepositCap) ? (Number(pool.usdbDepositCap) / 1e6).toLocaleString() : 'none'}`, + ); + } + printLine(); + + // --- Flash Mint --- + printLine('=== Flash Mint ==='); + const flashMint = await client.getFlashMintInfo(); + printLine(` Fee Rate: ${flashMint.feeRate}`); + printLine(); + + // --- Supported Coin Types --- + printLine('=== Supported Collateral Types ==='); + const collTypes = await client.getAllCollateralTypes(); + for (const ct of collTypes) { + const shortName = ct.split('::').pop() ?? ct; + printLine(` ${shortName}`); + } + printLine(); + + printLine('Done.'); +} + +main().catch((err) => { + console.error('Error:', err); + process.exit(1); +}); diff --git a/skill/bucket-sdk/scripts/query-state.ts b/skill/bucket-sdk/scripts/query-state.ts new file mode 100644 index 0000000..0436f39 --- /dev/null +++ b/skill/bucket-sdk/scripts/query-state.ts @@ -0,0 +1,121 @@ +/** + * Diagnostic script: query Bucket Protocol on-chain state. + * + * Usage (from a project that has @bucket-protocol/sdk installed): + * npx tsx /scripts/query-state.ts + * + * Examples of : + * .github/skills/bucket-sdk + * .agents/skills/bucket-sdk + * .claude/skills/bucket-sdk + * + * Requires: @bucket-protocol/sdk, @mysten/sui + * + * Prints a summary of: + * - USDB total supply + * - Oracle prices for all supported collateral + * - Vault stats (TVL, collateral ratio, interest rate, max supply) + * - PSM pool balances and fee rates + * - Saving pool stats (rate, deposit cap, LP supply) + * - Flash mint info (fee rate) + */ + +import { createRequire } from 'node:module'; + +// Resolve SDK from the caller project (cwd), not from the skill folder location. +const requireFromCwd = createRequire(`${process.cwd()}/package.json`); +const { BucketClient } = requireFromCwd('@bucket-protocol/sdk'); + +function printLine(message = '') { + process.stdout.write(`${message}\n`); +} + +function isDefined(value: T | null | undefined): value is T { + return value !== null && value !== undefined; +} + +async function main() { + printLine('Initializing BucketClient (mainnet)...'); + printLine(); + const client = await BucketClient.initialize({ network: 'mainnet' }); + + // --- USDB Supply --- + const supply = await client.getUsdbSupply(); + printLine(`USDB Total Supply: ${(Number(supply) / 1e6).toLocaleString()} USDB`); + printLine(); + + // --- Oracle Prices --- + printLine('=== Oracle Prices ==='); + const prices = await client.getAllOraclePrices(); + const sortedPrices = Object.entries(prices).sort(([, a], [, b]) => b - a); + for (const [coinType, price] of sortedPrices) { + const shortName = coinType.split('::').pop() ?? coinType; + printLine(` ${shortName.padEnd(20)} $${price}`); + } + printLine(); + + // --- Vaults --- + printLine('=== Vaults ==='); + const vaults = await client.getAllVaultObjects(); + for (const [coinType, vault] of Object.entries(vaults)) { + const shortName = coinType.split('::').pop() ?? coinType; + printLine(` [${shortName}]`); + printLine(` Min CR: ${vault.minCollateralRatio}`); + printLine(` Interest Rate: ${vault.interestRate}`); + printLine( + ` Max USDB: ${isDefined(vault.maxUsdbSupply) ? (Number(vault.maxUsdbSupply) / 1e6).toLocaleString() : 'unlimited'}`, + ); + printLine(` USDB Minted: ${(Number(vault.usdbSupply) / 1e6).toLocaleString()}`); + printLine(` Collateral: ${vault.collateralBalance}`); + printLine(` Positions: ${vault.positionTableSize}`); + } + printLine(); + + // --- PSM Pools --- + printLine('=== PSM Pools ==='); + const psmPools = await client.getAllPsmPoolObjects(); + for (const [coinType, pool] of Object.entries(psmPools)) { + const shortName = coinType.split('::').pop() ?? coinType; + printLine(` [${shortName}]`); + printLine(` Balance: ${pool.balance}`); + printLine(` Swap-In Fee: ${pool.feeRate.swapIn}`); + printLine(` Swap-Out Fee:${pool.feeRate.swapOut}`); + } + printLine(); + + // --- Saving Pools --- + printLine('=== Saving Pools ==='); + const savingPools = await client.getAllSavingPoolObjects(); + for (const [lpType, pool] of Object.entries(savingPools)) { + const shortName = lpType.split('::').pop() ?? lpType; + printLine(` [${shortName}]`); + printLine(` Saving Rate: ${pool.savingRate}`); + printLine(` LP Supply: ${pool.lpSupply}`); + printLine( + ` Deposit Cap: ${isDefined(pool.usdbDepositCap) ? (Number(pool.usdbDepositCap) / 1e6).toLocaleString() : 'none'}`, + ); + } + printLine(); + + // --- Flash Mint --- + printLine('=== Flash Mint ==='); + const flashMint = await client.getFlashMintInfo(); + printLine(` Fee Rate: ${flashMint.feeRate}`); + printLine(); + + // --- Supported Coin Types --- + printLine('=== Supported Collateral Types ==='); + const collTypes = await client.getAllCollateralTypes(); + for (const ct of collTypes) { + const shortName = ct.split('::').pop() ?? ct; + printLine(` ${shortName}`); + } + printLine(); + + printLine('Done.'); +} + +main().catch((err) => { + console.error('Error:', err); + process.exit(1); +});