{"record":{"id":"adb43869da694eda","repo":"datahaven-xyz/datahaven","slug":"invalidgateway-adb438","errorCode":"InvalidGateway","errorMessage":"InvalidGateway","messagePattern":"InvalidGateway","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"operator/pallets/outbound-queue-v2/src/lib.rs","lineNumber":402,"sourceCode":"            <PendingOrders<T>>::insert(nonce, order);\n\n            Nonce::<T>::set(nonce.checked_add(1).ok_or(Unsupported)?);\n\n            Self::deposit_event(Event::MessageAccepted { id, nonce });\n\n            Ok(true)\n        }\n\n        /// Process a delivery receipt from a relayer, to allocate the relayer reward.\n        pub fn process_delivery_receipt(\n            relayer: <T as frame_system::Config>::AccountId,\n            receipt: DeliveryReceiptOf<T>,\n        ) -> DispatchResult\n        where\n            <T as frame_system::Config>::AccountId: From<[u8; 32]>,\n        {\n            // Verify that the message was submitted from the known Gateway contract\n            ensure!(\n                T::GatewayAddress::get() == receipt.gateway,\n                Error::<T>::InvalidGateway\n            );\n\n            let nonce = receipt.nonce;\n\n            let order = <PendingOrders<T>>::get(nonce).ok_or(Error::<T>::InvalidPendingNonce)?;\n\n            if order.fee > 0 {\n                // Pay relayer reward\n                T::RewardPayment::register_reward(&relayer, T::DefaultRewardKind::get(), order.fee);\n            }\n\n            <PendingOrders<T>>::remove(nonce);\n\n            Self::deposit_event(Event::MessageDeliveryProofReceived { nonce });\n\n            Ok(())","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/datahaven-xyz/datahaven/blob/edcb13dbbcd3c29489eaa2480a6f60ee4cb1f3ec/operator/pallets/outbound-queue-v2/src/lib.rs#L384-L420","documentation":"`process_delivery_receipt` throws `Error::InvalidGateway` when the delivery receipt's `gateway` field does not equal the pallet's configured `T::GatewayAddress`. Just as with inbound messages, outbound delivery receipts must originate from the canonical Gateway contract on Ethereum; mismatched receipts are rejected before nonce/channel bookkeeping.","triggerScenarios":"Calling `process_delivery_receipt(channel_id, relayer, receipt)` where `receipt.gateway` (H160) differs from `T::GatewayAddress::get()` — a receipt from the wrong Ethereum network, a fork, or a re-deployed Gateway contract.","commonSituations":"Relayer listening to a testnet while the runtime targets mainnet (or the reverse); receipts replayed from anvil/local deployments with a different Gateway address; Gateway contract redeployed on Ethereum without updating the runtime constant.","solutions":["Verify the receipt's gateway against the runtime's configured GatewayAddress; re-fetch the receipt from the correct chain if they differ.","Fix the relayer configuration so it subscribes to Gateway events on the correct Ethereum network/contract.","If the Gateway address changed legitimately, ship a runtime upgrade updating `T::GatewayAddress`."],"exampleFix":"// before\nlet receipt = fetch_receipt_from(sepolia_gateway);\npallet.process_delivery_receipt(channel, relayer, receipt)?; // InvalidGateway\n\n// after\nassert_eq!(receipt.gateway, <Runtime as Config>::GatewayAddress::get());\npallet.process_delivery_receipt(channel, relayer, receipt)?;","handlingStrategy":"validation","validationCode":"if (receipt.gateway.toLowerCase() !== expectedGatewayAddress.toLowerCase()) throw new Error('receipt gateway mismatch');","typeGuard":"function isValidReceipt(receipt, expected) { return receipt && typeof receipt.gateway === 'string' && receipt.gateway.toLowerCase() === expected.toLowerCase(); }","tryCatchPattern":"try { pallet.process_delivery_receipt(channel, relayer, receipt); } catch (e) { if (matches!(e, Error::InvalidGateway)) { quarantineReceipt(receipt); } else { throw e; } }","preventionTips":["Subscribe to Gateway events on the correct network only","Validate receipt.gateway against runtime config pre-dispatch","Keep relayer chain config and runtime constants in sync","Watch for config drift after Gateway redeploys"],"tags":["substrate","bridge","gateway-address","delivery-receipt","relayer"],"backgroundTag":"invalid-argument-value","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"}