@@ -132,4 +132,212 @@ contract ChainlinkVRFV2_5Integration_SubscriptionTest is Deployment {
132132
133133 console.log ("received randomness " , consumer.getRandomWords (requestId)[0 ]);
134134 }
135+
136+ /// @notice Tests successful subscription ownership transfer flow
137+ function test_subscriptionOwnershipTransfer_successful () public {
138+ // Deploy wrapper adapter
139+ ChainlinkVRFCoordinatorV2_5Adapter wrapper =
140+ new ChainlinkVRFCoordinatorV2_5Adapter (admin, address (randomnessSender));
141+
142+ // Create subscription as alice
143+ vm.prank (alice);
144+ uint256 subId = wrapper.createSubscription ();
145+
146+ // Verify initial state
147+ assertTrue (wrapper.getPendingSubscriptionOwner (subId) == address (0 ), "No pending owner initially " );
148+
149+ // Request ownership transfer to bob
150+ vm.prank (alice);
151+ vm.expectEmit (true , false , false , true );
152+ emit ChainlinkVRFCoordinatorV2_5Adapter.SubscriptionOwnerTransferRequested (subId, alice, bob);
153+ wrapper.requestSubscriptionOwnerTransfer (subId, bob);
154+
155+ // Verify pending state
156+ assertTrue (wrapper.getPendingSubscriptionOwner (subId) == bob, "Bob should be pending owner " );
157+
158+ // Accept ownership transfer as bob
159+ vm.prank (bob);
160+ vm.expectEmit (true , false , false , true );
161+ emit ChainlinkVRFCoordinatorV2_5Adapter.SubscriptionOwnerTransferred (subId, alice, bob);
162+ wrapper.acceptSubscriptionOwnerTransfer (subId);
163+
164+ // Verify ownership transfer completed
165+ assertTrue (wrapper.getPendingSubscriptionOwner (subId) == address (0 ), "No pending owner after transfer " );
166+
167+ // Verify bob can now manage the subscription
168+ vm.prank (bob);
169+ wrapper.addConsumer (subId, makeAddr ("new-consumer " ));
170+
171+ // Verify alice can no longer manage the subscription
172+ vm.prank (alice);
173+ vm.expectRevert ("Caller is not subscription owner " );
174+ wrapper.addConsumer (subId, makeAddr ("another-consumer " ));
175+ }
176+
177+ /// @notice Tests that non-owner cannot request ownership transfer
178+ function test_subscriptionOwnershipTransfer_onlyOwnerCanRequest () public {
179+ ChainlinkVRFCoordinatorV2_5Adapter wrapper =
180+ new ChainlinkVRFCoordinatorV2_5Adapter (admin, address (randomnessSender));
181+
182+ vm.prank (alice);
183+ uint256 subId = wrapper.createSubscription ();
184+
185+ // Try to request transfer as non-owner (bob)
186+ vm.prank (bob);
187+ vm.expectRevert ("Caller is not subscription owner " );
188+ wrapper.requestSubscriptionOwnerTransfer (subId, bob);
189+ }
190+
191+ /// @notice Tests that only pending owner can accept transfer
192+ function test_subscriptionOwnershipTransfer_onlyPendingOwnerCanAccept () public {
193+ ChainlinkVRFCoordinatorV2_5Adapter wrapper =
194+ new ChainlinkVRFCoordinatorV2_5Adapter (admin, address (randomnessSender));
195+
196+ vm.prank (alice);
197+ uint256 subId = wrapper.createSubscription ();
198+
199+ // Request transfer to bob
200+ vm.prank (alice);
201+ wrapper.requestSubscriptionOwnerTransfer (subId, bob);
202+
203+ // Try to accept as charlie (not the pending owner)
204+ vm.prank (charlie);
205+ vm.expectRevert ("Caller is not the pending owner " );
206+ wrapper.acceptSubscriptionOwnerTransfer (subId);
207+
208+ // Try to accept as alice (current owner, not pending owner)
209+ vm.prank (alice);
210+ vm.expectRevert ("Caller is not the pending owner " );
211+ wrapper.acceptSubscriptionOwnerTransfer (subId);
212+ }
213+
214+ /// @notice Tests that accepting transfer without pending transfer fails
215+ function test_subscriptionOwnershipTransfer_noPendingTransfer () public {
216+ ChainlinkVRFCoordinatorV2_5Adapter wrapper =
217+ new ChainlinkVRFCoordinatorV2_5Adapter (admin, address (randomnessSender));
218+
219+ vm.prank (alice);
220+ uint256 subId = wrapper.createSubscription ();
221+
222+ // Try to accept transfer without requesting it first
223+ vm.prank (bob);
224+ vm.expectRevert ("No pending ownership transfer " );
225+ wrapper.acceptSubscriptionOwnerTransfer (subId);
226+ }
227+
228+ /// @notice Tests that requesting transfer to zero address fails
229+ function test_subscriptionOwnershipTransfer_zeroAddressRevert () public {
230+ ChainlinkVRFCoordinatorV2_5Adapter wrapper =
231+ new ChainlinkVRFCoordinatorV2_5Adapter (admin, address (randomnessSender));
232+
233+ vm.prank (alice);
234+ uint256 subId = wrapper.createSubscription ();
235+
236+ // Try to request transfer to zero address
237+ vm.prank (alice);
238+ vm.expectRevert ("New owner cannot be zero address " );
239+ wrapper.requestSubscriptionOwnerTransfer (subId, address (0 ));
240+ }
241+
242+ /// @notice Tests that requesting transfer to same owner fails
243+ function test_subscriptionOwnershipTransfer_sameOwnerRevert () public {
244+ ChainlinkVRFCoordinatorV2_5Adapter wrapper =
245+ new ChainlinkVRFCoordinatorV2_5Adapter (admin, address (randomnessSender));
246+
247+ vm.prank (alice);
248+ uint256 subId = wrapper.createSubscription ();
249+
250+ // Try to request transfer to same owner
251+ vm.prank (alice);
252+ vm.expectRevert ("New owner is the same as current owner " );
253+ wrapper.requestSubscriptionOwnerTransfer (subId, alice);
254+ }
255+
256+ /// @notice Tests that pending transfer is cleared when subscription is cancelled
257+ function test_subscriptionOwnershipTransfer_clearedOnCancellation () public {
258+ ChainlinkVRFCoordinatorV2_5Adapter wrapper =
259+ new ChainlinkVRFCoordinatorV2_5Adapter (admin, address (randomnessSender));
260+
261+ vm.prank (alice);
262+ uint256 subId = wrapper.createSubscription ();
263+
264+ // Fund subscription
265+ vm.prank (alice);
266+ wrapper.fundSubscriptionWithNative {value: 1 ether }(subId);
267+
268+ // Request ownership transfer
269+ vm.prank (alice);
270+ wrapper.requestSubscriptionOwnerTransfer (subId, bob);
271+
272+ // Verify pending transfer exists
273+ assertTrue (wrapper.getPendingSubscriptionOwner (subId) == bob, "Bob should be pending owner " );
274+
275+ // Cancel subscription
276+ uint96 preBalance = uint96 (alice.balance);
277+ (, uint96 subBalance ,,,) = wrapper.getSubscription (subId);
278+ vm.prank (alice);
279+ wrapper.cancelSubscription (subId, alice);
280+
281+ // Check that alice received the subscription balance
282+ assertTrue (uint96 (alice.balance) >= preBalance + subBalance, "Alice should receive the subscription balance " );
283+
284+ // Verify pending transfer is cleared
285+ assertTrue (wrapper.getPendingSubscriptionOwner (subId) == address (0 ), "Pending owner should be cleared " );
286+ }
287+
288+ /// @notice Tests that adapter remains owner in underlying SubscriptionAPI after transfer
289+ function test_subscriptionOwnershipTransfer_adapterRemainsUnderlyingOwner () public {
290+ ChainlinkVRFCoordinatorV2_5Adapter wrapper =
291+ new ChainlinkVRFCoordinatorV2_5Adapter (admin, address (randomnessSender));
292+
293+ vm.prank (alice);
294+ uint256 subId = wrapper.createSubscription ();
295+
296+ // Transfer ownership to bob
297+ vm.prank (alice);
298+ wrapper.requestSubscriptionOwnerTransfer (subId, bob);
299+
300+ vm.prank (bob);
301+ wrapper.acceptSubscriptionOwnerTransfer (subId);
302+
303+ // Verify that the wrapper is still the owner in the underlying SubscriptionAPI
304+ (,, address underlyingOwner ,) = randomnessSender.getSubscription (subId);
305+ assertTrue (underlyingOwner == address (wrapper), "Wrapper should remain the owner in underlying SubscriptionAPI " );
306+
307+ // Verify that bob can manage the subscription through the wrapper
308+ vm.prank (bob);
309+ wrapper.addConsumer (subId, makeAddr ("new-consumer " ));
310+
311+ // Verify management functions work (no revert)
312+ vm.prank (bob);
313+ wrapper.removeConsumer (subId, makeAddr ("new-consumer " ));
314+ }
315+
316+ /// @notice Tests that multiple ownership transfers can be requested (overwriting pending)
317+ function test_subscriptionOwnershipTransfer_overwritePending () public {
318+ ChainlinkVRFCoordinatorV2_5Adapter wrapper =
319+ new ChainlinkVRFCoordinatorV2_5Adapter (admin, address (randomnessSender));
320+
321+ vm.prank (alice);
322+ uint256 subId = wrapper.createSubscription ();
323+
324+ // Request transfer to bob
325+ vm.prank (alice);
326+ wrapper.requestSubscriptionOwnerTransfer (subId, bob);
327+ assertTrue (wrapper.getPendingSubscriptionOwner (subId) == bob, "Bob should be pending owner " );
328+
329+ // Request transfer to charlie (should overwrite)
330+ vm.prank (alice);
331+ wrapper.requestSubscriptionOwnerTransfer (subId, charlie);
332+ assertTrue (wrapper.getPendingSubscriptionOwner (subId) == charlie, "Charlie should be pending owner " );
333+
334+ // Bob can no longer accept (since pending was overwritten)
335+ vm.prank (bob);
336+ vm.expectRevert ("Caller is not the pending owner " );
337+ wrapper.acceptSubscriptionOwnerTransfer (subId);
338+
339+ // Charlie can accept
340+ vm.prank (charlie);
341+ wrapper.acceptSubscriptionOwnerTransfer (subId);
342+ }
135343}
0 commit comments