fix: raise default transaction expiry to 120 seconds - #9
Conversation
The 20-second default was narrower than a hardware-wallet signing round trip. A device chooser plus an on-device confirmation routinely takes 15 to 60 seconds, and a browser extension adds an approval prompt on top. The node accepted the transaction into its mempool and then dropped it once expiry passed, so callers held a hash for a transaction that never confirmed. Callers wanting the old window can pass options.expireTimestamp or set transactionGenerationConfig.defaultTxnExpirySecFromNow.
|
Closing this in favour of fixing the error instead. The complaint is that a dropped transaction leaves you with a hash and nothing explaining it. That's a diagnosis problem, and it lives in waitForTransaction. A 404 is treated as retryable, so once the node drops the transaction the loop keeps polling a hash that no longer exists and ends at "timed out in pending state after 20 seconds". It never compares expiration_timestamp_secs against ledger time, so it cannot distinguish a slow transaction from one that is gone. The error points at timeoutSecs, which is the wrong lever. The targeted fix is to capture the expiry from the pending response and, once polls start failing, throw an error saying the transaction expired and was dropped. That resolves the complaint for every consumer and changes behaviour for none. Raising the default does change behaviour for everyone. Sequence-number ordering means a submitted transaction that never commits blocks later transactions from the same account until it expires, so 120 seconds turns a 20-second stall into a two-minute one. Flows that need a longer window can pass options.expireTimestamp or set transactionGenerationConfig.defaultTxnExpirySecFromNow, which is what Motion Wallet's hardware paths already do. |
DEFAULT_TXN_EXP_SEC_FROM_NOWgoes from 20 to 120 seconds.Twenty seconds is narrower than a hardware-wallet signing round trip. The device chooser plus on-device confirmation routinely runs 15 to 60 seconds, and a browser extension adds an approval prompt in front of that. The node accepts the transaction into its mempool, then drops it once expiry passes, so the caller gets a hash for a transaction that never confirms and no error to explain it.
The constant is the only fallback behind
MovementConfig.getDefaultTxnExpirySecFromNow(), andtransactionBuilder.tsis its single consumer. Explicitoptions.expireTimestampandtransactionGenerationConfig.defaultTxnExpirySecFromNowboth still take precedence, so nothing that already sets a window changes.No test asserted the old value.