fix(#406): timelock admin raffle cancellation to prevent rug of ticket buyers#543
Merged
Otaiki1 merged 2 commits intoJun 30, 2026
Conversation
… rug of ticket buyers An admin could cancel a raffle with sold tickets immediately, returning the prize to the creator while buyers had to individually claim refunds. This adds a 48h timelock (matching the factory's TIMELOCK_DELAY_SECONDS) for admin cancels of raffles with sold tickets. - cancel_raffle now schedules (not executes) an admin cancel when tickets are sold: status stays Active, prize stays locked, and a CancelScheduled event is emitted with the cancel_at timestamp. - New execute_admin_cancel runs the actual cancel only after the timelock elapses (CancelTimelockActive / CancelNotScheduled errors otherwise). - refund_ticket lets holders refund immediately once a cancel is scheduled. - Admin cancels with zero tickets sold remain immediate. - Adds get_pending_cancel getter and wipes PendingAdminCancel in wipe_storage. - Tests cover scheduling, timelock enforcement, immediate refunds, and the no-schedule / zero-ticket paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
This PR closes a security hole in
contracts/raffle-instancewhere an admin could cancel a raffle with sold tickets instantly, returning the prize to the creator while ticket buyers were left to chase individual refunds — an immediate rug of ticket holders.It introduces a 48-hour timelock for admin-initiated cancellations of raffles that have already sold tickets (matching the factory's
TIMELOCK_DELAY_SECONDS). The cancel is now scheduled first (emitting aCancelScheduledevent) and only executed after the notice period, while ticket holders may refund immediately the moment a cancel is scheduled.Related Issue
Closes #406
Changes
🔒 Timelocked Admin Cancellation
[MODIFY]
contracts/raffle-instance/src/lib.rs—cancel_raffleWhen
CancelReason::AdminCancelledis used on a raffle withtickets_sold > 0, the cancel is now scheduled instead of executed: status staysActive, the prize stays locked, andPendingAdminCancel(acancel_attimestamp =now + 48h) is stored.Admin cancels of raffles with zero tickets sold remain immediate (no buyers to protect).
[ADD]
contracts/raffle-instance/src/lib.rs—execute_admin_cancelExecutes a previously scheduled admin cancel, but only after the timelock has elapsed.
Returns
CancelTimelockActiveif called beforecancel_at, andCancelNotScheduledif no cancel is pending.[ADD]
contracts/raffle-instance/src/lib.rs—get_pending_cancelRead-only getter returning the scheduled
cancel_attimestamp (orNone) for indexers/UIs.[MODIFY]
contracts/raffle-instance/src/lib.rs—refund_ticketTicket holders can now refund immediately once a cancel is scheduled, not just after it executes.
[MODIFY]
contracts/raffle-instance/src/lib.rs—wipe_storageCleans up the new
PendingAdminCancelkey on teardown.🧱 New State, Events & Errors
[ADD]
contracts/raffle-instance/src/events.rs—CancelScheduledEmitted when an admin schedules a cancel; carries
creator,scheduled_by,tickets_sold,cancel_at, andtimestamp.[MODIFY]
contracts/raffle-instance/src/lib.rsDataKey::PendingAdminCancel— stores the executable-at timestamp while a cancel is pending.ADMIN_CANCEL_TIMELOCK_SECONDS = 172_800(48 hours) — matches the factory'sTIMELOCK_DELAY_SECONDS.New errors:
CancelTimelockActive = 64,CancelNotScheduled = 65.🧪 Tests
contracts/raffle-instance/src/lib.rs(test module)admin_cancel_with_sold_tickets_requires_timelock— scheduling works, exactly oneCancelScheduledevent fires, executing immediately and 1s before the deadline both fail withCancelTimelockActive, and it succeeds at the deadline.ticket_holders_refund_immediately_after_cancel_scheduled— refunds rejected whileActive, allowed instantly once scheduled.execute_admin_cancel_without_schedule_fails— returnsCancelNotScheduled.admin_cancel_with_no_tickets_is_immediate— zero-ticket admin cancel stays instant.test_wipe_storage_removes_all_keys— updated to the schedule → wait →execute_admin_cancelflow.Verification Results
CancelScheduledevent is emitted when an admin schedules a cancel🤖 Generated with Claude Code