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

# Building Transactions

> Every flow returns the same pair — getRequirements() to resolve prerequisites, buildTx() to produce the final transaction.

Every state-changing flow on the `client.iris.core(chainId)` entity returns the same shape: `getRequirements()` resolves the on-chain prerequisites, and `buildTx(signatures?)` synchronously encodes the final `Transaction` object (`to`, `value`, `data`, plus a typed `action` discriminator). Building is deterministic — no simulation, no gas estimation, no sending; the SDK produces calldata and your application decides when and how to submit it. Each flow validates locally what the contract would reject — deadlines, bounds, roles, bitmap membership — as typed errors, so bad input fails at build time rather than as a revert.

## How flows are routed

| Flow                              | Route            | Who           |
| --------------------------------- | ---------------- | ------------- |
| `take`                            | Bundler3         | Borrower      |
| `repay`                           | Bundler3         | Anyone        |
| `supplyCollateral` / `supplyBond` | Bundler3         | Anyone        |
| `escape`                          | Bundler3         | Borrower      |
| `refinance`                       | Bundler3         | Solver        |
| `withdrawCollateral`              | Direct Iris call | Borrower      |
| `withdrawBond`                    | Direct Iris call | Solver        |
| `claim`                           | Direct Iris call | Balance owner |

Flows that move tokens **in** run through Bundler3's general adapter: Iris pulls funding atomically with the call, so the transfer and the Iris call must share one transaction — and the bundle is what lets a permit, a Permit2 approval or an Iris authorization signature ride along instead of costing a standalone transaction. Flows that only move tokens **out** call Iris directly: nothing is pulled from the caller, so there is nothing to make atomic and no approval to fold in.

**Accruing pulls are funded with an upper bound.** Iris prices `repay`, `escape` and `refinance` at execution, and the amount owed accrues per second — funding the fetched amount would under-fund the pull and revert. These bundles fund the position projected **two hours** forward and sweep the residual back to the receiver. The projection doubles as the transaction's validity window: rebuild after two hours rather than sending a stale one.

## Requirements

`getRequirements()` returns what must be satisfied before the transaction can land:

* **ERC-20 approval** — approve the general adapter (or, on the Permit2 path, the Permit2 contract) to pull tokens. Returned as a standard `approve` transaction to send first.
* **Permit / Permit2 signature** — off-chain approvals passed into `buildTx` instead of costing a transaction. Enabled by `irisViemExtension({ supportSignature: true })`; pass `useSimplePermit: true` to `getRequirements` to prefer an ERC-2612 permit when the token supports one.
* **Iris authorization** — bundled flows that operate on a user's loan need that user to authorize the general adapter on Iris: `take` and `escape` the borrower's, `refinance` the solver's. Returned as a `setAuthorization` transaction — or, with `supportSignature`, as a signable requirement folded into the bundle — and omitted when already in place.

```typescript theme={null}
import { isRequirementSignature } from "@iris-credit/iris-sdk";

const { buildTx, getRequirements } = iris.take({ userAddress, quote, quoteSignature });

const requirements = await getRequirements();

const signatures = [];
for (const requirement of requirements) {
  if (isRequirementSignature(requirement)) {
    // A signable requirement — a permit / Permit2 allowance or an Iris authorization.
    signatures.push(await requirement.sign(client, userAddress));
  } else {
    // A prerequisite transaction — an ERC-20 approval or the Iris authorization.
    await client.sendTransaction(requirement);
  }
}

const tx = buildTx(signatures);
const hash = await client.sendTransaction(tx);
```

The `isRequirementApproval` and `isRequirementIrisAuthorization` guards narrow the transaction requirements further when the two must be handled differently. `buildTx` consumes at most one permit and one authorization signature — passing several of a kind, or a kind the flow does not consume, throws instead of silently dropping a signed requirement.

<Warning>
  **Builder = signer.** `userAddress` must be the account that signs and sends the transaction. The entity enforces the contract's pinned roles at build time — `take`, `escape` and `withdrawCollateral` require the borrower, `withdrawBond` and `refinance` the solver — and every requirement's `sign(client, userAddress)` rejects a client whose account is not `userAddress`.
</Warning>

### Take

Opens a loan from a solver-signed quote delivered through the [RFQ](/rfq/overview) — the response carries the `quote`, its signature and, for solvers funding bonds per-quote, the `solverPermit2` payload. Validation is local-only: the RFQ already validated the quote, and the contract re-verifies everything at execution.

<CodeGroup>
  ```typescript ERC-20 collateral theme={null}
  const { buildTx, getRequirements } = iris.take({
    userAddress: borrower, // must be quote.borrower.
    quote,
    quoteSignature,
    solverPermit2, // optional — delivered with the quote.
  });

  const requirements = await getRequirements({ useSimplePermit: true });
  const tx = buildTx(signatures);
  ```

  ```typescript Native collateral theme={null}
  // For quotes collateralized in the chain's wNative, part (or all) of the
  // collateral can be paid natively and wrapped in-bundle; the ERC-20 pull
  // covers the remainder and the transaction's `value` carries the native amount.
  const { buildTx, getRequirements } = iris.take({
    userAddress: borrower,
    quote,
    quoteSignature,
    nativeAmount: 1000000000000000000n, // 1 ETH of the collateral, wrapped in-bundle.
  });
  ```
</CodeGroup>

The same `nativeAmount` parameter funds the debt token on `repay` / `escape` / `refinance` and the deposit on `supplyCollateral` / `supplyBond`, always requiring the funded asset to be the chain's wNative.

### Repay

Closes the loan in full — there are no partial repays on Iris. Permissionless: anyone can fund it, and the sweep returns the residual to `userAddress`.

```typescript theme={null}
const positionData = await iris.getPositionData(pod);

const { buildTx, getRequirements } = iris.repay({
  userAddress: payer, // anyone — repay is permissionless; the sweep returns here.
  positionData,
});

const requirements = await getRequirements();
const tx = buildTx([permitSignature]);
```

Repay resolves the loan but leaves the collateral with the position — exit it afterwards with [`escape`](#escape).

### Top-ups

`supplyCollateral` and `supplyBond` are permissionless — anyone can fund either side of a loan:

<CodeGroup>
  ```typescript Supply collateral theme={null}
  const loanData = await iris.getLoanData(pod);

  const { buildTx, getRequirements } = iris.supplyCollateral({
    userAddress: payer, // anyone — the supply is permissionless.
    loanData,
    amount: 50000000n, // 0.5 cbBTC.
  });

  const requirements = await getRequirements();
  const tx = buildTx([permitSignature]);
  ```

  ```typescript Supply bond theme={null}
  const loanData = await iris.getLoanData(pod);

  const { buildTx, getRequirements } = iris.supplyBond({
    userAddress: payer,
    loanData, // the bond is denominated in loanData.debtToken.
    amount: 1_000_000000n, // 1k USDC.
  });

  const requirements = await getRequirements();
  const tx = buildTx([permitSignature]);
  ```
</CodeGroup>

### Withdrawals

Direct Iris calls — no bundler, no approval. Withdrawals are validated locally against the ceilings Iris re-checks onchain, measured with a 0.5% buffer below the relevant LLTV so a withdrawal sized to the fetched state still clears once it lands. `withdrawCollateral` additionally validates the venue's own ceiling, which Iris's does not imply.

<CodeGroup>
  ```typescript Withdraw collateral theme={null}
  import { Time } from "@iris-credit/iris-ts";

  const positionData = await iris.getPositionData(pod);

  const { buildTx } = iris.withdrawCollateral({
    userAddress: borrower, // must be the loan's borrower (or authorized by them on Iris).
    positionData: positionData.accrueLegs(Time.timestamp()).rebase(), // ceilings are measured on it as given.
    amount: 50000000n, // 0.5 cbBTC.
  });

  const tx = buildTx();
  ```

  ```typescript Withdraw bond theme={null}
  const positionData = await iris.getPositionData(pod);

  const { buildTx } = iris.withdrawBond({
    userAddress: solver, // must be the loan's solver (or authorized by them on Iris).
    positionData,
    amount: 1_000_000000n, // 1k USDC.
  });

  const tx = buildTx();
  ```
</CodeGroup>

### Claim

Settlement credits the solver's net and surplus — and the fee recipient's fees — to claimable balances. `claim` draws one down; `amount` defaults to the whole balance.

```typescript theme={null}
const claimableData = await iris.getClaimableData(solver, debtToken);

const { buildTx } = iris.claim({
  userAddress: solver, // whose balance is drawn down and who receives the tokens.
  token: debtToken,
  claimableData,
});

const tx = buildTx();
```

### Escape

Exits the venue position of a **resolved** loan (`bondRequirement === 0n`): the bundle funds any remaining venue debt (projected two hours forward, residual swept back), settles it, and withdraws the venue collateral — yield included — to `userAddress`. After a repay or liquidation the venue usually carries no debt, in which case the funding requirement comes back empty.

```typescript theme={null}
const positionData = await iris.getPositionData(pod);

const { buildTx, getRequirements } = iris.escape({
  userAddress: borrower, // must be the loan's borrower.
  positionData,
});

const requirements = await getRequirements();
const tx = buildTx(signatures);
```

### Refinance

Moves the loan's position to another venue enabled in the loan's `venueBitmap`. The migration is replayed locally to reject what the contract would reject; the bundle then funds the current venue's debt (projected two hours forward), re-enters the new venue, and returns the borrow proceeds plus the swept residual to `userAddress`, leaving it whole.

```typescript theme={null}
import { ChainId, getChainRegistry } from "@iris-credit/core-sdk";

const { venues, marketDatas } = getChainRegistry(ChainId.EthMainnet);

const positionData = await iris.getPositionData(pod);
const newVenue = await iris.getVenueData({
  loanData: positionData.loan,
  venueId: venues.morphoBlue,
  data: marketDatas["morphoBlue:cbBTC/USDC"].data, // the target venue's market data payload.
});

const { buildTx, getRequirements } = iris.refinance({
  userAddress: solver, // must be the loan's solver.
  positionData,
  newVenue,
});

const requirements = await getRequirements();
const tx = buildTx(signatures);
```

<Note>
  Whether the new venue's `data` payload is enabled on Iris is not read here — the contract rejects a disabled payload at execution. Check upfront with `fetchIsDataEnabled` from `@iris-credit/core-sdk`.
</Note>

## Next

<Columns cols={2}>
  <Card title="Solver Signing" icon="signature" href="/sdk/solver-signing">
    The maker-flow counterpart: sign quotes and manage bond allowances.
  </Card>

  <Card title="Entities & Reads" icon="boxes" href="/sdk/entities">
    What the flows' pre-fetched inputs are made of.
  </Card>
</Columns>
