Skip to main content

useScaffoldMultiWriteContract

Use this hook to execute multiple contract calls in a single transaction batch. This is more gas-efficient than sending individual transactions and ensures atomicity - either all calls succeed or none do.

const { sendAsync, isLoading } = useScaffoldMultiWriteContract({
calls: [
{
contractName: "Token",
functionName: "approve",
args: [spenderAddress, amount],
},
{
contractName: "DEX",
functionName: "swap",
args: [tokenIn, tokenOut, amount],
},
],
});

// Execute all calls in a single transaction
await sendAsync();

Configurationโ€‹

ParameterTypeDescription
callsArray<ScaffoldWriteConfig | Call>Array of contract calls to execute. Can be scaffold config objects or raw Starknet Call objects.
options (optional)InvocationsDetailsAdditional transaction options (max fee, nonce, etc.)

Call Configuration Objectโ€‹

Each call in the calls array can be:

Scaffold Config Format:

{
contractName: string; // Name of deployed contract
functionName: string; // External function to call
args: unknown[]; // Function arguments
}

Raw Call Format (Starknet.js):

{
contractAddress: string; // Contract address
entrypoint: string; // Function selector
calldata: string[]; // Encoded calldata
}

Return Valuesโ€‹

PropertyTypeDescription
sendAsync() => Promise<string | undefined>Function to execute all calls. Returns transaction hash.
isLoadingbooleantrue while the transaction is being processed
errorError | nullError object if the transaction failed, null otherwise
status"idle" | "loading" | "success" | "error"Current transaction state

Usage Examplesโ€‹

Approve and Swapโ€‹

A common DeFi pattern - approve token spending and swap in one transaction:

import { View, Button, Alert } from "react-native";
import { useScaffoldMultiWriteContract } from "@/hooks/scaffold-stark";

export default function SwapButton({ amount, tokenIn, tokenOut, dexAddress }) {
const { sendAsync, isLoading } = useScaffoldMultiWriteContract({
calls: [
{
contractName: "ERC20",
functionName: "approve",
args: [dexAddress, amount],
},
{
contractName: "DEX",
functionName: "swap",
args: [tokenIn, tokenOut, amount, 0], // minAmountOut = 0
},
],
});

const handleSwap = async () => {
try {
const txHash = await sendAsync();
Alert.alert("Success", `Swap completed: ${txHash}`);
} catch (error) {
Alert.alert("Error", error.message);
}
};

return (
<Button
title={isLoading ? "Swapping..." : "Approve & Swap"}
onPress={handleSwap}
disabled={isLoading}
/>
);
}

Batch Transfersโ€‹

Send tokens to multiple recipients in one transaction:

import { View, Text, Button } from "react-native";
import { useScaffoldMultiWriteContract, createContractCall } from "@/hooks/scaffold-stark";

export default function BatchTransfer({ recipients }) {
// recipients = [{ address: "0x...", amount: 100n }, ...]

const calls = recipients.map((r) =>
createContractCall("Token", "transfer", [r.address, r.amount])
);

const { sendAsync, isLoading, status } = useScaffoldMultiWriteContract({
calls,
});

return (
<View>
<Button
title={isLoading ? "Sending..." : `Send to ${recipients.length} recipients`}
onPress={() => sendAsync()}
disabled={isLoading}
/>
{status === "success" && (
<Text>All transfers completed!</Text>
)}
</View>
);
}

Mixed Call Formatsโ€‹

Combine scaffold config and raw calls:

import { Button } from "react-native";
import { useScaffoldMultiWriteContract } from "@/hooks/scaffold-stark";

export default function MixedCalls() {
const { sendAsync, isLoading } = useScaffoldMultiWriteContract({
calls: [
// Scaffold config format
{
contractName: "YourContract",
functionName: "doSomething",
args: [123],
},
// Raw Starknet.js Call format
{
contractAddress: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
entrypoint: "transfer",
calldata: ["0x123...", "1000", "0"],
},
],
});

return (
<Button
title="Execute Mixed Calls"
onPress={() => sendAsync()}
disabled={isLoading}
/>
);
}

NFT Minting with Metadataโ€‹

Mint an NFT and set its metadata atomically:

import { useState } from "react";
import { View, TextInput, Button } from "react-native";
import { useScaffoldMultiWriteContract } from "@/hooks/scaffold-stark";

export default function MintWithMetadata() {
const [tokenUri, setTokenUri] = useState("");

const { sendAsync, isLoading } = useScaffoldMultiWriteContract({
calls: [
{
contractName: "NFT",
functionName: "mint",
args: [],
},
{
contractName: "NFT",
functionName: "setTokenURI",
args: [nextTokenId, tokenUri], // nextTokenId from a useScaffoldReadContract call
},
],
});

return (
<View style={{ gap: 12 }}>
<TextInput
placeholder="Token URI"
value={tokenUri}
onChangeText={setTokenUri}
style={{ borderWidth: 1, padding: 8 }}
/>
<Button
title={isLoading ? "Minting..." : "Mint NFT"}
onPress={() => sendAsync()}
disabled={isLoading || !tokenUri}
/>
</View>
);
}

Helper Functionโ€‹

createContractCallโ€‹

A helper function to create properly typed call objects:

import { createContractCall } from "@/hooks/scaffold-stark";

const call = createContractCall("Token", "transfer", [recipient, amount]);

The function takes three separate arguments: contractName, functionName, and args. This is useful when building calls dynamically or in loops.

Atomicityโ€‹

All calls in a multi-write transaction are atomic:

  • If all calls succeed, the transaction is confirmed
  • If any call fails, the entire transaction reverts
  • No partial execution is possible

This is critical for operations that must happen together (like approve + swap).

Gas Efficiencyโ€‹

Batching multiple calls into a single transaction is more gas-efficient than sending them separately:

ApproachTransaction CountGas Overhead
Individual transactionsNN ร— base fee
Multi-write (batched)11 ร— base fee

Differences from Web Versionโ€‹

The React Native version is functionally identical to the web version. The main differences are:

  • Uses React Native UI components
  • Integrates with mobile wallet (Cavos Aegis)
  • Mobile-optimized toast notifications