Skip to main content

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 identifier
  • sender - Payment sender address
  • recipients - Vector of recipient information
  • total_amount - Total amount to distribute
  • currency - Currency type
  • status - Batch status (0=Pending, 3=Completed)
  • executed_count - Number of processed recipients
  • success_count - Number of successful transfers
  • failed_count - Number of failed transfers
  • created_at - Creation epoch
  • executed_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 address
  • amount - Amount to send to this recipient
  • metadata - 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 recipients
  • ctx - Transaction context

Returns: BatchPayment object

Aborts:

  • E_EMPTY_RECIPIENTS (400) - Recipients vector is empty
  • E_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 payment
  • coin - Coin object with sufficient balance
  • ctx - 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 BatchPaymentExecuted event

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 address
  • amount - Amount to send
  • metadata - 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 - Pending
  • 3 - 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 reference
  • index - 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 - Pending
  • 1 - Success
  • 2 - 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

CodeConstantDescription
400E_EMPTY_RECIPIENTSRecipients vector is empty
401E_TOO_MANY_RECIPIENTSMore than 100 recipients
402E_INSUFFICIENT_BALANCECoin 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

  1. Recipient Validation: Validate all recipient addresses are not zero address
  2. Amount Limits: Consider setting per-recipient amount limits
  3. Balance Checks: Always verify sufficient coin balance before execution
  4. Status Verification: Check completion status after execution
  5. Partial Failures: Handle cases where some recipients fail

Gas Costs

Approximate gas costs (testnet):

OperationRecipientsEst. Cost
Create batchN/A~0.01 SUI
Execute10~0.015 SUI
Execute50~0.05 SUI
Execute100~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

Need help? Check the SDK documentation for integration examples.