# Split / Merge / Redeem

> Build, sign, and submit Safe transactions for on-chain token operations.

All four operations follow the same pattern: build the transaction, have the user sign it, then submit.

## Common Signing Pattern

```typescript
import { BuilderClient, UserClient } from '@opinion-labs/opinion-clob-sdk';

const builder = new BuilderClient({
  host: 'https://openapi.opinion.trade/openapi',
  builderApiKey: 'YOUR_BUILDER_KEY',
  chainId: 56,
  rpcUrl: 'https://bsc-dataseed.binance.org',
});

// Build any Safe TX (split, merge, redeem, or withdraw)
const txResult = await builder.buildSplitTx(safeAddress, collateral, conditionId, amount);

// User signs the EIP-712 typed data
const user = new UserClient('0xUserPrivateKey...');
const signature = await user.signTypedData(txResult.eip712Data);

// Submit to backend for relay
const result = await builder.submitSafeTx(userAddress, txResult, signature);
```

## Split Position

Convert quote tokens (e.g., USDC) into Yes and No outcome tokens.

```typescript
const txResult = await builder.buildSplitTx(
  safeAddress,          // User's Safe wallet address
  collateralToken,      // Quote token address (e.g., USDC)
  conditionId,          // Market condition ID (from getMarket())
  1000000n,             // Amount in wei (e.g., 1 USDC = 1000000 with 6 decimals)
);
```

| Parameter         | Type      | Required | Description                                              |
| ----------------- | --------- | -------- | -------------------------------------------------------- |
| `safeAddress`     | string    | Yes      | User's Safe wallet address                               |
| `collateralToken` | string    | Yes      | Quote token contract address                             |
| `conditionId`     | string    | Yes      | Market condition ID                                      |
| `amount`          | bigint    | Yes      | Amount in wei                                            |
| `partition`       | number\[] | No       | Outcome partition (default: `[1, 2]` for binary markets) |

## Merge Position

Convert equal amounts of Yes and No tokens back into quote tokens.

```typescript
const txResult = await builder.buildMergeTx(
  safeAddress,
  collateralToken,
  conditionId,
  1000000n,
);
```

| Parameter         | Type      | Required | Description                           |
| ----------------- | --------- | -------- | ------------------------------------- |
| `safeAddress`     | string    | Yes      | User's Safe wallet address            |
| `collateralToken` | string    | Yes      | Quote token contract address          |
| `conditionId`     | string    | Yes      | Market condition ID                   |
| `amount`          | bigint    | Yes      | Amount in wei                         |
| `partition`       | number\[] | No       | Outcome partition (default: `[1, 2]`) |

## Redeem Position

Claim winnings from a resolved market. Converts winning outcome tokens back to quote tokens.

```typescript
const txResult = await builder.buildRedeemTx(
  safeAddress,
  collateralToken,
  conditionId,
);
```

| Parameter         | Type      | Required | Description                           |
| ----------------- | --------- | -------- | ------------------------------------- |
| `safeAddress`     | string    | Yes      | User's Safe wallet address            |
| `collateralToken` | string    | Yes      | Quote token contract address          |
| `conditionId`     | string    | Yes      | Market condition ID                   |
| `partition`       | number\[] | No       | Outcome partition (default: `[1, 2]`) |

## Withdraw Tokens

Transfer ERC20 tokens from the Safe wallet to any address.

```typescript
const txResult = await builder.buildWithdrawTx(
  safeAddress,          // From: user's Safe wallet
  tokenAddress,         // ERC20 token to withdraw
  1000000n,             // Amount in wei
  recipientAddress,     // To: destination address
);
```

| Parameter      | Type   | Required | Description                  |
| -------------- | ------ | -------- | ---------------------------- |
| `safeAddress`  | string | Yes      | User's Safe wallet address   |
| `tokenAddress` | string | Yes      | ERC20 token contract address |
| `amount`       | bigint | Yes      | Amount in wei                |
| `toAddress`    | string | Yes      | Recipient address            |

## Submit Safe Transaction

All build methods return a `SafeTxResult`. Submit it with a user signature:

```typescript
const result = await builder.submitSafeTx(userAddress, txResult, signature);
```

| Parameter       | Type         | Required | Description                            |
| --------------- | ------------ | -------- | -------------------------------------- |
| `walletAddress` | string       | Yes      | User's wallet address (Safe owner EOA) |
| `safeTxResult`  | SafeTxResult | Yes      | Result from any `build*Tx()` method    |
| `signature`     | string       | Yes      | User's signature on the EIP-712 data   |

## Test Helper

For testing, sign Safe transactions with a private key:

```typescript
const signature = await BuilderClient.signSafeTxWithPrivateKey(
  txResult.eip712Data,
  '0xPrivateKey...',
);
```

## Notes

* All Safe TX operations require `rpcUrl` in the BuilderClient config.
* Transactions are relayed by the backend -- no gas required from the user.
* Safe TX signing uses `user.signTypedData(eip712Data)`, not `signHash()`.
* For amount conversions, use `safeAmountToWei()` from the SDK:

  ```typescript
  import { safeAmountToWei } from '@opinion-labs/opinion-clob-sdk';
  const weiAmount = safeAmountToWei(5.0, 6); // 5 USDC -> 5000000n
  ```
* The `conditionId` for split, merge, and redeem is available from `getMarket()`.
* Redeem only works on resolved markets with winning outcome tokens.
