datahaven-xyz/datahaven · error

Permit expired

Error message

Permit expired

What it means

Reverts the EIP-2612 permit precompile call because the deadline parameter encoded in the signed permit authorization is in the past: the current chain timestamp (converted from milliseconds to Ethereum-style seconds) now exceeds the supplied deadline, so the signature's validity window has closed and the approval can no longer be executed.

Solutions

  1. Submit the permit transaction before the deadline specified in the signed message
  2. Re-sign the permit with a later deadline (or the typical max uint256 for no expiry) and submit the new signature
  3. Synchronize the signer's clock and deadline calculation with the chain's timestamp, remembering the chain stores time in milliseconds while EIP-2612 deadlines are in seconds
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at operator/precompiles/erc20-balances/src/eip2612.rs:119 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at operator/precompiles/erc20-balances/src/eip2612.rs:119

        spender: Address,
        value: U256,
        deadline: U256,
        v: u8,
        r: H256,
        s: H256,
    ) -> EvmResult {
        // NoncesStorage: Blake2_128(16) + contract(20) + Blake2_128(16) + owner(20) + nonce(32)
        handle.record_db_read::<Runtime>(104)?;

        let owner: H160 = owner.into();
        let spender: H160 = spender.into();

        // 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::<Instance>::get(owner);

        let permit = Self::generate_permit(
            handle.context().address,
            owner,
            spender,
            value,
            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;

        let signer = sp_io::crypto::secp256k1_ecdsa_recover(&sig, &permit)

View on GitHub (pinned to edcb13dbbc)