Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions veritixpay/contract/token/src/batch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::balance::{receive_balance, spend_balance};
use crate::validation::require_positive_amount;
use soroban_sdk::{contracttype, symbol_short, Address, Env, Vec};

/// Represents a single entry in a batch transfer.
#[contracttype]
#[derive(Clone)]
pub struct BatchEntry {
pub address: Address,
pub amount: i128,
}

/// Transfers tokens from `from` to multiple recipients in one call.
/// Maximum 50 recipients per batch.
pub fn transfer_batch(e: &Env, from: Address, recipients: Vec<BatchEntry>) {
from.require_auth();
if recipients.len() > 50 {
panic!("BatchLimit: maximum 50 recipients per call");
}
let mut total: i128 = 0;
for entry in recipients.iter() {
require_positive_amount(entry.amount);
spend_balance(e, from.clone(), entry.amount);
receive_balance(e, entry.address.clone(), entry.amount);
total = total.checked_add(entry.amount).expect("overflow");
}
e.events().publish((symbol_short!("batch_xfer"), from), total);
}
43 changes: 43 additions & 0 deletions veritixpay/contract/token/src/batch_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use soroban_sdk::{testutils::Address as _, Address, Env, Vec};
use crate::balance::read_balance;
use crate::batch::{transfer_batch, BatchEntry};
use crate::contract::VeritixToken;

fn setup_env() -> Env { let e = Env::default(); e.mock_all_auths(); e }

#[test]
fn test_transfer_batch_distributes_correctly() {
let e = setup_env();
let cid = e.register_contract(None, VeritixToken);
let from = Address::generate(&e);
let r1 = Address::generate(&e);
let r2 = Address::generate(&e);

e.as_contract(&cid, || {
crate::balance::receive_balance(&e, from.clone(), 1000);
let mut recs: Vec<BatchEntry> = Vec::new(&e);
recs.push_back(BatchEntry { address: r1.clone(), amount: 400 });
recs.push_back(BatchEntry { address: r2.clone(), amount: 600 });
transfer_batch(&e, from.clone(), recs);
assert_eq!(read_balance(&e, r1.clone()), 400);
assert_eq!(read_balance(&e, r2.clone()), 600);
assert_eq!(read_balance(&e, from.clone()), 0);
});
}

#[test]
#[should_panic(expected = "BatchLimit")]
fn test_transfer_batch_rejects_over_50() {
let e = setup_env();
let cid = e.register_contract(None, VeritixToken);
let from = Address::generate(&e);

e.as_contract(&cid, || {
crate::balance::receive_balance(&e, from.clone(), 100_000);
let mut recs: Vec<BatchEntry> = Vec::new(&e);
for _ in 0..51 {
recs.push_back(BatchEntry { address: Address::generate(&e), amount: 1 });
}
transfer_batch(&e, from.clone(), recs);
});
}
4 changes: 4 additions & 0 deletions veritixpay/contract/token/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod splitter;
pub mod storage_types;
pub mod validation;

pub mod batch;
mod contract;

#[cfg(test)]
Expand All @@ -39,6 +40,9 @@ mod admin_test;
#[cfg(test)]
mod splitter_test;

#[cfg(test)]
mod batch_test;

#[cfg(test)]
mod dispute_test;

Expand Down