|
| 1 | +--- |
| 2 | +title: Expiring Nonces |
| 3 | +description: Use Tempo expiring nonces to submit time-bounded transactions without managing sequential account nonces. |
| 4 | +--- |
| 5 | + |
| 6 | +import { Cards, Card } from 'vocs' |
| 7 | + |
| 8 | +# Expiring Nonces |
| 9 | + |
| 10 | +Expiring nonces let Tempo Transactions use time-bounded replay protection instead of a sequential account nonce. They are useful when you need to submit independent transactions concurrently, recover cleanly from dropped transactions, or operate relayers that should not coordinate one global nonce stream. |
| 11 | + |
| 12 | +When a transaction uses an expiring nonce, Tempo identifies the transaction by its hash and accepts it only until its `validBefore` timestamp. After the validity window closes, the transaction cannot be included and the nonce entry can be evicted from protocol storage. |
| 13 | + |
| 14 | +## When to use expiring nonces |
| 15 | + |
| 16 | +Use expiring nonces for: |
| 17 | + |
| 18 | +- Parallel user actions where one delayed transaction should not block another. |
| 19 | +- Gasless or meta-transaction flows where relayers submit transactions for many users. |
| 20 | +- Short-lived automated actions that should fail closed if they are not included quickly. |
| 21 | +- Access-key flows where the signature should only be usable for a narrow time window. |
| 22 | + |
| 23 | +Use regular sequential nonces or 2D nonce keys when you need strict ordering within a transaction stream. |
| 24 | + |
| 25 | +## Transaction fields |
| 26 | + |
| 27 | +Set the Tempo Transaction nonce fields as follows: |
| 28 | + |
| 29 | +| Field | Value | |
| 30 | +| --- | --- | |
| 31 | +| `nonceKey` | `uint256.max` | |
| 32 | +| `nonce` | `0` | |
| 33 | +| `validBefore` | Unix timestamp in seconds, within the next 30 seconds | |
| 34 | + |
| 35 | +The transaction is valid only while `block.timestamp < validBefore`. Transactions with a `validBefore` timestamp in the past, too close to the current block timestamp, or more than 30 seconds in the future are rejected. |
| 36 | + |
| 37 | +## Foundry example |
| 38 | + |
| 39 | +Use `--tempo.expiring-nonce` and set `--tempo.valid-before` to a timestamp inside the 30-second validity window: |
| 40 | + |
| 41 | +```bash |
| 42 | +VALID_BEFORE=$(($(date +%s) + 25)) |
| 43 | + |
| 44 | +cast send <CONTRACT_ADDRESS> 'increment()' \ |
| 45 | + --rpc-url $TEMPO_RPC_URL \ |
| 46 | + --private-key $PRIVATE_KEY \ |
| 47 | + --tempo.expiring-nonce \ |
| 48 | + --tempo.valid-before $VALID_BEFORE |
| 49 | +``` |
| 50 | + |
| 51 | +For local testing, the same flags work with Anvil in Tempo mode: |
| 52 | + |
| 53 | +```bash |
| 54 | +anvil --tempo --hardfork t3 |
| 55 | + |
| 56 | +VALID_BEFORE=$(($(date +%s) + 25)) |
| 57 | + |
| 58 | +cast send <CONTRACT_ADDRESS> 'increment()' \ |
| 59 | + --rpc-url http://127.0.0.1:8545 \ |
| 60 | + --private-key $PRIVATE_KEY \ |
| 61 | + --tempo.expiring-nonce \ |
| 62 | + --tempo.valid-before $VALID_BEFORE |
| 63 | +``` |
| 64 | + |
| 65 | +## Replay protection |
| 66 | + |
| 67 | +Tempo records the transaction hash with its expiry timestamp. If the same transaction hash is seen again before the expiry timestamp, it is rejected as a replay. After expiry, the entry is no longer valid and can be removed from the fixed-size expiring nonce buffer. |
| 68 | + |
| 69 | +This means expiring nonces do not create a permanent account-level nonce queue. Each transaction stands on its own, and independent transactions can be sent at the same time. |
| 70 | + |
| 71 | +## Practical guidance |
| 72 | + |
| 73 | +- Pick a `validBefore` value close to the current time. `now + 20` to `now + 25` seconds leaves enough room for normal submission without hitting the 30-second upper bound. |
| 74 | +- Rebuild and re-sign a transaction if the validity window expires before inclusion. |
| 75 | +- Do not reuse the exact same signed transaction during its validity window; the protocol treats that as a replay. |
| 76 | +- Keep using ordered nonce streams for workflows where transaction B must execute only after transaction A. |
| 77 | + |
| 78 | +## Related docs |
| 79 | + |
| 80 | +<Cards> |
| 81 | + <Card |
| 82 | + description="Submit multiple independent transactions concurrently with SDK helpers." |
| 83 | + to="/guide/payments/send-parallel-transactions" |
| 84 | + icon="lucide:zap" |
| 85 | + title="Send Parallel Transactions" |
| 86 | + /> |
| 87 | + <Card |
| 88 | + description="Read the full transaction type and nonce field specification." |
| 89 | + to="/protocol/transactions/spec-tempo-transaction" |
| 90 | + icon="lucide:file-code" |
| 91 | + title="Tempo Transaction Spec" |
| 92 | + /> |
| 93 | + <Card |
| 94 | + description="Read the protocol proposal for expiring nonces." |
| 95 | + to="https://tips.sh/1009" |
| 96 | + icon="lucide:scroll-text" |
| 97 | + title="TIP-1009" |
| 98 | + /> |
| 99 | +</Cards> |
0 commit comments