diff --git a/skills/bucket-sdk/SKILL.md b/skills/bucket-sdk/SKILL.md new file mode 100644 index 0000000..3848c8e --- /dev/null +++ b/skills/bucket-sdk/SKILL.md @@ -0,0 +1,421 @@ +--- +name: bucket-sdk +description: Use when integrating with Bucket Protocol on Sui blockchain — building CDP transactions, PSM swaps, saving pool deposits/withdrawals, oracle price queries, flash mints, or querying vault and position data. Also use when the user wants to mint/repay USDB stablecoin, manage collateralized debt positions, swap stablecoins via PSM, or earn yield in Bucket saving pools. Trigger whenever someone needs to use @bucket-protocol/sdk as a dependency. +--- + +# Bucket Protocol SDK — Integration Guide + +`@bucket-protocol/sdk` is a TypeScript SDK for [Bucket Protocol](https://bucketprotocol.io), 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). The SDK lets you query on-chain state and build Programmable Transaction Blocks (PTBs) for CDPs, saving pools (sUSDB + BSR yield), PSM stablecoin swaps, flash mints, oracle price feeds, and one-click leverage. + +**Official docs**: + +## Installation + +```bash +npm install @bucket-protocol/sdk @mysten/sui @mysten/bcs +# or +pnpm add @bucket-protocol/sdk @mysten/sui @mysten/bcs +``` + +Peer dependencies: `@mysten/sui >=2.0.0`, `@mysten/bcs >=2.0.0`. + +## Quick Start + +```typescript +import { BucketClient, coinWithBalance } from '@bucket-protocol/sdk'; +import { Transaction } from '@mysten/sui/transactions'; + +// 1. Initialize (fetches config from chain automatically) +const client = await BucketClient.initialize({ network: 'mainnet' }); + +// 2. Build a transaction +const tx = new Transaction(); +const [, usdbCoin] = await client.buildManagePositionTransaction(tx, { + coinType: '0x2::sui::SUI', + depositCoinOrAmount: 1_000_000_000, // 1 SUI (9 decimals) + borrowAmount: 800_000, // 0.8 USDB (6 decimals) +}); +tx.transferObjects([usdbCoin], myAddress); + +// 3. Sign and execute (with your own signer) +await suiClient.signAndExecuteTransaction({ transaction: tx, signer }); +``` + +## Initialization + +```typescript +import { BucketClient } from '@bucket-protocol/sdk'; +// Custom RPC endpoint +import { SuiGrpcClient } from '@mysten/sui/grpc'; + +// Default — fetch all config from mainnet +const client = await BucketClient.initialize({ network: 'mainnet' }); + +const suiClient = new SuiGrpcClient({ network: 'mainnet', baseUrl: 'https://my-rpc.example.com' }); +const client = await BucketClient.initialize({ suiClient, network: 'mainnet' }); + +// Override specific config (e.g. Pyth Hermes endpoint) +const client = await BucketClient.initialize({ + network: 'mainnet', + configOverrides: { PRICE_SERVICE_ENDPOINT: 'https://custom-hermes.example.com' }, +}); +``` + +`initialize()` reads on-chain config objects to resolve all package IDs, vault/pool/aggregator shared object refs, and Pyth settings. Call `client.refreshConfig()` later to pick up protocol upgrades without re-creating the client. + +## Key Concepts + +### PTB Builders — Build, Don't Execute + +All write operations return PTB commands, not executed transactions. Methods prefixed `build*` take a `Transaction` object and append Move calls. You sign and execute separately. This lets you compose multiple SDK calls into a single atomic transaction. + +### `coinWithBalance` — Automatic Coin Merging + +Pass raw amounts instead of coin objects. The SDK auto-fetches user coins, merges them, and splits the needed amount at `tx.build()` time: + +```typescript +import { coinWithBalance } from '@bucket-protocol/sdk'; + +// These are equivalent: +await client.buildPSMSwapInTransaction(tx, { + coinType: usdcType, + inputCoinOrAmount: 1_000_000, // pass number directly +}); +await client.buildPSMSwapInTransaction(tx, { + coinType: usdcType, + inputCoinOrAmount: coinWithBalance({ type: usdcType, balance: 1_000_000 }), // explicit factory +}); +``` + +### Decimals + +| Token | Decimals | 1 unit in raw | +| ----- | -------- | --------------- | +| SUI | 9 | `1_000_000_000` | +| USDB | 6 | `1_000_000` | +| USDC | 6 | `1_000_000` | +| BTC | 8 | `100_000_000` | + +Always pass amounts in raw (smallest unit). The SDK does not auto-scale. + +### Account vs EOA + +Bucket supports `Account` objects that hold positions on behalf of a wallet. Most builder methods accept an optional `accountObjectOrId` parameter: + +- **Omit** → operates on the wallet address directly (EOA mode) +- **Pass Account object ID** → operates on that Account's positions + +## API Reference + +### Querying On-Chain Data + +All query methods are `async` and read-only — no transaction needed. + +```typescript +// Protocol-level data +const supply = await client.getUsdbSupply(); // bigint +const prices = await client.getOraclePrices({ coinTypes: [SUI_TYPE_ARG] }); // Record +const allPrices = await client.getAllOraclePrices(); // all supported coins + +// Vault data +const vaults = await client.getAllVaultObjects(); // Record +const collateralTypes = client.getAllCollateralTypes(); // string[] + +// Saving pool data +const pools = await client.getAllSavingPoolObjects(); // Record + +// PSM pool data +const psmPools = await client.getAllPsmPoolObjects(); // Record + +// Flash mint info +const flash = await client.getFlashMintInfo(); // { feeRate, partner } + +// User-specific data +const positions = await client.getUserPositions({ address }); // PositionInfo[] +const savings = await client.getUserSavings({ address }); // SavingInfo[] +const accounts = await client.getUserAccounts({ address }); // Account[] + +// Paginated position listing by collateral +const { positions, nextCursor } = await client.getAllPositions({ + coinType: SUI_TYPE_ARG, + pageSize: 50, + cursor: null, +}); + +// Reward queries +const borrowRewards = await client.getAccountBorrowRewards({ + address, + coinTypes: [SUI_TYPE_ARG], +}); // Record> + +const savingRewards = await client.getAccountSavingPoolRewards({ + address, + lpTypes: [susdbLpType], +}); // Record> +``` + +### Building Transactions + +#### CDP — Manage Collateralized Debt Positions + +```typescript +const tx = new Transaction(); + +// Deposit collateral + borrow USDB +const [collateralCoin, usdbCoin] = await client.buildManagePositionTransaction(tx, { + coinType: '0x2::sui::SUI', + depositCoinOrAmount: 2_000_000_000, // 2 SUI + borrowAmount: 1_000_000, // 1 USDB +}); +tx.transferObjects([usdbCoin], myAddress); + +// Repay debt (partial or full) +await client.buildManagePositionTransaction(tx, { + coinType: '0x2::sui::SUI', + repayCoinOrAmount: 500_000, // 0.5 USDB +}); + +// Withdraw collateral (requires sufficient CR) +const [withdrawnCoin] = await client.buildManagePositionTransaction(tx, { + coinType: '0x2::sui::SUI', + withdrawAmount: 500_000_000, // 0.5 SUI +}); +tx.transferObjects([withdrawnCoin], myAddress); + +// Close position entirely — repays all debt, returns all collateral +const [allCollateral] = client.buildClosePositionTransaction(tx, { + address: myAddress, + coinType: '0x2::sui::SUI', +}); +tx.transferObjects([allCollateral], myAddress); + +// Claim borrow incentive rewards +const rewards = client.buildClaimBorrowRewardsTransaction(tx, { + coinType: '0x2::sui::SUI', +}); +// rewards: Record +Object.values(rewards).forEach((coin) => tx.transferObjects([coin], myAddress)); +``` + +**Price is auto-fetched**: `buildManagePositionTransaction` calls `aggregatePrices` internally when `borrowAmount > 0` or `withdrawAmount > 0`. Deposit-only or repay-only operations skip the oracle call. + +#### Saving Pools — Two Layers of USDB Yield + +Bucket's saving system has **two layers**: + +- **Layer 1 — sUSDB (BSR)**: Stake USDB → receive sUSDB. Value appreciates via the Base Savings Rate (BSR) through an increasing exchange rate. `minted_sUSDB = staked_USDB / exchange_rate`. +- **Layer 2 — sUSDB Savings Pool**: Deposit sUSDB → earn additional SUI rewards on top of BSR. Total APR = BSR + SUI rewards APR. + +No stake/unstake fee. No lockup. + +```typescript +const tx = new Transaction(); + +// Deposit USDB +client.buildDepositToSavingPoolTransaction(tx, { + address: myAddress, + lpType: susdbLpType, // saving pool LP type + depositCoinOrAmount: 1_000_000, // 1 USDB +}); + +// Withdraw USDB +const usdbCoin = client.buildWithdrawFromSavingPoolTransaction(tx, { + lpType: susdbLpType, + amount: 500_000, +}); +tx.transferObjects([usdbCoin], myAddress); + +// Claim saving pool rewards +const rewards = client.buildClaimSavingRewardsTransaction(tx, { + lpType: susdbLpType, +}); +Object.values(rewards).forEach((coin) => tx.transferObjects([coin], myAddress)); +``` + +#### PSM — Peg Stability Module Swaps + +```typescript +const tx = new Transaction(); + +// Swap USDC → USDB +const usdbCoin = await client.buildPSMSwapInTransaction(tx, { + coinType: usdcType, + inputCoinOrAmount: 1_000_000, // 1 USDC +}); +tx.transferObjects([usdbCoin], myAddress); + +// Swap USDB → USDC +const usdcCoin = await client.buildPSMSwapOutTransaction(tx, { + coinType: usdcType, + usdbCoinOrAmount: 1_000_000, // 1 USDB +}); +tx.transferObjects([usdcCoin], myAddress); +``` + +#### Flash Mint — Borrow USDB Within a Single Transaction + +```typescript +const tx = new Transaction(); + +// Mint USDB (must be burned in the same tx) +const [usdbCoin, receipt] = client.flashMint(tx, { amount: 10_000_000 }); + +// ... use usdbCoin for arbitrage, liquidation, etc. ... + +// Burn (repay) — must include the fee +client.flashBurn(tx, { usdbCoin: remainingUsdb, flashMintReceipt: receipt }); +``` + +### Composing Multiple Operations + +A key strength of PTBs is atomic composition. You can chain SDK calls in one transaction: + +```typescript +const tx = new Transaction(); + +// 1. Swap USDC to USDB +const usdbCoin = await client.buildPSMSwapInTransaction(tx, { + coinType: usdcType, + inputCoinOrAmount: 1_000_000, +}); + +// 2. Deposit the USDB into a saving pool +client.buildDepositToSavingPoolTransaction(tx, { + address: myAddress, + lpType: susdbLpType, + depositCoinOrAmount: usdbCoin, // pass TransactionResult directly +}); +``` + +#### Leverage via Flash Mint (One-Click Leverage Pattern) + +Bucket supports leveraged positions using flash mints. The one-click flow: + +1. Flash-mint USDB +2. Swap flash-minted USDB → target collateral (via DEX) +3. Deposit all collateral → borrow USDB +4. Repay flash mint with borrowed USDB + +```typescript +const tx = new Transaction(); + +// 1. Flash mint USDB for the leverage amount +const [flashUsdb, receipt] = client.flashMint(tx, { amount: leverageUsdbAmount }); + +// 2. Swap USDB → SUI via external DEX aggregator (e.g. Cetus) +// ... (DEX swap calls — not part of Bucket SDK) ... + +// 3. Deposit all SUI as collateral, borrow USDB to repay flash +const [, borrowedUsdb] = await client.buildManagePositionTransaction(tx, { + coinType: '0x2::sui::SUI', + depositCoinOrAmount: totalSuiCoin, + borrowAmount: leverageUsdbAmount + flashFee, +}); + +// 4. Repay flash mint +client.flashBurn(tx, { usdbCoin: borrowedUsdb, flashMintReceipt: receipt }); +``` + +### Low-Level PTB Helpers + +For advanced use cases, the building blocks behind `build*` methods are also public: + +- **Price**: `aggregateBasicPrices(tx, { coinTypes })` (Pyth-only feeds), `aggregatePrices(tx, { coinTypes })` (all feeds incl. derivatives) → `TransactionResult[]` +- **CDP internals**: `debtorRequest()` → `checkUpdatePositionRequest()` → `updatePosition()` → `checkUpdatePositionResponse()` +- **Saving pool calls**: `savingPoolDeposit()`, `savingPoolWithdraw()`, `checkDepositResponse()`, `checkWithdrawResponse()`, `claimPoolIncentive()` +- **PSM calls**: `psmSwapIn()`, `psmSwapOut()` +- **Object refs**: `treasury(tx)`, `vault(tx, { coinType })`, `aggregator(tx, { coinType })`, `savingPoolObj(tx, { lpType })`, `psmPoolObj(tx, { coinType })`, `vaultRewarderRegistry(tx)`, `savingPoolGlobalConfig(tx)`, `flashGlobalConfig(tx)` +- **Account helpers**: `accountAddress(tx, { address, accountObjectOrId })`, `newAccountRequest(tx, { address, accountObjectOrId })`, `newPriceCollector(tx, { coinType })` + +## Key Types + +```typescript +// Vault info — returned by getAllVaultObjects() +type VaultInfo = { + collateralType: string; + collateralDecimal: number; + collateralBalance: bigint; + usdbSupply: bigint; + maxUsdbSupply: bigint; + minCollateralRatio: number; // e.g. 1.1 = 110% + interestRate: number; + positionTableSize: number; + rewardRate: Record; +}; + +// User position — returned by getUserPositions() +type PositionInfo = { + collateralType: string; + collateralAmount: bigint; + debtAmount: bigint; + debtor: string; + accountId?: string; + rewards?: Record; +}; + +// Saving pool info — returned by getAllSavingPoolObjects() +type SavingPoolInfo = { + lpType: string; + lpSupply: bigint; + usdbBalance: bigint; + usdbDepositCap: bigint | null; + savingRate: number; + rewardRate: Record; +}; + +// PSM pool info — returned by getAllPsmPoolObjects() +type PsmPoolInfo = { + coinType: string; + decimal: number; + balance: bigint; + usdbSupply: bigint; + feeRate: { swapIn: number; swapOut: number }; + partnerFeeRate: Record; +}; + +// User saving — returned by getUserSavings() +type SavingInfo = { + lpType: string; + address: string; + accountId?: string; + usdbBalance: bigint; + lpBalance: bigint; + rewards: Record; +}; + +// Flash mint info — returned by getFlashMintInfo() +type FlashMintInfo = { + feeRate: number; + partner: Record; +}; +``` + +## Common Gotchas + +1. **async vs sync builders**: `buildManagePositionTransaction` and `buildPSMSwap*Transaction` are `async` (they fetch Pyth prices). `buildDepositToSavingPoolTransaction`, `buildWithdrawFromSavingPoolTransaction`, `buildClaimSavingRewardsTransaction`, `buildClosePositionTransaction`, and `buildClaimBorrowRewardsTransaction` are synchronous. + +2. **Zero coin auto-cleanup**: When `borrowAmount` is 0, the returned USDB coin is a zero coin that gets auto-destroyed. Same for collateral when `withdrawAmount` is 0. You don't need to handle this. + +3. **Dry-run / simulation**: Use `suiClient.simulateTransaction({ transaction: tx })` to test without signing. Set `tx.setSender(address)` first. + +4. **Config refresh**: If the protocol upgrades, call `client.refreshConfig()` to re-fetch on-chain config. Overrides from `initialize()` are preserved automatically. + +5. **No borrow fee**: The protocol has **no** one-time borrow fee (removed in the v2 upgrade). Only fixed per-asset interest rates apply. Interest accrues in real time and is added to debt continuously. + +6. **Mint caps**: Each vault has a max USDB supply (`VaultInfo.maxUsdbSupply`). Borrowing beyond this cap will fail on-chain. + +7. **Full liquidation**: When a position's CR drops below MCR, **all collateral is seized** (not partial). Liquidations are protocol-run. User loss ≈ `(MCR - 1) × Debt`. The SDK does not execute liquidations. + +8. **Repay with collateral**: The "repay with collateral" feature (selling collateral to repay debt) is a UI-level feature that routes through on-chain DEXes. The base SDK provides `buildManagePositionTransaction` with `repayCoinOrAmount` for repaying with USDB you already hold. + +9. **sUSDB exchange rate**: sUSDB appreciates vs USDB over time via BSR. When withdrawing, users receive more USDB per sUSDB than they deposited. The exchange rate only increases. + +## Bundled Resources + +This skill includes additional reference files. Consult them when you need detailed data: + +| Resource | Path | When to use | +| ---------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| **Coin Types** | `references/coin-types.md` | Need the full `0x...` type string for a specific token (collateral, PSM, LP, reward) | +| **Protocol Concepts** | `references/protocol-concepts.md` | Need to understand CDP mechanics, PSM vs CDP decision, saving pool yield, flash mints, Account vs EOA, or oracle pricing | +| **Query State Script** | `scripts/query-state.ts` | Need to inspect live on-chain state (vault stats, prices, pool balances). Run with `npx tsx skills/bucket-sdk/scripts/query-state.ts` | diff --git a/skills/bucket-sdk/references/coin-types.md b/skills/bucket-sdk/references/coin-types.md new file mode 100644 index 0000000..4933279 --- /dev/null +++ b/skills/bucket-sdk/references/coin-types.md @@ -0,0 +1,100 @@ +# Bucket Protocol — Supported Coin Types (Mainnet) + +This file lists all coin types supported by Bucket Protocol on Sui mainnet. Use these exact strings when calling SDK methods like `buildManagePositionTransaction`, `buildPSMSwapInTransaction`, etc. + +> **Note**: Coin types may change as the protocol adds or removes support. Call `client.getAllCollateralTypes()`, `client.getAllOracleCoinTypes()`, or inspect `client.getConfig()` for the live list. + +## USDB (Bucket's Stablecoin) + +| Token | Coin Type | Decimals | +| ----- | -------------------------------------------------------------------------------- | -------- | +| USDB | `0xe14726c336e81b32328e92afc37345d159f5b550b09fa92bd43640cfdd0a0cfd::usdb::USDB` | 6 | + +Get it programmatically with `client.getUsdbCoinType()`. + +## Basic Collateral (Direct Pyth Price Feed) + +These tokens have a direct Pyth oracle price. They can be used as CDP collateral and are supported by `aggregateBasicPrices()`. + +| Token | Coin Type | Decimals | Notes | +| ------ | ------------------------------------------------------------------------------------ | -------- | -------------------- | +| SUI | `0x2::sui::SUI` | 9 | Native gas token | +| BTC | `0xaafb102dd0902f5055cadecd687fb5b71ca82ef0e0285d90afde828ec58ca96b::btc::BTC` | 8 | | +| ETH | `0xd0e89b2af5e4910726fbcd8b8dd37bb79b29e5f83f7491bca830e94f7f226d29::eth::ETH` | 8 | | +| WAL | `0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL` | 9 | Walrus token | +| USDC | `0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC` | 6 | | +| USDT | `0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT` | 6 | | +| haSUI | `0xbde4ba4c2e274a60ce15c1cfff9e5c42e41654ac8b6d906a57efa4bd3c29f47d::hasui::HASUI` | 9 | Haedal staked SUI | +| CERT | `0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT` | 9 | Volo staked SUI | +| afSUI | `0xf325ce1300e8dac124071d3152c5c5ee6174914f8bc2161e88329cf579246efc::afsui::AFSUI` | 9 | Aftermath staked SUI | +| SCA | `0x7016aae72cfc67f2fadf55769c0a7dd54291a583b63051a5ed71081cce836ac6::sca::SCA` | 9 | Scallop token | +| BUCK | `0xce7ff77a83ea0cb6fd39bd8748e2ec89a3f41e8efdc3f4eb123e0ca37b184db2::buck::BUCK` | 9 | Bucket V1 stablecoin | +| DEEP | `0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP` | 6 | DeepBook token | +| XBTC | `0x876a4b7bce8aeaef60464c11f4026903e9afacab79b9b142686158aa86560b50::xbtc::XBTC` | 8 | | +| WBTC | `0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC` | 8 | | +| UP_USD | `0x5de877a152233bdd59c7269e2b710376ca271671e9dd11076b1ff261b2fd113c::up_usd::UP_USD` | 6 | Unihouse USD | +| XAUM | `0x9d297676e7a4b771ab023291377b2adfaa4938fb9080b8d12430e4b108b836a9::xaum::XAUM` | 6 | Tokenized gold | + +## Derivative Collateral (Price from Underlying) + +These tokens derive their price from a basic token via an exchange rate (Scallop sCoin, Unihouse gCoin, or BFBTC). The SDK handles this automatically in `aggregatePrices()` — you don't need to treat them differently from basic collateral when using `build*` methods. + +### sCoin (Scallop Lending Receipts) — derivative_kind: `sCoin` + +| Token | Coin Type | Underlying | +| ----- | ------------------------------------------------------------------------------------------------------ | ---------- | +| sSUI | `0xaafc4f740de0dd0dde642a31148fb94517087052f19afb0f7bed1dc41a50c77b::scallop_sui::SCALLOP_SUI` | SUI | +| sUSDC | `0x854950aa624b1df59fe64e630b2ba7c550642e9342267a33061d59fb31582da5::scallop_usdc::SCALLOP_USDC` | USDC | +| sUSDT | `0xb1d7df34829d1513b73ba17cb7ad90c88d1e104bb65ab8f62f13e0cc103783d3::scallop_sb_usdt::SCALLOP_SB_USDT` | USDT | +| sWAL | `0x622345b3f80ea5947567760eec7b9639d0582adcfd6ab9fccb85437aeda7c0d0::scallop_wal::SCALLOP_WAL` | WAL | +| sDEEP | `0xeb7a05a3224837c5e5503575aed0be73c091d1ce5e43aa3c3e716e0ae614608f::scallop_deep::SCALLOP_DEEP` | DEEP | +| sETH | `0xb14f82d8506d139eacef109688d1b71e7236bcce9b2c0ad526abcd6aa5be7de0::scallop_sb_eth::SCALLOP_SB_ETH` | ETH | +| sSCA | `0x5ca17430c1d046fae9edeaa8fd76c7b4193a00d764a0ecfa9418d733ad27bc1e::scallop_sca::SCALLOP_SCA` | SCA | + +### gCoin (Unihouse Staked House Coins) — derivative_kind: `gCoin` + +| Token | Coin Type | Underlying | +| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| gSUI | `0x2f2226a22ebeb7a0e63ea39551829b238589d981d1c6dd454f01fcc513035593::house::StakedHouseCoin<0x2::sui::SUI>` | SUI | +| gUP_USD | `0x2f2226a22ebeb7a0e63ea39551829b238589d981d1c6dd454f01fcc513035593::house::StakedHouseCoin<0x5de877a152233bdd59c7269e2b710376ca271671e9dd11076b1ff261b2fd113c::up_usd::UP_USD>` | UP_USD | + +### BFBTC — derivative_kind: `BFBTC` + +| Token | Coin Type | Underlying | +| ----- | ---------------------------------------------------------------------------------- | ---------- | +| BFBTC | `0x7438e8caf5c345fbd3772517380bf0ca432f53892dee65ee0dda3eb127993cd9::bfbtc::BFBTC` | BTC | + +## PSM Pool Coins + +These coins can be swapped 1:1 with USDB via `buildPSMSwapInTransaction` / `buildPSMSwapOutTransaction`: + +| Token | Coin Type | Decimals | +| ----- | -------------------------------------------------------------------------------- | -------- | +| USDC | `0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC` | 6 | +| USDT | `0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT` | 6 | +| BUCK | `0xce7ff77a83ea0cb6fd39bd8748e2ec89a3f41e8efdc3f4eb123e0ca37b184db2::buck::BUCK` | 9 | + +## Saving Pool LP Types + +Pass these as the `lpType` parameter to saving pool methods (`buildDepositToSavingPoolTransaction`, etc.): + +| Token | LP Type | +| ----- | ---------------------------------------------------------------------------------- | +| sUSDB | `0x38f61c75fa8407140294c84167dd57684580b55c3066883b48dedc344b1cde1e::susdb::SUSDB` | + +## Common Reward Types + +These appear as keys in borrow/saving reward records: + +| Token | Coin Type | +| ----- | -------------------------------------------------------------------------------- | +| SUI | `0x2::sui::SUI` | +| SCA | `0x7016aae72cfc67f2fadf55769c0a7dd54291a583b63051a5ed71081cce836ac6::sca::SCA` | +| DEEP | `0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP` | +| XAUM | `0x9d297676e7a4b771ab023291377b2adfaa4938fb9080b8d12430e4b108b836a9::xaum::XAUM` | + +## Protocol Token + +| Token | Coin Type | Decimals | Notes | +| ----- | ------------------------------------------------------------------------------ | -------- | ------------------------------------------ | +| BUT | `0xbc858cb910b9914bee64fff0f9b38855355a040c49155a17b265d9086d256545::but::BUT` | 9 | Native protocol token; stakeable for deBUT | diff --git a/skills/bucket-sdk/references/protocol-concepts.md b/skills/bucket-sdk/references/protocol-concepts.md new file mode 100644 index 0000000..790b185 --- /dev/null +++ b/skills/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] = client.flashMint(tx, { amount: 10_000_000 }); +// ... do things with usdbCoin (arbitrage, etc.) ... +// Must return principal + fee: +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, +}); +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] = client.flashMint(tx, { amount: 10_000_000 }); +// ... use usdb for arbitrage on a DEX ... +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] = 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 +client.flashBurn(tx, { usdbCoin: borrowedUsdb, flashMintReceipt: receipt }); +``` diff --git a/skills/bucket-sdk/scripts/query-state.ts b/skills/bucket-sdk/scripts/query-state.ts new file mode 100644 index 0000000..c192d30 --- /dev/null +++ b/skills/bucket-sdk/scripts/query-state.ts @@ -0,0 +1,112 @@ +/** + * Diagnostic script: query Bucket Protocol on-chain state. + * + * Usage (from project root, or any project that has @bucket-protocol/sdk installed): + * npx tsx skills/bucket-sdk/scripts/query-state.ts + * + * 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 { BucketClient } from '@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 = 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); +});