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

# TxBuilder

> Fluent builder for batching StarkNet multicalls into atomic transactions with fee estimation.

> [!WARNING]
>
> `@chipi-stack/core` is in development (v0.1.0). APIs may change before stable release.

## Overview

`TxBuilder` provides a fluent API for composing multiple StarkNet calls into a single atomic transaction. All calls execute together via `Account.execute()`, which is more gas-efficient than separate transactions (important when sponsoring gas via a paymaster).

## Usage

```typescript theme={null}
import { TxBuilder } from "@chipi-stack/core";
import type { Account } from "starknet";

const result = await new TxBuilder(account)
  .approve(tokenAddr, spenderAddr, amount)
  .transfer(tokenAddr, [{ to: recipient, amount: 1_000_000n }])
  .send();

console.log(result.transaction_hash);
```

## Constructor

```typescript theme={null}
new TxBuilder(account: Account, opts?: { paymaster?: PaymasterAdapter })
```

### Parameters

* `account` (Account): A starknet.js `Account` instance
* `opts.paymaster` (PaymasterAdapter, optional): Adapter for gasless transactions via `sendSponsored()`

## Methods

### `add(call)`

Add one or more raw StarkNet calls to the batch.

```typescript theme={null}
builder.add({
  contractAddress: "0x...",
  entrypoint: "swap",
  calldata: [tokenIn, tokenOut, amountIn, "0"],
});

// Or add multiple at once
builder.add([call1, call2, call3]);
```

#### Parameters

* `call` (Call | Call\[]): A single Call or array of Calls

#### Returns

`this` (chainable)

### `approve(token, spender, amount)`

Add an ERC20 approve call. Handles uint256 encoding automatically.

```typescript theme={null}
builder.approve(USDC_ADDRESS, DEX_ADDRESS, 1_000_000n);
```

#### Parameters

* `token` (string): Token contract address
* `spender` (string): Address to approve
* `amount` (bigint): Raw amount in base units

#### Returns

`this` (chainable)

### `transfer(token, targets)`

Add one or more ERC20 transfer calls.

```typescript theme={null}
builder.transfer(USDC_ADDRESS, [
  { to: "0xalice...", amount: 500_000n },
  { to: "0xbob...", amount: 300_000n },
]);
```

#### Parameters

* `token` (string): Token contract address
* `targets` (TransferTarget\[]): Array of `{ to: string, amount: bigint }` pairs

#### Returns

`this` (chainable)

### `calls()`

Returns a copy of the current call batch (for inspection).

```typescript theme={null}
const currentCalls = builder.calls();
console.log(`${currentCalls.length} calls queued`);
```

#### Returns

`Call[]`

### `send()`

Execute all batched calls atomically.

```typescript theme={null}
const result = await builder.send();
console.log(result.transaction_hash);
```

#### Returns

`Promise<InvokeFunctionResponse>`

#### Throws

* If no calls have been added
* If `send()` was already called (builders are single-use)

### `sendSponsored()`

Execute via Chipi's paymaster (gasless). Requires a `PaymasterAdapter` in the constructor.

```typescript theme={null}
const txHash = await builder.sendSponsored();
console.log(txHash); // "0x..."
```

#### Returns

`Promise<string>` (transaction hash)

#### Throws

* If no `paymaster` was configured in the constructor
* If no calls have been added
* If `send()` or `sendSponsored()` was already called (builders are single-use)

### `estimateFee()`

Estimate the fee for the current batch without executing.

```typescript theme={null}
const fee = await builder.estimateFee();
console.log(`Estimated fee: ${fee.overall_fee} wei`);
```

#### Returns

`Promise<EstimateFeeResponse>`

#### Throws

* If no calls have been added

### `preflight()`

Simulate the transaction without submitting. Returns the transaction trace and fee estimation. Useful for checking if a transaction would succeed before sending.

```typescript theme={null}
const sim = await builder.preflight();
console.log(`Would cost: ${sim.fee_estimation.overall_fee} wei`);
```

#### Returns

`Promise<PreflightResult>` with `transaction_trace` and `fee_estimation`

#### Throws

* If no calls have been added

## Examples

### Simple Transfer

```typescript theme={null}
const result = await new TxBuilder(account)
  .transfer(USDC_ADDRESS, [{ to: recipient, amount: 1_500_000n }])
  .send();
```

### Approve + Swap (Atomic Batch)

```typescript theme={null}
const result = await new TxBuilder(account)
  .approve(USDC_ADDRESS, DEX_ADDRESS, 1_000_000n)
  .add({
    contractAddress: DEX_ADDRESS,
    entrypoint: "swap",
    calldata: [USDC_ADDRESS, ETH_ADDRESS, "1000000", "0"],
  })
  .send();
```

### Fee Preview Before Sending

```typescript theme={null}
const builder = new TxBuilder(account)
  .transfer(USDC_ADDRESS, [{ to, amount }]);

const fee = await builder.estimateFee();
console.log(`This will cost: ${fee.overall_fee} wei`);

// User confirms, create new builder to send
const result = await new TxBuilder(account)
  .transfer(USDC_ADDRESS, [{ to, amount }])
  .send();
```

### With Erc20 and Amount

```typescript theme={null}
import { TxBuilder, Erc20, Amount, TokenRegistry } from "@chipi-stack/core";

const registry = new TokenRegistry("mainnet");
const usdcInfo = registry.bySymbol("USDC")!;
const usdc = new Erc20(usdcInfo, account);
const amount = Amount.parse("50.0", usdcInfo);

const result = await new TxBuilder(account)
  .add(usdc.approveCall(spender, amount))
  .add(usdc.transferCall(recipient, amount))
  .send();
```

### With Bramble (browser wallet)

The user's own wallet signs and pays gas. starknet.js's `WalletAccount` wraps the wallet as an `Account`; Bramble injects it as `window.starknet_bramble`.

```typescript theme={null}
import { RpcProvider, WalletAccount } from "starknet";
import { TxBuilder } from "@chipi-stack/core";

declare const USDC: string;
declare const recipient: string;

const bramble = (window as unknown as { starknet_bramble?: WalletAccount["walletProvider"] })
  .starknet_bramble;
if (!bramble) throw new Error("Bramble is not installed");

// From a click: Bramble opens its panel only during a user gesture.
// connect() sends wallet_requestAccounts with { silent_mode: false }.
const account = await WalletAccount.connect(
  new RpcProvider({ nodeUrl: "https://api.cartridge.gg/x/starknet/mainnet" }),
  bramble
);

// send() calls wallet_addInvokeTransaction with { calls } and nothing else.
// Bramble estimates the fee, the user approves it, and the account pays in STRK.
const { transaction_hash } = await new TxBuilder(account)
  .transfer(USDC, [{ to: recipient, amount: 1_000_000n }])
  .send();
```

* Bramble parses params strictly: a fee, `max_fee` or paymaster field fails with `114`. `send()` adds none (`WalletAccount.execute` sends only `{ calls }`). Keep a batch to 16 calls, and pass contract addresses as lowercase hex.
* A new OpenZeppelin or Argent account is deployed in the same flow as its first transaction. An undeployed Braavos account is refused.
* Braavos and Ready work the same way through their own injected objects. With starknetkit, add Bramble as an `InjectedConnector` with id `"bramble"`; it is not in the default list.
* A transaction hash is not a result: wait for the receipt (`provider.waitForTransaction`).

### Can Bramble use `sendSponsored()`?

Bramble's guide says dApp-sponsored (paymaster) transactions are not supported through its public provider: `wallet_addInvokeTransaction` refuses paymaster fields. `sendSponsored()` takes a different path. With Chipi's adapter (`sdk.createPaymasterAdapter`) it:

1. reads the account's class hash with `getClassHashAt`, so the account must already be deployed;
2. asks Chipi's backend for SNIP-9 outside-execution typed data for the calls;
3. has the wallet sign it with `signMessage` (`wallet_signTypedData`) and keeps `{ r, s }`: the two felts of `[r, s]`, or the last two of Argent's `["1", "0", publicKey, r, s]` (14.17.0+; earlier versions sent `r = 1, s = 0` for that shape);
4. has Chipi's paymaster relay it, paying the gas.

So it needs a deployed account whose class supports outside execution and accepts `[r, s]` from its key. Bramble reviews an outside-execution typed data like an invoke. This path has **not been verified with Bramble or Ready**: until it is, use `send()`.

### Gasless with Cartridge Controller

```typescript theme={null}
import Controller from "@cartridge/controller";
import { TxBuilder } from "@chipi-stack/core";
import { ChipiBrowserSDK } from "@chipi-stack/backend";

const controller = new Controller();
await controller.connect();

const sdk = new ChipiBrowserSDK({ apiPublicKey: "pk_..." });
const paymaster = sdk.createPaymasterAdapter(userBearerToken);

// controller IS Account-compatible
const txHash = await new TxBuilder(controller, { paymaster })
  .transfer(USDC, [{ to, amount }])
  .sendSponsored();
```

### Backend Automation (gasless)

```typescript theme={null}
import { createAccount, DirectSigner, TxBuilder } from "@chipi-stack/core";
import { ChipiServerSDK } from "@chipi-stack/backend";

const sdk = new ChipiServerSDK({ apiPublicKey: "pk_...", apiSecretKey: "sk_..." });
const paymaster = sdk.createPaymasterAdapter();

const provider = { nodeUrl: "https://starknet-mainnet.infura.io/v3/YOUR_KEY" };
const accountAddress = "0xYOUR_ACCOUNT_ADDRESS";
const signer = new DirectSigner(process.env.AUTOMATION_KEY!, accountAddress);
const account = await createAccount({ signer, provider });

const txHash = await new TxBuilder(account, { paymaster })
  .approve(USDC, spender, amount)
  .transfer(USDC, [{ to, amount }])
  .sendSponsored(); // gasless, no popup
```

## Related

* [Amount](/sdk/core/amount): Type-safe token amounts for use with TxBuilder
* [Erc20](/sdk/core/erc20): Generate typed Call objects for approve/transfer
* [SignerAdapter](/sdk/core/signer-adapter): Different signing strategies for the Account


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.