Skip to main content

Troubleshooting

Common errors, how to handle them, and debugging tips for Signet development.

Common errors and debugging tips for Signet development. Select your language to see relevant code.

My transaction reverted

If an Ethereum transaction calling Passage or the Transactor reverts, nothing happens on Signet. No tokens are minted, no state changes, no funds stuck in transit.

Check the revert reason:

typescript
try {
  const hash = await hostWalletClient.writeContract({ ... });
  const receipt = await hostPublicClient.waitForTransactionReceipt({ hash });
  if (receipt.status === 'reverted') {
    console.error('Transaction reverted');
  }
} catch (err) {
  console.error('Revert reason:', err.shortMessage || err.message);
}

Common causes: insufficient gas, ERC-20 not approved for the target contract, unsupported token.

My order isn’t getting filled

Fillers choose which orders to fill based on profitability. If your order sits unfilled, the spread between input and output is likely too narrow.

typescript
const inputAmount = parseEther('1');           // 1 WETH on Signet
const outputAmount = (inputAmount * 995n) / 1000n;  // 0.5% to fillers

If that’s not getting filled, widen to 1% or more. See pricing guidance.

When the deadline passes, the order expires and the user’s tokens remain on Signet. No funds are lost.

My order submission was rejected

The tx-cache rejects orders with invalid Permit2 signatures or already-consumed nonces. Run a feasibility check before submitting:

typescript
import { checkOrderFeasibility } from '@signet-sh/sdk/signing';

const result = await checkOrderFeasibility(signetPublicClient, signedOrder);
if (result.issues.length > 0) {
  for (const issue of result.issues) {
    console.error(`${issue.type}: need ${issue.required}, have ${issue.available}`);
  }
}

Common issues caught by the feasibility check:

  • insufficient_balance: the user doesn’t have enough of the input token
  • insufficient_allowance: Permit2 hasn’t been approved for the input token
  • nonce_used: the Permit2 nonce was already consumed by a previous order
  • deadline_expired: the order deadline has already passed

I can’t cancel my order

On-chain orders created via initiate cannot be cancelled. The deadline is the only expiry mechanism.

For gasless Permit2-based orders, cancel by consuming the nonce:

typescript
import { isNonceUsed } from '@signet-sh/sdk/signing';

const used = await isNonceUsed(signetPublicClient, userAddress, orderNonce);
if (!used) {
  // Submit any transaction using the same nonce to invalidate the order
}

My bundle wasn’t included

Check that blockNumber targets a future block:

typescript
const currentBlock = await signetPublicClient.getBlockNumber();
console.log('Current block:', currentBlock, 'Bundle targets:', bundle.blockNumber);
// bundle.blockNumber must be > currentBlock

Other causes:

  • The bundle wasn’t profitable enough for the builder
  • A non-revertible transaction in the bundle would revert (the entire bundle is dropped). Mark transactions that are allowed to fail with revertingTxHashes.
  • The bundle expired (default TTL is 10 minutes / ~50 blocks)

Simulation results differ from execution

On-chain state can change between simulation and inclusion. Simulate close to submission time:

typescript
import { SignetCallBundleBuilder } from '@signet-sh/sdk/signing';

const simulation = SignetCallBundleBuilder.new()
  .withTxs(bundle.txs)
  .withBlockNumber(targetBlock)
  .withStateBlockNumber('latest')  // simulate against current state
  .build();

Use minTimestamp/maxTimestamp on the bundle to constrain the execution window.

Unexpected balance values

Signet’s native asset is USD, not ETH. msg.value, address.balance, and tx.value all denominate in USD with 18 decimals.

typescript
// This returns USD balance, not ETH
const balance = await signetPublicClient.getBalance({ address: userAddress });
console.log('USD balance:', formatEther(balance));

See EVM behavior for all differences from Ethereum.

Wrong chain or wrong network

Parmigiana testnet: rollup chain ID 88888, host chain ID 3151908. Mainnet: rollup 519, host 1.

typescript
const chainId = await signetPublicClient.getChainId();
console.log('Connected to chain:', chainId);
// Should be 88888 for Parmigiana rollup

Permit2 approval missing

Gasless orders require a one-time Permit2 approval per input token:

typescript
import { ensurePermit2Approval } from '@signet-sh/sdk/permit2';

const { approved, txHash } = await ensurePermit2Approval(
  signetWalletClient,
  signetPublicClient,
  { token: inputTokenAddress, owner: userAddress, amount: orderAmount }
);

The SDK’s ensurePermit2Approval checks the current allowance first, handles USDT-style tokens that require resetting to zero, and sets the allowance to maxUint256.

Receipt call hangs

Signet spans two chains. If waitForTransactionReceipt never resolves, the public client is pointed at the wrong one. A receipt call to Signet won’t find a transaction sent to Ethereum, and vice versa.

typescript
// Wrong: waiting on Signet for an Ethereum transaction
const receipt = await signetPublicClient.waitForTransactionReceipt({ hash: ethTxHash });

// Right: match the client to the chain where the tx was sent
const receipt = await hostPublicClient.waitForTransactionReceipt({ hash: ethTxHash });

Token not supported

Passage supports a fixed set of bridgeable tokens. If a deposit fails, check the supported list on the Parmigiana quickstart.

On Parmigiana testnet: ETH, USDC, USDT, and WBTC.

My transaction reverted

If an Ethereum transaction calling Passage or the Transactor reverts, nothing happens on Signet. No tokens are minted, no state changes, no funds stuck in transit.

Check the revert reason:

rust
match provider.send_transaction(tx).await {
    Ok(pending) => {
        let receipt = pending.get_receipt().await?;
        if !receipt.status() {
            eprintln!("Transaction reverted in block {}", receipt.block_number.unwrap());
        }
    }
    Err(e) => eprintln!("Send failed: {e:?}"),
}

Common causes: insufficient gas, ERC-20 not approved for the target contract, unsupported token.

My order isn’t getting filled

Fillers choose which orders to fill based on profitability. If your order sits unfilled, the spread between input and output is likely too narrow.

rust
let input_amount = U256::from(1_000_000_000_000_000_000u128); // 1 WETH
let output_amount = input_amount * U256::from(995) / U256::from(1000); // 0.5% to fillers

If that’s not getting filled, widen to 1% or more. See pricing guidance.

When the deadline passes, the order expires and the user’s tokens remain on Signet. No funds are lost.

My order submission was rejected

The tx-cache rejects orders with invalid Permit2 signatures or already-consumed nonces.

Common issues:

  • Insufficient balance: the user doesn’t have enough of the input token
  • Insufficient allowance: Permit2 hasn’t been approved for the input token
  • Nonce used: the Permit2 nonce was already consumed by a previous order
  • Deadline expired: the order deadline has already passed

I can’t cancel my order

On-chain orders created via initiate cannot be cancelled. The deadline is the only expiry mechanism.

For gasless Permit2-based orders, cancel by consuming the nonce:

rust
// Read the nonce bitmap from Permit2
let word_pos = order_nonce / 256;
let bit_pos = order_nonce % 256;
let bitmap = permit2.nonceBitmap(owner, word_pos).call().await?._0;
let is_used = (bitmap >> bit_pos) & U256::from(1) == U256::from(1);

My bundle wasn’t included

Check that block_number targets a future block:

rust
let current_block = provider.get_block_number().await?;
println!("Current: {}, Target: {}", current_block, bundle.block_number);

Other causes:

  • The bundle wasn’t profitable enough for the builder
  • A non-revertible transaction in the bundle would revert (the entire bundle is dropped). Mark transactions that are allowed to fail with reverting_tx_hashes.
  • The bundle expired (default TTL is 10 minutes / ~50 blocks)

Simulation results differ from execution

On-chain state can change between simulation and inclusion. Simulate close to submission time:

rust
use signet_bundle::SignetCallBundle;
use alloy::eips::BlockNumberOrTag;

let bundle = SignetCallBundle {
    bundle: EthCallBundle {
        txs: signed_txs,
        block_number: target_block,
        state_block_number: BlockNumberOrTag::Latest,
        ..Default::default()
    },
};
let response = provider.client().request("signet_callBundle", (bundle,)).await?;

Use min_timestamp/max_timestamp on the bundle to constrain the execution window.

Unexpected balance values

Signet’s native asset is USD, not ETH. msg.value, address.balance, and tx.value all denominate in USD with 18 decimals.

rust
// This returns USD balance, not ETH
let balance = provider.get_balance(user_address).await?;
println!("USD balance: {} wei", balance);

See EVM behavior for all differences from Ethereum.

Wrong chain or wrong network

Parmigiana testnet: rollup chain ID 88888, host chain ID 3151908. Mainnet: rollup 519, host 1.

rust
let chain_id = provider.get_chain_id().await?;
println!("Connected to chain: {}", chain_id);
// Should be 88888 for Parmigiana rollup

Permit2 approval missing

Gasless orders require a one-time Permit2 approval per input token:

rust
// Approve Permit2 for a token (max allowance)
let token = IERC20::new(token_address, &provider);
let tx = token.approve(permit2_address, U256::MAX).send().await?;
tx.get_receipt().await?;

Token not supported

Passage supports a fixed set of bridgeable tokens. If a deposit fails, check the supported list on the Parmigiana quickstart.

On Parmigiana testnet: ETH, USDC, USDT, and WBTC.

My transaction reverted

If an Ethereum transaction calling Passage or the Transactor reverts, nothing happens on Signet. No tokens are minted, no state changes, no funds stuck in transit.

Check the revert reason:

bash
# Check a transaction receipt
cast receipt $TX_HASH --rpc-url $HOST_RPC

# Decode a revert reason
cast run $TX_HASH --rpc-url $HOST_RPC

Common causes: insufficient gas, ERC-20 not approved for the target contract, unsupported token.

My order isn’t getting filled

Fillers choose which orders to fill based on profitability. If your order sits unfilled, the spread between input and output is likely too narrow. A 0.5% spread (50 basis points) is a reasonable starting point.

If that’s not getting filled, widen to 1% or more. See pricing guidance.

When the deadline passes, the order expires and the user’s tokens remain on Signet. No funds are lost.

My order submission was rejected

The tx-cache rejects orders with invalid Permit2 signatures or already-consumed nonces.

Common issues:

  • Insufficient balance: the user doesn’t have enough of the input token
  • Insufficient allowance: Permit2 hasn’t been approved for the input token
  • Nonce used: the Permit2 nonce was already consumed by a previous order
  • Deadline expired: the order deadline has already passed

I can’t cancel my order

On-chain orders created via initiate cannot be cancelled. The deadline is the only expiry mechanism.

For gasless Permit2-based orders, cancel by consuming the nonce:

bash
# Check if a Permit2 nonce is consumed
WORD=$(( $ORDER_NONCE / 256 ))
cast call $PERMIT2_ADDRESS \
  "nonceBitmap(address,uint256)(uint256)" \
  $OWNER_ADDRESS $WORD \
  --rpc-url $SIGNET_RPC

My bundle wasn’t included

Check that the target block is in the future:

bash
cast block-number --rpc-url $SIGNET_RPC

Other causes:

  • The bundle wasn’t profitable enough for the builder
  • A non-revertible transaction in the bundle would revert (the entire bundle is dropped). Mark transactions that are allowed to fail with revertingTxHashes.
  • The bundle expired (default TTL is 10 minutes / ~50 blocks)

Simulation results differ from execution

On-chain state can change between simulation and inclusion. Simulate close to submission time:

bash
cast rpc signet_callBundle \
  '{"txs":["0x..."],"blockNumber":"0x...","stateBlockNumber":"latest"}' \
  --rpc-url $SIGNET_RPC

Use minTimestamp/maxTimestamp on the bundle to constrain the execution window.

Unexpected balance values

Signet’s native asset is USD, not ETH. msg.value, address.balance, and tx.value all denominate in USD with 18 decimals.

bash
# This shows USD, not ETH
cast balance $YOUR_ADDRESS --rpc-url $SIGNET_RPC

See EVM behavior for all differences from Ethereum.

Wrong chain or wrong network

Parmigiana testnet: rollup chain ID 88888, host chain ID 3151908. Mainnet: rollup 519, host 1.

bash
cast chain-id --rpc-url $SIGNET_RPC
# Should return 88888

cast chain-id --rpc-url $HOST_RPC
# Should return 3151908

Permit2 approval missing

Gasless orders require a one-time Permit2 approval per input token:

bash
cast send $TOKEN_ADDRESS \
  "approve(address,uint256)" \
  $PERMIT2_ADDRESS $(cast max-uint) \
  --rpc-url $SIGNET_RPC \
  --private-key $PRIVATE_KEY

Token not supported

Passage supports a fixed set of bridgeable tokens. If a deposit fails, check the supported list on the Parmigiana quickstart.

On Parmigiana testnet: ETH, USDC, USDT, and WBTC.

ESC

Start typing to search documentation...

Navigate Select ⌘K Open