{"record":{"id":"7efa708f88a2e027","repo":"datahaven-xyz/datahaven","slug":"permit-expired","errorCode":null,"errorMessage":"Permit expired","messagePattern":"Permit expired","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"operator/precompiles/call-permit/src/lib.rs","lineNumber":181,"sourceCode":"        // ENSURE GASLIMIT IS SUFFICIENT\n        let call_cost = call_cost(value, <Runtime as pallet_evm::Config>::config());\n\n        let total_cost = gas_limit\n            .checked_add(call_cost)\n            .ok_or_else(|| revert(\"Call require too much gas (uint64 overflow)\"))?;\n\n        if total_cost > handle.remaining_gas() {\n            return Err(revert(\"Gaslimit is too low to dispatch provided call\"));\n        }\n\n        // VERIFY PERMIT\n\n        // Blockchain time is in ms while Ethereum use second timestamps.\n        let timestamp: u128 =\n            <Runtime as pallet_evm::Config>::Timestamp::now().unique_saturated_into();\n        let timestamp: U256 = U256::from(timestamp / 1000);\n\n        ensure!(deadline >= timestamp, revert(\"Permit expired\"));\n\n        let nonce = NoncesStorage::get(from);\n\n        let permit = Self::generate_permit(\n            handle.context().address,\n            from,\n            to,\n            value,\n            data.clone(),\n            gas_limit,\n            nonce,\n            deadline,\n        );\n\n        let mut sig = [0u8; 65];\n        sig[0..32].copy_from_slice(&r.as_bytes());\n        sig[32..64].copy_from_slice(&s.as_bytes());\n        sig[64] = v;","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/datahaven-xyz/datahaven/blob/edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec/operator/precompiles/call-permit/src/lib.rs#L163-L199","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: short deadline, tx lands late\nlet deadline = U256::from(now_secs + 30); // 30s window\n// ... tx mined after deadline -> revert(\"Permit expired\")\n\n// after\nlet deadline = U256::from(now_secs + 600); // 10-minute window\nlet permit = sign_permit(caller, deadline, ...);","handlingStrategy":"try-catch","validationCode":"const now = Math.floor(Date.now() / 1000);\nif (BigInt(deadline) < BigInt(now)) throw new Error('permit expired before dispatch');","typeGuard":"function isPermitLive(deadline, chainNowSecs) { return BigInt(deadline) >= BigInt(chainNowSecs); }","tryCatchPattern":"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; }","preventionTips":["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"],"tags":["evm","precompile","permit","deadline","eip712"],"backgroundTag":"jwt-token-expired","analyzedSha":"edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec","analyzedAt":"2026-09-13T19:19:32.206Z","contentChangedAt":"2026-09-13T19:19:32.206Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}