datahaven-xyz/datahaven · error · Error

InvalidGateway

InvalidGateway

Error message

InvalidGateway

What it means

`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.

Solutions

  1. Verify the receipt's gateway against the runtime's configured GatewayAddress; re-fetch the receipt from the correct chain if they differ.
  2. Fix the relayer configuration so it subscribes to Gateway events on the correct Ethereum network/contract.
  3. If the Gateway address changed legitimately, ship a runtime upgrade updating `T::GatewayAddress`.

Example fix

// before
let receipt = fetch_receipt_from(sepolia_gateway);
pallet.process_delivery_receipt(channel, relayer, receipt)?; // InvalidGateway

// after
assert_eq!(receipt.gateway, <Runtime as Config>::GatewayAddress::get());
pallet.process_delivery_receipt(channel, relayer, receipt)?;
Defensive patterns

Strategy: validation

Validate before calling

if (receipt.gateway.toLowerCase() !== expectedGatewayAddress.toLowerCase()) throw new Error('receipt gateway mismatch');

Type guard

function isValidReceipt(receipt, expected) { return receipt && typeof receipt.gateway === 'string' && receipt.gateway.toLowerCase() === expected.toLowerCase(); }

Try / catch

try { pallet.process_delivery_receipt(channel, relayer, receipt); } catch (e) { if (matches!(e, Error::InvalidGateway)) { quarantineReceipt(receipt); } else { throw e; } }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at operator/pallets/outbound-queue-v2/src/lib.rs:402

            <PendingOrders<T>>::insert(nonce, order);

            Nonce::<T>::set(nonce.checked_add(1).ok_or(Unsupported)?);

            Self::deposit_event(Event::MessageAccepted { id, nonce });

            Ok(true)
        }

        /// Process a delivery receipt from a relayer, to allocate the relayer reward.
        pub fn process_delivery_receipt(
            relayer: <T as frame_system::Config>::AccountId,
            receipt: DeliveryReceiptOf<T>,
        ) -> DispatchResult
        where
            <T as frame_system::Config>::AccountId: From<[u8; 32]>,
        {
            // Verify that the message was submitted from the known Gateway contract
            ensure!(
                T::GatewayAddress::get() == receipt.gateway,
                Error::<T>::InvalidGateway
            );

            let nonce = receipt.nonce;

            let order = <PendingOrders<T>>::get(nonce).ok_or(Error::<T>::InvalidPendingNonce)?;

            if order.fee > 0 {
                // Pay relayer reward
                T::RewardPayment::register_reward(&relayer, T::DefaultRewardKind::get(), order.fee);
            }

            <PendingOrders<T>>::remove(nonce);

            Self::deposit_event(Event::MessageDeliveryProofReceived { nonce });

            Ok(())

View on GitHub (pinned to edcb13dbbc)