@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
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
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.
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.
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.
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).
const currentCalls = builder.calls();
console.log(`${currentCalls.length} calls queued`);
Returns
Call[]
send()
Execute all batched calls atomically.
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)
Execute via Chipi’s paymaster (gasless). Requires a PaymasterAdapter in the constructor.
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.
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.
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
const result = await new TxBuilder(account)
.transfer(USDC_ADDRESS, [{ to: recipient, amount: 1_500_000n }])
.send();
Approve + Swap (Atomic Batch)
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
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
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();
Gasless with Argent X / Braavos (browser)
import { connect } from "starknetkit";
import { TxBuilder } from "@chipi-stack/core";
import { ChipiBrowserSDK } from "@chipi-stack/backend";
const { wallet } = await connect();
const sdk = new ChipiBrowserSDK({ apiPublicKey: "pk_..." });
const paymaster = sdk.createPaymasterAdapter(userBearerToken);
// wallet.account already has the Argent/Braavos signer — triggers popup on sign
const txHash = await new TxBuilder(wallet.account, { paymaster })
.transfer(USDC, [{ to: recipient, amount: 1_000_000n }])
.sendSponsored(); // popup -> sign -> gasless execution
Gasless with Cartridge Controller
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)
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
- Amount: Type-safe token amounts for use with TxBuilder
- Erc20: Generate typed Call objects for approve/transfer
- SignerAdapter: Different signing strategies for the Account