DevMode Timestamp Offset
Description
Section titled “Description”This example demonstrates how to manage block timestamp offset in DevMode using:
- blockTimeStampOffset() - Get the current timestamp offset
- setBlockTimeStampOffset(offset) - Set a new timestamp offset In DevMode, you can control the timestamp of blocks by setting an offset. This is useful for testing time-dependent smart contracts without waiting for real time to pass. Key concepts:
- Timestamp offset is in seconds
- Setting offset to 0 resets to using the real clock
- New blocks will have timestamps = realTime + offset
- These endpoints only work on DevMode nodes (LocalNet in dev mode)
Prerequisites
Section titled “Prerequisites”- LocalNet running in dev mode (via
algokit localnet start) - Note: These endpoints return HTTP 404 if not running on a DevMode node.
Run This Example
Section titled “Run This Example”From the repository root:
cd examplesnpm run example algod_client/18-devmode-timestamp.ts/** * Example: DevMode Timestamp Offset * * This example demonstrates how to manage block timestamp offset in DevMode using: * - blockTimeStampOffset() - Get the current timestamp offset * - setBlockTimeStampOffset(offset) - Set a new timestamp offset * * In DevMode, you can control the timestamp of blocks by setting an offset. * This is useful for testing time-dependent smart contracts without waiting * for real time to pass. * * Key concepts: * - Timestamp offset is in seconds * - Setting offset to 0 resets to using the real clock * - New blocks will have timestamps = realTime + offset * - These endpoints only work on DevMode nodes (LocalNet in dev mode) * * Prerequisites: * - LocalNet running in dev mode (via `algokit localnet start`) * * Note: These endpoints return HTTP 404 if not running on a DevMode node. */
import { microAlgo } from '@algorandfoundation/algokit-utils'import type { GetBlockTimeStampOffsetResponse } from '@algorandfoundation/algokit-utils/algod-client'import { createAlgodClient, createAlgorandClient, getFundedAccount, printError, printHeader, printInfo, printStep, printSuccess,} from '../shared/utils.js'
async function main() { printHeader('DevMode Timestamp Offset Example')
// Create clients const algod = createAlgodClient() const algorand = createAlgorandClient()
// Check if we're running on DevMode printStep(1, 'Checking if running on DevMode')
// On DevMode, blockTimeStampOffset() returns 404 when offset was never set. // We detect DevMode by checking the error message - "block timestamp offset was never set" // means DevMode is active but no offset is set (default behavior). // A different 404 error means the node is not running DevMode. let isDevMode = false let offsetNeverSet = false
try { await algod.blockTimeStampOffset() isDevMode = true printSuccess('Running on DevMode - timestamp offset endpoints are available') } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error)
// Check if this is the "never set" message - this means DevMode IS running if (errorMessage.includes('block timestamp offset was never set')) { isDevMode = true offsetNeverSet = true printSuccess('Running on DevMode - timestamp offset was never set (using real clock)') printInfo('The blockTimeStampOffset() endpoint returns 404 when offset was never set.') printInfo('Setting an offset to 0 will initialize it.') } else if (errorMessage.includes('404') || errorMessage.includes('not found') || errorMessage.includes('Not Found')) { printError('Not running on DevMode - timestamp offset endpoints are not available') printInfo('These endpoints only work on LocalNet in dev mode.') printInfo('Start LocalNet with: algokit localnet start') printInfo('') printHeader('Summary') printInfo('DevMode Timestamp Offset endpoints require a DevMode node.') printInfo('On non-DevMode nodes, these endpoints return HTTP 404.') return } else { throw error } } printInfo('')
if (!isDevMode) { return }
// If offset was never set, initialize it by setting to 0 if (offsetNeverSet) { printInfo('Initializing timestamp offset by setting it to 0...') await algod.setBlockTimeStampOffset(0) printSuccess('Timestamp offset initialized to 0') printInfo('') }
// ========================================================================= // Step 2: Get the current timestamp offset // ========================================================================= printStep(2, 'Getting current timestamp offset')
const initialOffset = await algod.blockTimeStampOffset() displayTimestampOffset(initialOffset)
// ========================================================================= // Step 3: Get a baseline block timestamp // ========================================================================= printStep(3, 'Getting baseline block timestamp')
// Get the current time for comparison const realTimeNow = Math.floor(Date.now() / 1000) printInfo(`Real time (system clock): ${realTimeNow}`) printInfo(`Real time (formatted): ${new Date(realTimeNow * 1000).toISOString()}`) printInfo('')
// Trigger a new block to see the current block timestamp // We use a self-payment (send to self) to trigger a block without minimum balance issues const sender = await getFundedAccount(algorand)
printInfo('Submitting a transaction to trigger a new block...') const result1 = await algorand.send.payment({ sender: sender.addr, receiver: sender.addr, // Self-payment to trigger block amount: microAlgo(0), note: new TextEncoder().encode('baseline-block'), })
// Get the block to see its timestamp const block1 = await algod.block(result1.confirmation.confirmedRound!) const baselineTimestamp = block1.block.header.timestamp printSuccess(`Block ${result1.confirmation.confirmedRound} created`) printInfo(`Block timestamp: ${baselineTimestamp}`) printInfo(`Block timestamp (formatted): ${new Date(Number(baselineTimestamp) * 1000).toISOString()}`) printInfo('')
// ========================================================================= // Step 4: Set a timestamp offset // ========================================================================= printStep(4, 'Setting a timestamp offset')
// Set offset to 1 hour (3600 seconds) in the future const oneHourInSeconds = 3600 printInfo(`Setting timestamp offset to ${oneHourInSeconds} seconds (1 hour in the future)...`)
try { await algod.setBlockTimeStampOffset(oneHourInSeconds) printSuccess(`Timestamp offset set to ${oneHourInSeconds} seconds`) } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) printError(`Failed to set timestamp offset: ${errorMessage}`) return } printInfo('')
// Verify the offset was set const newOffset = await algod.blockTimeStampOffset() printInfo('Verifying the offset was set:') displayTimestampOffset(newOffset)
// ========================================================================= // Step 5: See how timestamp offset affects block timestamps // ========================================================================= printStep(5, 'Observing the effect on block timestamps')
printInfo('Submitting another transaction to trigger a new block with the offset applied...') const result2 = await algorand.send.payment({ sender: sender.addr, receiver: sender.addr, // Self-payment to trigger block amount: microAlgo(0), note: new TextEncoder().encode('offset-block-1h'), })
// Get the new block's timestamp const block2 = await algod.block(result2.confirmation.confirmedRound!) const offsetTimestamp = block2.block.header.timestamp printSuccess(`Block ${result2.confirmation.confirmedRound} created`) printInfo('')
// Compare timestamps printInfo('Comparing block timestamps:') printInfo(` Baseline block (round ${result1.confirmation.confirmedRound}):`) printInfo(` Timestamp: ${baselineTimestamp}`) printInfo(` Formatted: ${new Date(Number(baselineTimestamp) * 1000).toISOString()}`) printInfo('') printInfo(` Offset block (round ${result2.confirmation.confirmedRound}):`) printInfo(` Timestamp: ${offsetTimestamp}`) printInfo(` Formatted: ${new Date(Number(offsetTimestamp) * 1000).toISOString()}`) printInfo('')
// Calculate actual difference const timeDiff = Number(offsetTimestamp - baselineTimestamp) printInfo(`Time difference between blocks: ${timeDiff} seconds`) printInfo(`Expected offset: ${oneHourInSeconds} seconds`) printInfo('')
// Note: The actual difference may not exactly match the offset due to real time passing // between the two transactions, but it should be close to the offset value if (timeDiff >= oneHourInSeconds - 10 && timeDiff <= oneHourInSeconds + 60) { printSuccess('Block timestamp reflects the offset (within expected margin)') } else { printInfo('Note: Actual time difference may vary due to real time elapsed between transactions') } printInfo('')
// ========================================================================= // Step 6: Test with different offset values // ========================================================================= printStep(6, 'Testing different offset values')
// Try setting a larger offset (1 day = 86400 seconds) const oneDayInSeconds = 86400 printInfo(`Setting timestamp offset to ${oneDayInSeconds} seconds (1 day in the future)...`) await algod.setBlockTimeStampOffset(oneDayInSeconds)
const dayOffset = await algod.blockTimeStampOffset() printSuccess(`Timestamp offset set to ${dayOffset.offset} seconds`) printInfo('')
// Trigger another block printInfo('Creating a block with 1-day offset...') const result3 = await algorand.send.payment({ sender: sender.addr, receiver: sender.addr, // Self-payment to trigger block amount: microAlgo(0), note: new TextEncoder().encode('offset-block-1d'), })
const block3 = await algod.block(result3.confirmation.confirmedRound!) const futureDayTimestamp = block3.block.header.timestamp printInfo(` Block ${result3.confirmation.confirmedRound} timestamp: ${new Date(Number(futureDayTimestamp) * 1000).toISOString()}`) printInfo('')
// ========================================================================= // Step 7: Reset the offset to 0 // ========================================================================= printStep(7, 'Resetting the timestamp offset to 0')
printInfo('Setting timestamp offset back to 0 (real clock)...') await algod.setBlockTimeStampOffset(0)
const resetOffset = await algod.blockTimeStampOffset() printSuccess('Timestamp offset reset to 0') displayTimestampOffset(resetOffset)
// Verify by creating another block printInfo('Creating a block with real clock timestamp...') const result4 = await algorand.send.payment({ sender: sender.addr, receiver: sender.addr, // Self-payment to trigger block amount: microAlgo(0), note: new TextEncoder().encode('reset-block'), })
const block4 = await algod.block(result4.confirmation.confirmedRound!) const realTimestamp = block4.block.header.timestamp const currentRealTime = Math.floor(Date.now() / 1000) printInfo(` Block ${result4.confirmation.confirmedRound} timestamp: ${new Date(Number(realTimestamp) * 1000).toISOString()}`) printInfo(` Current real time: ${new Date(currentRealTime * 1000).toISOString()}`)
const diffFromReal = Math.abs(Number(realTimestamp) - currentRealTime) if (diffFromReal < 60) { printSuccess('Block timestamp is back to real time (within 60 seconds)') } else { printInfo(`Block timestamp differs from real time by ${diffFromReal} seconds`) } printInfo('')
// ========================================================================= // Summary // ========================================================================= printHeader('Summary')
printInfo('DevMode Timestamp Offset - Key Points:') printInfo('') printInfo('1. What It Does:') printInfo(' - Allows you to control block timestamps in DevMode') printInfo(' - Useful for testing time-dependent smart contracts') printInfo(' - New blocks will have timestamps = realTime + offset') printInfo('') printInfo('2. API Methods:') printInfo(' blockTimeStampOffset(): Promise<GetBlockTimeStampOffsetResponse>') printInfo(' - Returns { offset: number } with current offset in seconds') printInfo('') printInfo(' setBlockTimeStampOffset(offset: number): Promise<void>') printInfo(' - Sets the timestamp offset in seconds') printInfo(' - offset = 0 resets to using real clock') printInfo('') printInfo('3. GetBlockTimeStampOffsetResponse:') printInfo(' {') printInfo(' offset: number // Timestamp offset in seconds') printInfo(' }') printInfo('') printInfo('4. Use Cases:') printInfo(' - Testing time-locked smart contracts') printInfo(' - Simulating future block timestamps') printInfo(' - Testing vesting schedules') printInfo(' - Testing auction end times') printInfo(' - Any time-dependent logic verification') printInfo('') printInfo('5. Important Notes:') printInfo(' - Only works on DevMode nodes (LocalNet in dev mode)') printInfo(' - Returns HTTP 404 on non-DevMode nodes') printInfo(' - Offset is in seconds (not milliseconds)') printInfo(' - Always reset offset to 0 after testing') printInfo(' - The offset affects ALL new blocks until changed') printInfo('') printInfo('6. Best Practices:') printInfo(' - Always check if DevMode is available before using') printInfo(' - Reset offset to 0 in cleanup/finally blocks') printInfo(' - Document time-sensitive test assumptions') printInfo(' - Use try/finally to ensure cleanup happens')}
/** * Display the timestamp offset information */function displayTimestampOffset(response: GetBlockTimeStampOffsetResponse): void { printInfo(' GetBlockTimeStampOffsetResponse:') printInfo(` offset: ${response.offset} seconds`)
if (response.offset === 0) { printInfo(' (Using real clock - no offset applied)') } else if (response.offset > 0) { const hours = Math.floor(response.offset / 3600) const minutes = Math.floor((response.offset % 3600) / 60) const seconds = response.offset % 60 printInfo(` (${hours}h ${minutes}m ${seconds}s in the future)`) } else { const absOffset = Math.abs(response.offset) const hours = Math.floor(absOffset / 3600) const minutes = Math.floor((absOffset % 3600) / 60) const seconds = absOffset % 60 printInfo(` (${hours}h ${minutes}m ${seconds}s in the past)`) } printInfo('')}
main().catch((error) => { console.error('Fatal error:', error) process.exit(1)})Other examples in Algod Client
Section titled “Other examples in Algod Client”- Node Health and Status
- Version and Genesis Information
- Ledger Supply Information
- Account Information
- Transaction Parameters
- Send and Confirm Transaction
- Pending Transactions
- Block Data
- Asset Information
- Application Information
- Application Boxes
- TEAL Compile and Disassemble
- Transaction Simulation
- Ledger State Deltas
- Transaction Proof
- Light Block Header Proof
- State Proof
- DevMode Timestamp Offset
- Sync Round Management