> ## Documentation Index
> Fetch the complete documentation index at: https://docs.darkpool.fun/llms.txt
> Use this file to discover all available pages before exploring further.

# PTB Builders

> Every Move-call builder the UI signs lives in lib/ptb.ts.

All PTB construction lives in `packages/frontend/src/lib/ptb.ts`. Consumers receive a fully-built `Transaction` they can hand to `useTxSigner().sign()`.

## Predict

```typescript theme={"system"}
buildCreatePredictManagerPTB(): Transaction;

buildDepositToPredictManagerPTB({
  managerId: string;
  amountMicro: bigint;
}): Transaction;

buildWithdrawFromPredictManagerPTB({
  managerId: string;
  amountMicro: bigint;
}): Transaction;

buildPredictMintBinaryPTB({
  managerId: string;
  oracleId: string;
  expiryMs: bigint;
  strike: bigint;
  isUp: boolean;
  costMicro: bigint;
  minQty: bigint;
}): Transaction;

buildPredictMintRangePTB({ … }): Transaction;

buildPredictRedeemPTB({ managerId, oracleId, … }): Transaction;
```

The mint PTB asserts the on-chain ask hasn't moved more than 105% of the quote before submitting (avoids `decrease_position` code 3).

## Binary markets (scaffolded)

```typescript theme={"system"}
buildMintPairPTB({
  settlementId: string;
  packageId: string;
  amountMicro: bigint;
}): Transaction;

buildBurnPairPTB({ settlementId, packageId, yesCoinIds, noCoinIds }): Transaction;
buildRedeemYesPTB({ … }): Transaction;
buildRedeemNoPTB({ … }): Transaction;
```

All merge multiple coin objects per leg into one PTB so the user can redeem a basket in a single tx.

## DeepBook V3 swap

```typescript theme={"system"}
/**
 * Market buy/sell via pool::swap_exact_quantity with pay_with_deep=false.
 * Fees in the input token; 1% slippage floor.
 */
buildV3SwapPTB({
  poolId: string;
  baseType: string;          // YES type
  quoteType: string;         // DUSDC
  side: 'buy' | 'sell';
  inputAmountMicro: bigint;
  expectedOutMicro: bigint;
  slippageBps: number;        // default 100 = 1%
}): Transaction;
```

## Dark-pool vault

```typescript theme={"system"}
buildVaultDepositPTB({
  vaultId: string;
  amountMicro: bigint;
  accessPolicyId: string;
  encryptedBlobId: Uint8Array;     // or empty
  attributionHash: Uint8Array;     // 32 bytes (zero-padded when Seal is off)
}): Transaction;

buildVaultRedeemPTB({
  vaultId: string;
  receiptId: string;
}): Transaction;
```

## Agent wizard

Not a single builder. The `/agents` Create wizard inlines the PTB:

```typescript theme={"system"}
const tx = new Transaction();
const [bm] = tx.moveCall({ target: 'balance_manager::new' });
tx.moveCall({ target: 'balance_manager::share', arguments: [bm] });
const [tradeCap] = tx.moveCall({
  target: 'balance_manager::mint_trade_cap',
  arguments: [bm, owner],
});
// optional DUSDC deposit:
if (depositCoin) tx.moveCall({ target: 'balance_manager::deposit<DUSDC>', arguments: [bm, depositCoin] });
const [agent, ownerCap] = tx.moveCall({
  target: `${DARKPOOL_PACKAGE_ID}::agent::register_agent`,
  arguments: [registry, name, strategyBlob, agentAddr, bm, tradeCap, predictManagerId],
});
tx.transferObjects([tradeCap], operatorAddress);
tx.transferObjects([agent, ownerCap], ownerAddress);
```

Dry-run-verified on testnet (creates BalanceManager, TradeCap, TradeAgent, AgentOwnerCap, registry AgentSummary).

## Optimistic Oracle

```typescript theme={"system"}
buildOOProposePTB({ marketLinkId, outcome, bondMicro }): Transaction;
buildOODisputePTB({ proposalId, bondMicro }): Transaction;
buildOOFinalizeAutoPTB({ proposalId }): Transaction;
buildOOFinalizeVotePTB({ proposalId }): Transaction;
```

Latest finalize\_vote matches the multi-return pattern from `scripts/oo-finalize.ts`, drops the address return, and `transferObjects`-es both bond coins to sender.

## Send DUSDC (zkLogin onramp / offramp)

Inlined in `SendDusdcCard.tsx`:

```typescript theme={"system"}
const coins = await sui.getCoins({ owner: account.address, coinType: DUSDC_COIN_TYPE });
const sorted = coins.data.sort((a, b) => Number(BigInt(b.balance) - BigInt(a.balance)));
const tx = new Transaction();
const primary = tx.object(sorted[0].coinObjectId);
if (primary.balance < amount) tx.mergeCoins(primary, sorted.slice(1).map(c => tx.object(c.coinObjectId)));
const [out] = tx.splitCoins(primary, [amount]);
tx.transferObjects([out], recipient);
```

Flows through `useTxSigner()` so gas is sponsored when allowlisted.
