Skip to main content
A withdrawal is an ordinary transaction from one of your hot wallet addresses. The parts that need care are the ones the protocol leaves to you: which transaction type to sign, what fee to pay, keeping nonces in order, and what to do when a transaction does not confirm.

Where keys live

Keep signing keys off your nodes. A node that serves JSON-RPC and also holds unlocked keys turns any RPC exposure into a loss of funds.
  • Hot wallet: sign in your own signing service, HSM or key management system, and hand the node only the signed transaction. Parallax signatures are standard secp256k1 with EIP-155, so any signer that supports Ethereum legacy transactions works with chain ID 2110.
  • Cold storage: sign on an offline machine with parallax-wallet signtx, which never talks to a node, and carry the signed transaction across. See parallax-wallet.
  • Clef is also available as an external signer with rule-based approval; see Clef.
Keep in the hot wallet only what a day or so of withdrawals needs, and refill it from cold storage.

Validate the destination

  • An address is 0x followed by 40 hexadecimal characters. Reject anything else.
  • If the address is mixed case, it carries an EIP-55 checksum: verify it and reject the address if it fails. That catches most typing mistakes. An all-lowercase or all-uppercase address has no checksum to verify.
  • Parallax uses the same address format as Ethereum and other EVM chains, so an address can be valid on several chains at once. Make clear in your interface that the withdrawal is on Parallax, so users do not send it to an address only they control on another chain.

Build and sign

Sign a legacy (type 0) transaction with an explicit gasPrice and chain ID 2110: Ethereum libraries work unchanged as long as they produce legacy transactions. Most choose legacy on their own when blocks have no baseFeePerGas, which Parallax blocks never have, but setting the type and gas price explicitly avoids surprises after a library upgrade. For example, with ethers v6:
Or offline, with parallax-wallet:

Fees

Parallax has no base fee. A transaction pays gas used × gasPrice, all of it to the miner, and miners include the best-paying transactions first. A plain transfer always uses 21,000 gas. Price withdrawals with eth_estimateSmartFee, a port of Bitcoin Core’s fee estimator. Give it how many blocks you are willing to wait:
While recent blocks are less than half full, the estimate is the floor that miners accept by default, 1 gwei. Transactions priced below that are generally not mined. eth_gasPrice (or parallax-cli gasprice) returns the estimate for a 2-block target. A target of 2 to 6 blocks (20 minutes to an hour) suits most withdrawals. Set an upper limit of your own, too. The node also refuses to broadcast any transaction whose total fee exceeds --rpc.txfeecap, 1 LAX by default.

Nonces

Each address’s transactions must use consecutive nonces, starting from 0. A transaction cannot be mined until every lower nonce from the same address has been, so one stuck transaction holds up every withdrawal behind it.
  • Send withdrawals from a small number of hot addresses, each used by one signing process at a time.
  • Assign nonces from your own database, inside the same transaction that reserves the withdrawal. Do not ask the node for the next nonce for each withdrawal: with several in flight it will hand out the same one twice.
  • On startup and after any doubt, reconcile your next nonce against eth_getTransactionCount(address, "latest"), which counts mined transactions, and "pending", which also counts those in the node’s pool.

Broadcast

Submit with eth_sendRawTransaction, or parallax-cli sendraw <hex>, to more than one of your nodes. Each relays it to its peers. Submitting the same transaction twice is harmless: the second node already knows it. Errors you may see: Your own node accepts a transaction you submit even if it pays less than the network’s usual minimum, and keeps it across restarts. Miners do not: by default a mining node refuses transactions below 1 gwei, so an underpriced withdrawal can sit in pools indefinitely without being mined. Do not price below the eth_estimateSmartFee result.

After broadcast

Track each withdrawal until it is buried, the same way you track deposits:
  1. Poll eth_getTransactionReceipt with the transaction hash. When it returns a receipt, the transaction is in a block. Check status is 0x1. A failed withdrawal still uses its nonce and pays its fee.
  2. Record the block number and hash, and count confirmations as for deposits.
  3. If a reorganization removes the block, the transaction normally goes back into nodes’ pools and is mined again. If it has not reappeared after a few blocks, rebroadcast the same signed transaction. It has the same nonce and hash, so it can never be paid twice.
Show users the transaction hash and an explorer link as soon as you broadcast, and set expectations in minutes, not seconds: the first confirmation takes 10 minutes on average and sometimes much longer.

Stuck withdrawals

Pending transactions are not dropped after a timeout, so an underpriced withdrawal can wait in the pool for a long time. To speed it up, sign a replacement: the same nonce, the same destination and amount, and a gasPrice at least 10% higher than the one it replaces. Nodes swap the old transaction for the new one. Use a fresh eth_estimateSmartFee with a short target, and stay at least 10% above the original. Whichever version is mined, the other becomes invalid, because they share a nonce. Watch for both hashes until one confirms, and update the hash you show the user. To cancel a withdrawal that has not been mined, replace it with a transaction of the same nonce that sends 0 LAX to your own address, at least 10% more expensive. If the original is mined first, the cancellation fails with nonce too low, and the withdrawal stands.

Batching

Parallax has no native batch payment. Each withdrawal is one transaction and one nonce. If you send many small withdrawals, a batch-payment contract can pay many recipients in one transaction. Compile it with evmVersion: "berlin", and audit it as you would any contract that holds customer funds.