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:
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.
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:
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 tokeninsufficient_allowance: Permit2 hasn’t been approved for the input tokennonce_used: the Permit2 nonce was already consumed by a previous orderdeadline_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:
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:
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:
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.
// 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.
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:
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.
// 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:
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.
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:
// 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:
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:
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.
// 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.
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:
// 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:
# Check a transaction receipt
cast receipt $TX_HASH --rpc-url $HOST_RPC
# Decode a revert reason
cast run $TX_HASH --rpc-url $HOST_RPCCommon 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:
# 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_RPCMy bundle wasn’t included
Check that the target block is in the future:
cast block-number --rpc-url $SIGNET_RPCOther 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:
cast rpc signet_callBundle \
'{"txs":["0x..."],"blockNumber":"0x...","stateBlockNumber":"latest"}' \
--rpc-url $SIGNET_RPCUse 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.
# This shows USD, not ETH
cast balance $YOUR_ADDRESS --rpc-url $SIGNET_RPCSee 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.
cast chain-id --rpc-url $SIGNET_RPC
# Should return 88888
cast chain-id --rpc-url $HOST_RPC
# Should return 3151908Permit2 approval missing
Gasless orders require a one-time Permit2 approval per input token:
cast send $TOKEN_ADDRESS \
"approve(address,uint256)" \
$PERMIT2_ADDRESS $(cast max-uint) \
--rpc-url $SIGNET_RPC \
--private-key $PRIVATE_KEYToken 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.