datahaven-xyz/datahaven · error

Permit expired

Error message

Permit expired

What it means

The call-permit precompile's `dispatch` reverts with "Permit expired" when the current chain timestamp (converted from milliseconds to seconds) is greater than the `deadline` carried in the signed call permit. EIP-712-style permits have a bounded validity window; once past the deadline the signed authorization can no longer be dispatched.

Solutions

  1. Re-sign a fresh permit with a later deadline and retry the dispatch.
  2. Set generous deadlines (e.g. now + several minutes to hours) when creating permits to tolerate network delays.
  3. Monitor pending permit transactions and rebroadcast with higher gas priority before the deadline elapses.

Example fix

// before: short deadline, tx lands late
let deadline = U256::from(now_secs + 30); // 30s window
// ... tx mined after deadline -> revert("Permit expired")

// after
let deadline = U256::from(now_secs + 600); // 10-minute window
let permit = sign_permit(caller, deadline, ...);
Defensive patterns

Strategy: try-catch

Validate before calling

const now = Math.floor(Date.now() / 1000);
if (BigInt(deadline) < BigInt(now)) throw new Error('permit expired before dispatch');

Type guard

function isPermitLive(deadline, chainNowSecs) { return BigInt(deadline) >= BigInt(chainNowSecs); }

Try / catch

try { await callPermit.dispatch(call, permit); } catch (e) { if (e.message.includes('Permit expired')) { const fresh = signPermit({ deadline: nowSecs() + 600 }); return callPermit.dispatch(call, fresh); } throw e; }

Prevention

When it happens

Trigger: Calling `dispatch(call, permit)` where `deadline < now_seconds` — i.e. the EVM block timestamp (ms/1000) exceeds the permit's deadline field. Note the check is `deadline >= timestamp`, so a permit is only valid up to and including the deadline second.

Common situations: User signs a permit, transaction confirmation is delayed (congestion, low gas price), and by inclusion time the deadline has passed; timekeeping drift between the signing tool and chain time; reusing a cached permit long after signing.

Related errors


AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13). Data as JSON: /api/errors/7efa708f88a2e027. Report an issue: GitHub.

Appendix: source

Thrown at operator/precompiles/call-permit/src/lib.rs:181

        // ENSURE GASLIMIT IS SUFFICIENT
        let call_cost = call_cost(value, <Runtime as pallet_evm::Config>::config());

        let total_cost = gas_limit
            .checked_add(call_cost)
            .ok_or_else(|| revert("Call require too much gas (uint64 overflow)"))?;

        if total_cost > handle.remaining_gas() {
            return Err(revert("Gaslimit is too low to dispatch provided call"));
        }

        // VERIFY PERMIT

        // Blockchain time is in ms while Ethereum use second timestamps.
        let timestamp: u128 =
            <Runtime as pallet_evm::Config>::Timestamp::now().unique_saturated_into();
        let timestamp: U256 = U256::from(timestamp / 1000);

        ensure!(deadline >= timestamp, revert("Permit expired"));

        let nonce = NoncesStorage::get(from);

        let permit = Self::generate_permit(
            handle.context().address,
            from,
            to,
            value,
            data.clone(),
            gas_limit,
            nonce,
            deadline,
        );

        let mut sig = [0u8; 65];
        sig[0..32].copy_from_slice(&r.as_bytes());
        sig[32..64].copy_from_slice(&s.as_bytes());
        sig[64] = v;

View on GitHub (pinned to edcb13dbbc)