Skip to content

feat(order): add price snapshot and restaurant-scoped validation to place_order()#298

Merged
Leothosine merged 2 commits into
tosirano:mainfrom
Ibinola:feat/issue-287-price-snapshot-restaurant-validation
Jun 26, 2026
Merged

feat(order): add price snapshot and restaurant-scoped validation to place_order()#298
Leothosine merged 2 commits into
tosirano:mainfrom
Ibinola:feat/issue-287-price-snapshot-restaurant-validation

Conversation

@Ibinola

@Ibinola Ibinola commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the price manipulation and cross-restaurant pollution vulnerabilities in place_order() by introducing an on-chain menu item registry and capturing an immutable price snapshot at order time.

Problem

place_order() accepted caller-supplied unit_price values with no on-chain validation, enabling:

  1. A caller submitting any price (e.g. 1 stroop for a 50,000-stroop meal)
  2. A caller submitting a menu_item_id from a different restaurant
  3. No historical price record for dispute resolution

Implementation

New types:

  • MenuItem — on-chain authoritative menu item keyed by (restaurant_id, menu_item_id)
  • MenuItemSnapshot — immutable price record captured at order time (menu_item_id, name, price_at_order)
  • OrderItem.snapshot — embedded snapshot on each stored line item

New storage key:

  • DataKey::MenuItem(restaurant_id, menu_item_id)

New admin functions:

  • register_menu_item() — register/update an item's authoritative price
  • remove_menu_item() — remove an item from a restaurant's menu
  • get_menu_item() — view a registered item

Updated place_order():

  • Validates each menu_item_id belongs to the given restaurant_id via on-chain registry; panics with "item not on menu" if not found
  • Ignores caller-supplied unit_price; always uses the registry price
  • Stores a MenuItemSnapshot (name + price_at_order) on every OrderItem

Validation

All 9 tests pass (0 failures):

Test Result
Caller-supplied price is ignored; registry price used
Snapshot stored correctly with price_at_order
Item from wrong restaurant panics with "item not on menu"
Unregistered item panics with "item not on menu"
place_order + get_order (existing)
advance_status lifecycle (existing)
Customer cancel pending (existing)
Customer cannot cancel confirmed (existing)
get_restaurant_orders (existing)

Closes #287

Ibinola and others added 2 commits June 23, 2026 10:41
…lace_order()

- Add MenuItem struct: on-chain authoritative menu item registry keyed by
  (restaurant_id, menu_item_id)
- Add MenuItemSnapshot struct: immutable price record captured at order time
- Add DataKey::MenuItem(u64, u64) storage key
- Add register_menu_item(), remove_menu_item(), get_menu_item() admin functions
- Update OrderItem to include snapshot field with price_at_order
- Rewrite place_order() to:
  - Validate each menu_item_id belongs to the restaurant_id via on-chain registry
  - Panic with 'item not on menu' if validation fails
  - Ignore caller-supplied unit_price; always use authoritative registry price
  - Capture MenuItemSnapshot for each item in persistent storage
- Add 9 unit tests (all passing):
  - Caller-supplied unit_price is ignored; registry price is used
  - price_at_order snapshot is stored correctly
  - Item from wrong restaurant panics with 'item not on menu'
  - Unregistered item panics with 'item not on menu'
  - All existing lifecycle tests updated to register menu items first

Closes tosirano#287
@Leothosine Leothosine merged commit 1d35042 into tosirano:main Jun 26, 2026
2 of 3 checks 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.

[CO-09] Add price snapshot and restaurant-scoped validation to place_order()

2 participants