Skip to content

feat: expose unique trader count and emit UniqueTraderAdded (#985) - #987

Merged
Chucks1093 merged 5 commits into
accesslayerorg:mainfrom
Yunusabdul38:feat/unique-trader-tracking-985
Sep 27, 2026
Merged

Chucks1093 merged 5 commits into
accesslayerorg:mainfrom
Yunusabdul38:feat/unique-trader-tracking-985

Conversation

@Yunusabdul38

@Yunusabdul38 Yunusabdul38 commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Exposes unique trader tracking on the bonding curve contract.

The counting itself already existed and is correct: accrue_trade_analytics sets a HasTraded(creator, trader) flag on a wallet's first buy or sell and increments UniqueTraderCount(creator) behind it, so repeat trades do not double-count, and get_analytics returns the total. What was missing was everything a caller could use — a dedicated count view, a has-traded check, and an event.

Changes

  • events.rs — UniqueTraderAddedEvent (uniq_trd), carrying key_id, trader, the new unique_trader_count and the ledger, plus the matching unique_trader_added_topics helper.
  • lib.rs — emits the event inside the first-trade branch, so an indexer gets exactly one event per (key_id, trader) pair rather than one per trade. Adds get_unique_trader_count(key_id) and has_traded(key_id, wallet) as public views.
  • test_unique_traders.rs — new test module.

has_traded previously existed only as a storage-key helper with no way to read it from outside the contract; the count was reachable only by fetching all three analytics fields.

Acceptance criteria

  • Unique count increments only on first trade per wallet — already the case; covered by test_unique_trader_count_view_matches_analytics.
  • Repeat trades by same wallet do not increment count — test_unique_trader_count_unchanged_by_repeat_trades drives two buys and a sell from one wallet and asserts the count stays at 1 while trade_count reaches 3.
  • has_traded returns correct bool for traded and untouched wallets — test_has_traded_false_for_untouched_wallet, test_has_traded_true_after_first_buy, test_has_traded_is_per_wallet. test_has_traded_stays_true_after_selling_out pins that the flag records a trade having happened, not a balance being held.
  • UniqueTraderAdded event emitted exactly once per wallet — test_unique_trader_event_emitted_once_per_wallet and test_unique_trader_event_emitted_for_each_new_wallet.
  • get_unique_trader_count returns correct value after bulk trades — test_unique_trader_count_after_bulk_trades runs ten wallets trading twice each and asserts 10 unique against 20 trades.

One note on scope: the issue suggests storing the trader set "using a bitmap or sorted set pattern". The existing HasTraded(creator, trader) flag already gives O(1) membership and insertion without materialising a set, and switching representations would rewrite working, tested accounting for no gain — so the storage layout is left as is.

Closes #985

Closes #979
Closes #982
Closes #984

The per-creator counting already existed: accrue_trade_analytics sets a
HasTraded flag on a wallet's first buy or sell and increments UniqueTraderCount
behind it, and get_analytics returns the total. What was missing was everything
a caller could actually use.

- events.rs: UniqueTraderAddedEvent (uniq_trd), carrying key_id, trader, the
  new count and the ledger. Published inside the first-trade branch, so an
  indexer gets exactly one event per wallet per creator rather than one per
  trade.
- lib.rs: get_unique_trader_count(key_id) and has_traded(key_id, wallet) as
  public views. The count was previously reachable only by reading all three
  analytics fields; has_traded was not reachable at all, existing only as a
  storage-key helper.
- test_unique_traders.rs: the two views, the event firing exactly once per
  wallet and once per new wallet, has_traded staying true after a wallet sells
  out, and a ten-wallet bulk-trade count.

Closes accesslayerorg#985
@drips-wave

drips-wave Bot commented Sep 26, 2026

Copy link
Copy Markdown

@Yunusabdul38 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

The verify job runs cargo fmt --all -- --check and flagged two hunks in the
files added by the previous commit: the unique_trader_added_topics signature
needed wrapping, and the wallets binding in test_unique_trader_count_after_bulk_trades
fits on one line. No behaviour change.
Three problems in the tests added by the earlier commit, all caught by the
verify job:

- cargo fmt: unique_trader_added_topics needed a wrapped signature.
- The crate is no_std, so std::vec::Vec did not resolve. Collect the bulk-trade
  wallets into a soroban_sdk::Vec instead.
- Event topics come back as raw Val, which has no PartialEq, so comparing one
  against a Symbol did not compile. Convert the first topic to a Symbol before
  comparing; a non-symbol topic belongs to another event and does not match.

The two event tests were also asserting against a running total, but
env.events().all() only holds the most recent invocation's events. They now
assert per call — one event on a wallet's first buy, none on its repeat buy or
sell — which pins "exactly once per wallet" more tightly than the totals did.

cargo fmt --all -- --check, cargo clippy --workspace --all-targets -D warnings,
and cargo test --workspace all pass locally.
@Chucks1093
Chucks1093 merged commit 1624ce1 into accesslayerorg:main Sep 27, 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

2 participants