Batching Actions Within a Contract
While transactions to different contracts are independent, you can batch multiple actions targeting the same contract into a single transaction. If any action in the batch fails, they all get reverted.
Why Batch Actions?
Batching ensures atomicity - either all actions succeed or none do. Common use cases include:
- Registering a user and transferring tokens in one transaction
- Approving and transferring NFTs atomically
- Multiple sequential contract calls that must all succeed
Action Types
NEAR supports several action types:
FunctionCall- Call a contract methodTransfer- Send NEAR tokensCreateAccount- Create a new accountAddKey- Add an access keyDeleteKey- Remove an access keyDeleteAccount- Delete an account
Batching Actions Example
Here's a practical example of registering a user for FT storage and transferring tokens:
const REGISTER_DEPOSIT = "1250000000000000000000"; // 0.00125 NEAR
const THIRTY_TGAS = "30000000000000";
const ftTx = {
receiverId: "token.near",
actions: [
// First action: Register storage for receiver
{
type: 'FunctionCall',
params: {
methodName: 'storage_deposit',
args: { account_id: "alice.near" },
gas: THIRTY_TGAS,
deposit: REGISTER_DEPOSIT
}
},
// Second action: Transfer tokens
{
type: 'FunctionCall',
params: {
methodName: 'ft_transfer',
args: {
receiver_id: "alice.near",
amount: "1000000000000000000" // 1 token
},
gas: THIRTY_TGAS,
deposit: "1" // 1 yoctoNEAR required by standard
}
}
]
};
await wallet.signAndSendTransactions({ transactions: [ftTx] });
Execution Flow
Actions execute sequentially within the transaction:
storage_depositregisters alice.near for FT storage- If successful,
ft_transfertransfers tokens to alice.near - If either fails, both revert
Example: Guest Book Actions
The Guest Book transaction in our example already uses action batching:
const guestTx = {
receiverId: GUEST_ADDRESS,
actions: [
{
type: 'FunctionCall',
params: {
methodName: 'add_message',
args: { text: greeting.value },
gas: THIRTY_TGAS,
deposit: GUEST_DEPOSIT
}
}
// You could add more actions here targeting the same contract
]
};
While this example has only one action, you could add more:
const guestTx = {
receiverId: GUEST_ADDRESS,
actions: [
{
type: 'FunctionCall',
params: {
methodName: 'add_message',
args: { text: "First message" },
gas: THIRTY_TGAS,
deposit: NO_DEPOSIT
}
},
{
type: 'FunctionCall',
params: {
methodName: 'add_message',
args: { text: "Second message" },
gas: THIRTY_TGAS,
deposit: NO_DEPOSIT
}
}
]
};
Both messages get added, or neither does.
Key Differences
Multiple Transactions (Independent):
const tx1 = { receiverId: "contract1.near", actions: [...] };
const tx2 = { receiverId: "contract2.near", actions: [...] };
await wallet.signAndSendTransactions({ transactions: [tx1, tx2] });
// If tx1 fails, tx2 can still succeed
Batched Actions (Atomic):
const tx = {
receiverId: "contract.near",
actions: [action1, action2, action3]
};
await wallet.signAndSendTransactions({ transactions: [tx] });
// If any action fails, ALL revert
Error Handling
When a batch fails, you'll receive an error indicating which action failed:
try {
await wallet.signAndSendTransactions({ transactions: [ftTx] });
} catch (error) {
console.error("Batch failed:", error);
// All actions in the batch have been reverted
}
Best Practices
- Use batching for atomic operations: When actions must succeed together
- Keep batches small: Large batches may exceed gas limits
- Order matters: Actions execute sequentially, so order dependencies correctly
- Test failure scenarios: Ensure rollback behavior is correct
Combining Both Approaches
You can combine multiple transactions AND batch actions:
const tx1 = {
receiverId: "contract1.near",
actions: [action1, action2] // Batched, atomic
};
const tx2 = {
receiverId: "contract2.near",
actions: [action3, action4] // Batched, atomic
};
// Independent transactions, each with batched actions
await wallet.signAndSendTransactions({ transactions: [tx1, tx2] });
This provides maximum flexibility - atomic operations within each contract, with independent execution across contracts.
Summary
You've learned how to:
- Set up wallet integration for multiple contracts
- Query data from multiple contracts simultaneously
- Send independent transactions to multiple contracts
- Batch actions within a single contract for atomic operations
This pattern is essential for building complex Web3 applications that interact with multiple protocols and contracts in the NEAR ecosystem.