Skip to content

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

Closed
teethaking wants to merge 28 commits into
accesslayerorg:mainfrom
teethaking:main
Closed

Add a batch transfer function allowing holders to send keys to multiple recipients in a single transaction#814
teethaking wants to merge 28 commits into
accesslayerorg:mainfrom
teethaking:main

Conversation

@teethaking

Copy link
Copy Markdown
Contributor

Summary

Closes #799

Adds batch_transfer_keys(creator, from, transfers: Vec<(Address, u32)>) — a new entrypoint that lets any holder distribute keys to up to 10 recipients in one signed, atomic transaction.

Changes

creator-keys/src/lib.rs

  • ContractError: append BatchTransferSizeExceeded = 51 and InvalidRecipient = 52 (ABI-safe, end-of-enum)
  • MAX_BATCH_TRANSFER_SIZE = 10 constant
  • batch_transfer_keys implementation:
    • from.require_auth() + assert_not_paused guards
    • Pre-flight pass: validates size <= 10, all quantities > 0, no self-recipients, accumulates total with overflow protection
    • Reads sender balance once and settles dividend checkpoint before any mutation
    • Rejects atomically if balance < total_quantity
    • Apply pass: settles per-recipient dividends, decrements running sender balance, updates recipient balances, tracks holder count incrementally
    • Writes final sender balance, decrements holder count if sender empties
    • Emits BatchTransferCompletedEvent
    • Total supply is never touched — bonding curve invariant preserved

creator-keys/src/events.rs

  • BATCH_TRANSFER_COMPLETED_EVENT_NAME = symbol_short!("bat_xfer")
  • BatchTransferCompletedEvent { creator_id, from, transfers, total_transferred, ledger }
  • batch_transfer_completed_topics(creator, from) helper

Tests

tests/batch_transfer_keys.rs — 11 integration tests:

  • Sender balance decremented by total quantity
  • Each recipient balance incremented correctly
  • Accumulation onto existing recipient balance
  • Total supply unchanged (bonding curve invariant)
  • Holder count increments for new recipients
  • Holder count net-zero when sender empties and recipient is new
  • BatchTransferSizeExceeded on 11 entries; succeeds on exactly 10
  • InsufficientBalance + state unchanged when total > balance
  • InvalidRecipient when any recipient == sender
  • ZeroTransferAmount when any entry has qty == 0
  • NotRegistered for unregistered creator

tests/batch_transfer_event_fields.rs — 7 event-field unit tests:

  • creator_id, from, total_transferred, transfers.len(), per-entry (address, qty), ledger fields
  • No event emitted on revert

teethaking and others added 2 commits August 27, 2026 16:23
Add batch_transfer_keys(creator, from, transfers: Vec<(Address, u32)>) allowing a holder to send keys to up to 10 recipients in a single atomic transaction.

Changes:
- ContractError: append BatchTransferSizeExceeded = 51, InvalidRecipient = 52
- Add MAX_BATCH_TRANSFER_SIZE = 10 constant
- Implement batch_transfer_keys with pre-flight validation pass then apply
  pass, matching the two-pass pattern used by airdrop_keys; dividend
  checkpoints settled for sender and each recipient before balance changes
- events.rs: add BATCH_TRANSFER_COMPLETED_EVENT_NAME, BatchTransferCompletedEvent
  struct, and batch_transfer_completed_topics helper
- tests/batch_transfer_keys.rs: 11 integration tests covering balance updates,
  supply invariant, holder count, and all error paths
- tests/batch_transfer_event_fields.rs: 7 event-field unit tests

Closes accesslayerorg#799
@teethaking teethaking changed the title [FEATURE] Add bounty template picker in the creation form Add a batch transfer function allowing holders to send keys to multiple recipients in a single transaction Aug 27, 2026
@drips-wave

drips-wave Bot commented Aug 27, 2026

Copy link
Copy Markdown

@teethaking Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

teethaking and others added 14 commits August 27, 2026 16:38
…get_curve_exponent (accesslayerorg#755 accesslayerorg#756)

Implement five missing entrypoints referenced by test_issues.rs:

- ContractError: append BatchSizeExceeded=53, InvalidExponent=54, RoyaltyExceedsLimit=55
- BatchBuyResult contracttype struct (creator, quantity, price_paid)
- batch_buy(buyer, orders: Vec<(Address,u32)>): 1-5 orders, calls buy_key
  quantity times per creator at current quote price; BatchSizeExceeded on
  empty or >MAX_BATCH_BUY_SIZE orders
- set_royalty(creator, buy_fee_bps, sell_fee_bps): creator-auth, validates
  <= MAX_ROYALTY_BPS (500), stores RoyaltyConfig, emits RoyaltyUpdatedEvent
- get_royalty_config(creator) -> Option<RoyaltyConfig>: read-only view
- migrate_curve(admin, exponent, key_ids): admin-auth, exponent 1-5,
  writes CurveExponent per creator, emits CurveMigratedEvent
- get_curve_exponent(creator) -> Option<u32>: read-only view
test_issues.rs: add missing 7th &None (whitelist param) to all 6 direct
client.register_creator calls that were written before whitelist was added.

test_new_features.rs: replace non-existent client.initialize(&admin, &treasury, &price)
with the actual setup sequence: set_protocol_admin + set_key_price +
set_fee_config + set_treasury_address. Also add 2 missing &None args to
the register_creator helper.
…ix test arg counts

DataKey: add ProtocolFeeBps, LockupDurationSecs, RoyaltyConfig, CurveExponent,
HolderCapBps, LastBuyTimestamp variants (referenced in lib.rs but absent from enum).

constants::storage: add holder_cap_bps, last_buy_timestamp helpers and
PROTOCOL_FEE_BPS, LOCKUP_DURATION_SECS constants; remove duplicate
royalty_config/curve_exponent definitions.

ContractError: append MaxHoldingExceeded=56, LockupPeriodActive=57,
InvalidHolderCap=58 (referenced throughout lib.rs but never defined).

events.rs: add FeeCollectedEvent + fee_collected_topics (used by
collect_protocol_trade_fee) and LockupBlockedEvent + lockup_blocked_topics
(used by sell_key lockup enforcement path).

test_issues.rs: revert erroneous extra &None  contract register_creator
takes params + 6 optional args (7 total), tests correctly pass 6 options.

test_new_features.rs: register_creator helper had 5 &None, needs 6.
@Chucks1093

Copy link
Copy Markdown
Member

❌ CI Failed — verify (Contracts CI)

The verify check is failing on this PR.

Likely causes:

  • Compile error from a missing import or unresolved symbol after merge
  • cargo fmt not run before pushing
  • Unused import warning treated as error

Steps to fix:

  1. Run cargo build and fix all errors
  2. Run cargo fmt --all and commit
  3. Push

@Chucks1093

Copy link
Copy Markdown
Member

Fix CI and MC

@teethaking

teethaking commented Sep 4, 2026 via email

Copy link
Copy Markdown
Contributor Author

teethaking and others added 9 commits September 5, 2026 04:19
The first of two duplicate `last_buy_timestamp` functions inside
`constants::storage` was missing its closing brace, which caused
`cargo fmt` to fail with an unclosed delimiter error at EOL.

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

Same pattern as lib.rs: duplicate FeeCollectedEvent and
LockupBlockedEvent structs where the first copy of each was missing
its closing brace, plus a duplicate LOCKUP_BLOCKED_EVENT_NAME
constant. Removed the incomplete first copies and the extra constant.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Two-space indent changed to four-space to match surrounding enum
variants and satisfy cargo fmt.

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

Merge conflicts introduced duplicate variants for ProtocolFeeBps,
HolderCapBps, LockupDurationSecs, RoyaltyConfig, CurveExponent,
and LastBuyTimestamp. The derive macros (PartialEq, Debug) and
contracttype macro cannot handle duplicates, causing E0004
non-exhaustive patterns and 85 total compilation errors.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Remove duplicate DataKey variants (RoyaltyConfig, CurveExponent),
duplicate constants::storage functions (royalty_config, curve_exponent,
last_buy_timestamp), and duplicate contract methods (batch_buy,
set_royalty, get_royalty_config, migrate_curve, get_curve_exponent)
that were introduced by failed merge conflict resolution.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
The dedup script missed royalty_config, curve_exponent, and
last_buy_timestamp in constants::storage (8-space indent) because
the regex only matched 4-space depth. These 3 duplicates caused
71 compilation errors from duplicate name definitions.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Three ContractError variants (MaxHoldingExceeded, LockupPeriodActive,
InvalidHolderCap) appeared twice with different discriminant values,
and 8 other variants had colliding values. Removed the 3 duplicates,
renumbered the 7 unique second-block variants to 59-65, and shifted
trailing variants to 66-69.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
teethaking and others added 3 commits September 6, 2026 00:00
Merge conflict left two definitions with different symbol values
(lck_blk vs lk_blk). Removed the second occurrence to fix E0428.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Merge conflict left two identical definitions. Removed the second
occurrence to prevent E0428 duplicate definition error.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Remove duplicate checked_sub/saturating_sub in burn function and
replace manual subtraction with saturating_sub in fee computation.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@teethaking teethaking closed this Sep 7, 2026
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