Batch Payment API Reference
The Batch Payment module enables efficient multi-recipient payments in a single transaction. Process up to 100 recipients with automatic status tracking and failure handling.
Overview
Module: dolphinpay::batch
File: sources/tokens/batch.move
Key capabilities:
- Send payments to multiple recipients in one transaction
- Automatic individual payment tracking
- Partial failure handling (continue processing if some fail)
- Per-recipient success/failure status
- Detailed execution metadata
Data Structures
BatchPayment
Main batch payment object tracking multiple recipients.
public struct BatchPayment has key {
id: UID,
sender: address,
recipients: vector<Recipient>,
total_amount: u64,
currency: TypeName,
status: u8,
executed_count: u64,
success_count: u64,
failed_count: u64,
created_at: u64,
executed_at: Option<u64>,
}
Fields:
id- Unique identifiersender- Payment sender addressrecipients- Vector of recipient informationtotal_amount- Total amount to distributecurrency- Currency typestatus- Batch status (0=Pending, 3=Completed)executed_count- Number of processed recipientssuccess_count- Number of successful transfersfailed_count- Number of failed transferscreated_at- Creation epochexecuted_at- Execution epoch (if executed)
Recipient
Information for a single recipient in the batch.
public struct Recipient has store, copy, drop {
address: address,
amount: u64,
metadata: String,
status: u8,
failure_reason: Option<String>,
}
Fields:
address- Recipient addressamount- Amount to send to this recipientmetadata- Optional metadata (invoice ID, note, etc.)status- Individual status (0=Pending, 1=Success, 2=Failed)failure_reason- Reason for failure (if failed)
Constants
const MAX_BATCH_SIZE: u64 = 100;
// Status codes
const STATUS_PENDING: u8 = 0;
const STATUS_SUCCESS: u8 = 1;
const STATUS_FAILED: u8 = 2;
const STATUS_COMPLETED: u8 = 3;
Core Functions
create_batch_payment
Create a new batch payment object.
public fun create_batch_payment<T>(
recipients: vector<Recipient>,
total_amount: u64,
ctx: &mut TxContext,
): BatchPayment
Parameters:
recipients- Vector of recipients (max 100)total_amount- Total amount for all recipientsctx- Transaction context
Returns: BatchPayment object
Aborts:
E_EMPTY_RECIPIENTS(400) - Recipients vector is emptyE_TOO_MANY_RECIPIENTS(401) - More than 100 recipients- Total amount validation recommended but not enforced
Example:
let mut recipients = vector::empty<Recipient>();
vector::push_back(&mut recipients, batch::create_recipient(
@0xALICE,
1_000_000_000, // 1 SUI
string::utf8(b"Payment to Alice")
));
vector::push_back(&mut recipients, batch::create_recipient(
@0xBOB,
500_000_000, // 0.5 SUI
string::utf8(b"Payment to Bob")
));
let batch = batch::create_batch_payment<SUI>(
recipients,
1_500_000_000, // Total: 1.5 SUI
ctx
);
execute_batch_payment
Execute the batch payment, sending coins to all recipients.
public entry fun execute_batch_payment<T>(
batch: &mut BatchPayment,
mut coin: Coin<T>,
ctx: &mut TxContext,
)
Parameters:
batch- Mutable reference to batch paymentcoin- Coin object with sufficient balancectx- Transaction context
Behavior:
- Processes each recipient sequentially
- Continues execution even if individual transfers fail
- Returns excess funds to sender
- Updates batch status to
COMPLETED - Emits
BatchPaymentExecutedevent
Aborts:
E_INSUFFICIENT_BALANCE(402) - Coin balance < total_amount
Partial Failures: If coin runs out during execution, remaining recipients will be marked as FAILED with reason "Insufficient remaining balance".
Example:
// Create coin with sufficient balance
let coin = coin::mint_for_testing<SUI>(2_000_000_000, ctx);
// Execute batch payment
batch::execute_batch_payment(&mut batch, coin, ctx);
// Check results
assert!(batch::get_success_count(&batch) == 2, 0);
assert!(batch::is_completed(&batch), 1);
create_recipient
Helper function to create a recipient.
public fun create_recipient(
address: address,
amount: u64,
metadata: String,
): Recipient
Parameters:
address- Recipient addressamount- Amount to sendmetadata- Optional metadata string
Returns: Recipient struct
Query Functions
get_sender
Get the batch payment sender address.
public fun get_sender(batch: &BatchPayment): address
get_total_amount
Get the total amount for the batch.
public fun get_total_amount(batch: &BatchPayment): u64
get_status
Get the batch payment status code.
public fun get_status(batch: &BatchPayment): u8
Returns:
0- Pending3- Completed
get_success_count
Get number of successful transfers.
public fun get_success_count(batch: &BatchPayment): u64
get_failed_count
Get number of failed transfers.
public fun get_failed_count(batch: &BatchPayment): u64
get_executed_count
Get number of processed recipients.
public fun get_executed_count(batch: &BatchPayment): u64
get_total_recipients
Get total number of recipients in the batch.
public fun get_total_recipients(batch: &BatchPayment): u64
is_completed
Check if batch execution is complete.
public fun is_completed(batch: &BatchPayment): bool
is_pending
Check if batch is pending execution.
public fun is_pending(batch: &BatchPayment): bool
get_recipient
Get recipient information by index.
public fun get_recipient(batch: &BatchPayment, index: u64): &Recipient
Parameters:
batch- Batch payment referenceindex- Recipient index (0-based)
Aborts: If index is out of bounds
get_recipient_address
Get address from a recipient.
public fun get_recipient_address(recipient: &Recipient): address
get_recipient_amount
Get amount for a recipient.
public fun get_recipient_amount(recipient: &Recipient): u64
get_recipient_status
Get status code for a recipient.
public fun get_recipient_status(recipient: &Recipient): u8
Returns:
0- Pending1- Success2- Failed
get_recipient_metadata
Get metadata string for a recipient.
public fun get_recipient_metadata(recipient: &Recipient): String
get_created_at
Get batch creation timestamp.
public fun get_created_at(batch: &BatchPayment): u64
get_executed_at
Get batch execution timestamp (if executed).
public fun get_executed_at(batch: &BatchPayment): Option<u64>
Returns: Some(timestamp) if executed, None if pending
Events
BatchPaymentExecuted
Emitted when a batch payment is executed.
public struct BatchPaymentExecuted has copy, drop {
batch_id: ID,
total_recipients: u64,
success_count: u64,
failed_count: u64,
timestamp: u64,
}
Error Codes
| Code | Constant | Description |
|---|---|---|
| 400 | E_EMPTY_RECIPIENTS | Recipients vector is empty |
| 401 | E_TOO_MANY_RECIPIENTS | More than 100 recipients |
| 402 | E_INSUFFICIENT_BALANCE | Coin balance less than total_amount |
Usage Examples
Simple Batch Payment
use dolphinpay::batch;
use sui::coin;
use std::string;
// Create recipients
let mut recipients = vector::empty();
vector::push_back(&mut recipients, batch::create_recipient(
@0xALICE,
1_000_000_000,
string::utf8(b"Invoice #001")
));
vector::push_back(&mut recipients, batch::create_recipient(
@0xBOB,
2_000_000_000,
string::utf8(b"Invoice #002")
));
vector::push_back(&mut recipients, batch::create_recipient(
@0xCHARLIE,
500_000_000,
string::utf8(b"Invoice #003")
));
// Create batch
let mut batch = batch::create_batch_payment<SUI>(
recipients,
3_500_000_000, // 3.5 SUI total
ctx
);
// Execute with sufficient coin
let coin = coin::mint_for_testing<SUI>(3_500_000_000, ctx);
batch::execute_batch_payment(&mut batch, coin, ctx);
// Check results
assert!(batch::get_success_count(&batch) == 3, 0);
assert!(batch::is_completed(&batch), 1);
Checking Individual Results
// After execution, check each recipient status
let recipient_count = batch::get_total_recipients(&batch);
let mut i = 0;
while (i < recipient_count) {
let recipient = batch::get_recipient(&batch, i);
let status = batch::get_recipient_status(recipient);
if (status == 1) { // STATUS_SUCCESS
// Transfer succeeded
let addr = batch::get_recipient_address(recipient);
let amt = batch::get_recipient_amount(recipient);
// Log or process success
} else if (status == 2) { // STATUS_FAILED
// Transfer failed
// Handle failure case
};
i = i + 1;
};
Salary Payments
// Pay salaries to multiple employees
let mut employees = vector::empty();
// Add all employees
vector::push_back(&mut employees, batch::create_recipient(
@employee1, 5_000_000_000, string::utf8(b"Salary - January")
));
vector::push_back(&mut employees, batch::create_recipient(
@employee2, 5_000_000_000, string::utf8(b"Salary - January")
));
// ... up to 100 employees
let total = calculate_total_salaries(&employees);
let batch = batch::create_batch_payment<SUI>(employees, total, ctx);
// Execute payroll
let salary_funds = coin::split(&mut company_treasury, total, ctx);
batch::execute_batch_payment(&mut batch, salary_funds, ctx);
Airdrop Distribution
// Airdrop tokens to community members
let mut airdrop_recipients = vector::empty();
// Add 100 community members
let mut i = 0;
while (i < 100) {
let recipient_addr = get_community_member_address(i);
vector::push_back(&mut airdrop_recipients, batch::create_recipient(
recipient_addr,
10_000_000, // 0.01 tokens per person
string::utf8(b"Community Airdrop Q1 2024")
));
i = i + 1;
};
let batch = batch::create_batch_payment<TOKEN>(
airdrop_recipients,
1_000_000_000, // 1000 tokens total
ctx
);
batch::execute_batch_payment(&mut batch, airdrop_tokens, ctx);
Best Practices
Amount Calculation
// Always verify total matches sum of individual amounts
fun verify_batch_total(recipients: &vector<Recipient>, total: u64): bool {
let mut sum = 0u64;
let mut i = 0;
let len = vector::length(recipients);
while (i < len) {
let recipient = vector::borrow(recipients, i);
sum = sum + batch::get_recipient_amount(recipient);
i = i + 1;
};
sum == total
}
Gas Optimization
- Batch Size: Stay under 100 recipients per batch
- Sequential Processing: Understand that recipients are processed sequentially
- Gas Estimation: ~0.001 SUI per recipient, ~0.05 SUI for 50 recipients
Error Handling
// Check balance before execution
let coin_value = coin::value(&payment_coin);
let batch_total = batch::get_total_amount(&batch);
assert!(coin_value >= batch_total, E_INSUFFICIENT_BALANCE);
// Execute and check results
batch::execute_batch_payment(&mut batch, payment_coin, ctx);
let failed = batch::get_failed_count(&batch);
if (failed > 0) {
// Handle partial failures
// Log or alert about failures
};
Metadata Usage
// Use metadata for tracking
batch::create_recipient(
recipient_addr,
amount,
string::utf8(b"invoice:INV-2024-001|purpose:consulting|month:jan")
);
Security Considerations
- Recipient Validation: Validate all recipient addresses are not zero address
- Amount Limits: Consider setting per-recipient amount limits
- Balance Checks: Always verify sufficient coin balance before execution
- Status Verification: Check completion status after execution
- Partial Failures: Handle cases where some recipients fail
Gas Costs
Approximate gas costs (testnet):
| Operation | Recipients | Est. Cost |
|---|---|---|
| Create batch | N/A | ~0.01 SUI |
| Execute | 10 | ~0.015 SUI |
| Execute | 50 | ~0.05 SUI |
| Execute | 100 | ~0.09 SUI |
Performance Notes
- Sequential Processing: Recipients processed one-by-one
- Failure Resilience: Continues even if individual transfers fail
- Memory Efficient: No intermediate object creation
- Event Emission: Single event per batch execution
Related Documentation
- Split Payment API - Percentage-based payment splitting
- Token Registry API - Multi-currency support
- Payment API - Single payment operations
- Core Modules - Module architecture
Need help? Check the SDK documentation for integration examples.