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.
Validate the destination
- An address is
0xfollowed 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 explicitgasPrice 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:
parallax-wallet:
Fees
Parallax has no base fee. A transaction paysgas 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 witheth_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:- Poll
eth_getTransactionReceiptwith the transaction hash. When it returns a receipt, the transaction is in a block. Checkstatusis0x1. A failed withdrawal still uses its nonce and pays its fee. - Record the block number and hash, and count confirmations as for deposits.
- 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.
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 agasPrice 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 withevmVersion: "berlin", and audit it as you would any contract that holds
customer funds.
