يوفر وقت تشغيل Surfpool ثلاثة أدوات مساعدة للسفر عبر الزمن. كل واحدة منها تنقل
الساعة المحلية إلى هدف مطلق (وليس إزاحة نسبية) وتُعيد EpochInfo المحدَّثة حتى
تتمكن الاختبارات من التحقق من أن وقت التشغيل وصل فعلاً إلى الوقت المطلوب.
| Helper | Target | Returns |
|---|---|---|
time_travel_to_slot / timeTravelToSlot | رقم slot مطلق | EpochInfo مع absolute_slot الجديدة |
time_travel_to_epoch / timeTravelToEpoch | رقم epoch مطلق | EpochInfo مع epoch الجديدة |
time_travel_to_timestamp / timeTravelToTimestamp | طابع زمني Unix بالميلي ثانية | EpochInfo يعكس الـ slot المستنتج |
للأمام فقط
لا يمكن للسفر عبر الزمن إلا أن يحرّك الساعة للأمام. أي هدف في الماضي يُرفض ولا يُتجاهل — تُعيد مساعدات Rust قيمة Err وتُطلق مساعدات JS استثناءً. تُشير رسالة الخطأ إلى الوحدة التي تراجعت إلى الوراء، على سبيل المثال:
Cannot travel to past slot: target=1000, current=2000.
الانتقال إلى slot
use surfpool_sdk::Surfnet;let surfnet = Surfnet::start().await?;let cheats = surfnet.cheatcodes();let info = cheats.time_travel_to_slot(1_000_000)?;assert!(info.absolute_slot >= 1_000_000);
الانتقال إلى epoch
let info = cheats.time_travel_to_epoch(420)?;assert_eq!(info.epoch, 420);
الانتقال إلى طابع زمني Unix
مرّر الطابع الزمني بوحدة المللي ثانية منذ Unix epoch — وهي الوحدة ذاتها التي تستخدمها Date.now() في JavaScript — وليس الثواني. يحسب وقت التشغيل أقرب slot لذلك الطابع الزمني.
// 2030-01-01T00:00:00Zlet info = cheats.time_travel_to_timestamp(1_893_456_000_000)?;
الساعة تُرجع قيمًا بالثواني، لا بالمللي ثانية
كل طابع زمني تقرأه من وقت التشغيل يكون بوحدة الثواني — سواء unix_timestamp في متغير النظام Clock على السلسلة، أو قيمة ClockValue التي يحملها حدث systemClockUpdated. إذا أعدت تمرير أي من هذه القيم مباشرةً إلى مساعد السفر عبر الزمن، فستُحسب كلحظة في مطلع عام 1970 وسيُرفض الاستدعاء باعتباره هدفًا في الماضي. اضرب القيمة في 1000 أولًا.
// WRONG — `unixTimestamp` is in seconds, so this is a past target.surfnet.timeTravelToTimestamp(clock.unixTimestamp + 3600);// RIGHT — convert to milliseconds.surfnet.timeTravelToTimestamp((clock.unixTimestamp + 3600) * 1000);
إيقاف الساعة واستئنافها
يقفز السفر عبر الزمن إلى هدف محدد؛ أما الإيقاف المؤقت فيمنع الساعة من التقدم كليًا. تُوقف surfnet_pauseClock إنتاج الـ slot وتقدم الوقت حتى يُنفَّذ surfnet_resumeClock — الجأ إليها حين يحتاج اختبارك إلى أن يظل الـ slot والطابع الزمني ثابتين عبر عدة تأكيدات.
لا يُغلّف Rust SDK ولا الفئة Surfnet في @solana/surfpool هذين الكودين الخفيين. استدعهما عبر إضافة Kit، أو مباشرةً عبر JSON-RPC.
const paused = await client.cheatcodes.pauseClock().send();// Nothing advances until the clock is resumed, so this is the only thing that// moves the slot.await client.cheatcodes.timeTravel({ absoluteSlot: paused.absoluteSlot + 1_000n }).send();await client.cheatcodes.resumeClock().send();
يُعيد كلا الكودين الخفيين قيمة EpochInfo — حالة الساعة لحظة الإيقاف المؤقت وبعد الاستئناف. لاحظ الأمرين اللذين لا تحملهما هذه البنية:
- لا يوجد طابع زمني. لا يحتوي
EpochInfoعلى حقل زمني. اقرأ وقت Unix المحاكى من متغير النظامClockبدلًا من ذلك: استخدمgetAccountInfoعلىSysvarC1ock11111111111111111111111111111111مع ترميزjsonParsed— وستكون القيمة بالثواني أيضًا. - لا يوجد مؤشر للإيقاف المؤقت. لا توجد طريقة RPC تُخبرك بما إذا كانت الساعة موقوفة حاليًا. من خلال حزم SDK، راقب حدث
clockUpdateالذي يُطلَق معclockCommandعند تنفيذ أي إيقاف مؤقت أو استئناف أو تغيير في الفترة الزمنية. وإلا، تتبّع الحالة بنفسك في اختبارك.
الأنماط الشائعة
اختبار نافذة قفل أو استحقاق
المثال أدناه هو مخطط تقريبي — assertWithdrawFails و assertWithdrawSucceeds
هي عناصر نائبة لأي أدوات مساعدة من جانب العميل تستخدمها مجموعة الاختبارات لديك
للتحقق من سلوك RPC.
import { Surfnet } from "@solana/surfpool";const surfnet = Surfnet.start();const beneficiary = Surfnet.newKeypair();// 1. Set up a vesting account that unlocks at slot 1,000,000.surfnet.setAccount(/* ...lockup program state... */);// 2. Verify withdrawal fails before unlock.await assertWithdrawFails(surfnet.rpcUrl, beneficiary);// 3. Travel past the unlock slot.surfnet.timeTravelToSlot(1_000_001);// 4. Verify withdrawal succeeds.await assertWithdrawSucceeds(surfnet.rpcUrl, beneficiary);surfnet.stop();
تشغيل سيناريو متعدد الـ epoch
يتم تخطي الـ slot الوسيطة
السفر عبر الزمن ينتقل مباشرةً إلى الهدف — الانتقال من epoch 1 إلى epoch 5
يتخطى الـ slot الواقعة بينهما. إذا كان برنامجك يحتاج إلى تفعيل كل حد epoch
وسيط (مثلاً لإضافة المكافآت لكل epoch)، استدعِ time_travel_to_epoch مرة
واحدة لكل epoch مع أي معاملات مطلوبة بينهما.
for epoch in 2..=5 {cheats.time_travel_to_epoch(epoch)?;surfnet.rpc_client().send_transaction(&claim_rewards_tx)?;}
شكل EpochInfo
يُعيد كلا الـ SDK كائنًا بشكل EpochInfo يحتوي على هذه الحقول:
| الحقل | Rust | JS |
|---|---|---|
| الـ slot المطلق | absolute_slot: u64 | absoluteSlot: number |
| الـ slot ضمن الـ epoch | slot_index: u64 | slotIndex: number |
| عدد الـ slot في الـ epoch | slots_in_epoch: u64 | slotsInEpoch: number |
| رقم الـ epoch | epoch: u64 | epoch: number |
| ارتفاع الكتلة | block_height: u64 | blockHeight: number |
| عدد المعاملات | transaction_count: Option<u64> | transactionCount?: number |
Is this page helpful?