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
- Re-sign a fresh permit with a later deadline and retry the dispatch.
- Set generous deadlines (e.g. now + several minutes to hours) when creating permits to tolerate network delays.
- 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
- Use long deadlines (10+ minutes)
- Rebroadcast pending permit txs before expiry
- Read chain time, not local clock, when validating
- Rotate to fresh permits on any expiry
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)