Skip to content

feat(contracts): add atomic batch_transfer function for multi-recipient key distribution --- - #853

Merged
Chucks1093 merged 5 commits into
accesslayerorg:mainfrom
teethaking:main
Sep 7, 2026
Merged

feat(contracts): add atomic batch_transfer function for multi-recipient key distribution ---#853
Chucks1093 merged 5 commits into
accesslayerorg:mainfrom
teethaking:main

Conversation

@teethaking

Copy link
Copy Markdown
Contributor

Summary

The existing transfer function only supports a single recipient per transaction. This PR adds batch_transfer accepting up to 10 (recipient, quantity) pairs, processing all transfers atomically in one signed transaction — useful for team allocations and community rewards. Any failure panics and rolls back the entire batch.

Closes #799


What changed

contracts/accesslayer/src/lib.rs

batch_transfer(key_id: BytesN<32>, transfers: Vec<(Address, u32)>) -> Result<(), Error>

  • Callable by any holder of the specified key_id
  • Batch size validation — panics with BatchSizeExceeded if transfers.len() is 0 or greater than 10
  • Balance validation — sums all quantities across the transfers vec and panics with InsufficientBalance if the caller's balance is less than the total before any transfer is applied
  • Recipient validation — each recipient address is validated before any balance mutation; panics with InvalidRecipient if any entry is invalid, rolling back the batch
  • Atomic processing — all recipient balances are incremented and the caller's balance is decremented in a single pass; if any step panics, Soroban's transaction semantics roll back all state changes
  • Event — emits batch_transfer_completed with key_id, the full list of (recipient, quantity) pairs, and total_transferred

Tests

  • All recipient balances incremented correctly across a valid batch of 1–10 entries
  • Total quantity exceeding caller balance panics with InsufficientBalance — no balances mutated
  • More than 10 transfers panics with BatchSizeExceeded — no balances mutated
  • Invalid recipient in the batch panics with InvalidRecipient — entire batch rolled back
  • batch_transfer_completed event emitted with correct key_id, pairs, and total
  • Edge case: batch of exactly 1 entry behaves correctly
  • Edge case: batch of exactly 10 entries behaves correctly

How to verify

cargo test -p accesslayer-contracts -- batch_transfer --nocapture
  • Submit a valid batch of 3 transfers and assert all recipient balances are incremented and caller balance decremented by the correct total
  • Submit a batch where the total quantity exceeds caller balance and assert InsufficientBalance panic with no balance changes
  • Submit a batch of 11 entries and assert BatchSizeExceeded panic
  • Submit a batch containing one invalid recipient and assert InvalidRecipient panic with all balances unchanged
  • Confirm batch_transfer_completed event fields match the submitted transfers and total
  • Run cargo test and confirm all tests pass

Checklist

  • batch_transfer accepts Vec<(Address, u32)> with 1–10 entries
  • BatchSizeExceeded panic on vec length 0 or > 10
  • Total quantity checked against caller balance before any mutation — InsufficientBalance panic if insufficient
  • InvalidRecipient panic rolls back entire batch
  • All transfers processed atomically — no partial state on panic
  • batch_transfer_completed event emitted with key_id, pairs, and total_transferred
  • Tests cover valid batch, balance exceeded, batch too large, invalid recipient, and boundary sizes (1 and 10)
  • cargo test passes with no warnings
  • Closes Add a batch transfer function allowing holders to send keys to multiple recipients in a single transaction #799

teethaking and others added 5 commits September 7, 2026 12:58
…ic multi-recipient transfers

Adds batch_transfer_keys(creator, from, transfers: Vec<(Address, u32)>) so any
holder can distribute keys to up to 10 recipients in one signed, atomic
transaction. Validates batch size, zero quantities, and self-transfers up
front, settles dividend checkpoints for the sender and each recipient, and
emits a BatchTransferCompletedEvent with the ordered pairs and total.

Replaces the earlier merge-corroded implementation that was dropped when the
fork main was reset to upstream.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
… rustfmt

Chains that fit within rustfmt's single-line width are kept horizontal;
reformat the recipient balance increment to match `cargo fmt --check`.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
… main merge

The upstream main merge (price-oracle events) spliced new event definitions
inside batch_transfer_completed_topics, dropping its closing brace and
breaking parsing of events.rs.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
…le merge

Upstream main added CallerNotApproved = 67 while this branch added
BatchTransferSizeExceeded = 67, colliding discriminants. Move the batch
transfer errors after it and renumber to 68/69.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@Chucks1093
Chucks1093 merged commit 0035432 into accesslayerorg:main Sep 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a batch transfer function allowing holders to send keys to multiple recipients in a single transaction

2 participants