Appearance
Running Stitch on Fireblocks
This guide sets up a Stitch filler bot to sign with a Fireblocks vault account. The signing key stays in Fireblocks' MPC custody. The bot holds API credentials, requests each signature from Fireblocks, and never has access to the key.
It covers the API user, the vault account, the two policy rules the bot needs, and the Permit2 approval. The numbered steps follow the console order. A final section lists optional measures for teams that want to harden the configuration further.
If you have not set up a Stitch bot before, start with How to become a filler and return here for the signer.
Configuration rules
Stitch signs EIP-712 typed messages only: Permit2 orders and a session handshake with the Textile venue. It never asks Fireblocks to sign arbitrary bytes, so it does not need the Raw Signing entitlement.
The bot host holds two credentials, the API key and the RSA private key. Whoever holds both can request any signature the policy auto-approves, through Stitch or by calling the Fireblocks API directly. Configure the workspace with that in mind.
Fireblocks policy decides which user may sign for which vault. Three rules keep the workspace's permissions aligned with what the bot needs:
- Scope the Typed Message rule to the bot's vault account and API user. Set it to auto-approve. Do not make it workspace-wide.
- Do not auto-approve the Contract Call rule. A contract call is a full transaction. An auto-approving rule would let the bot's credentials send any transaction from the vault. The panel only needs this rule for Permit2 approvals, which are rare and happen while an operator is present.
- Do not grant Raw Signing to this API user. Stitch does not use it. If the entitlement exists on your workspace, leave it out of this user's rules.
Rules 1 and 2 are steps 3 and 5 below. Rule 3 requires no action.
Policy does not inspect message contents. If your security review requires checks at that level, see Hardening. Those measures are optional.
Prerequisites
A Fireblocks workspace with an API Co-Signer. The Signer role holds an MPC key share, and Fireblocks only offers that role once a co-signer exists to hold it. Without a co-signer, the role dropdown does not list Signer.
If your workspace already signs programmatically, it has a co-signer. If not, arrange one with Fireblocks before continuing.
One-time addresses enabled on the workspace. The panel sends each Permit2 approval to the token contract as a one-time address. Fireblocks ships this feature disabled. Enabling it requires the workspace Owner and the Admin Quorum, so request it early.
1. Create the API user
Settings → Users → Add user, type API user, role Signer.
Two other roles look plausible and are not. Editor and Viewer can read vaults but cannot sign. Embedded Wallet Signer belongs to a different product. It signs for end-user wallets, not for the vault account Stitch trades from.
Fireblocks asks for a CSR. Generate the key pair on your own machine:
bash
openssl req -new -newkey rsa:4096 -nodes -subj "/CN=stitch" \
-keyout fireblocks_secret.key -out fireblocks.csrThis writes two files. Upload fireblocks.csr, the public half. Keep fireblocks_secret.key. Fireblocks never sees it, and it is the file you give Stitch. The -nodes flag leaves the key without a passphrase, which is required because the bot reads it unattended.
When the user is approved, Fireblocks issues the API key, a UUID. The API key and the key file are everything Stitch needs.
2. Prepare the vault account
Choose the vault account the bot will quote from. Your liquidity stays in this account until a swap fills. The bot signs orders against it and does not move funds on its own.
The account needs at least one EVM asset wallet so there is an address to read. If it has none, add Ethereum. You do not need to hold ETH or trade on Ethereum. Adding the asset is how Fireblocks creates the wallet and its address.
On EVM chains, a Fireblocks vault account has one address across every network. The same address serves Celo, Base, Arbitrum, BSC and the rest. Stitch reads the address from whichever EVM asset the vault has.
Hold a small amount of native gas on each chain you quote. The Permit2 approvals in step 5 need it.
3. Write the Typed Message policy rule
Fireblocks rejects typed-message signing until a policy allows it. In Policies, add a Typed Message rule scoped to the vault account from step 2 and the API user from step 1. Keep it that narrow. A workspace-wide rule lets this key sign for vaults it has no business with.
Set it to auto-approve. Textile gives a filler 5.5 seconds to answer a quote request. A rule that routes to a human approver cannot meet that.
Once this rule is active, every typed message it covers is signed without human review. That is what lets the bot quote.
4. Point Stitch at it
In the Stitch panel, open the bot's settings and choose Signer → Fireblocks. Enter the API key, then drop fireblocks_secret.key onto the key field. The panel reads the file in the browser and hides it once loaded, so the key is not left on screen during setup. Choose your workspace region if it is not the global one. An EU or US-East workspace answers only on its own host.
Load the vault accounts, select yours, and press Verify.
Verify signs one throwaway message. That single action confirms the credentials parse, the co-signer is online, the policy rule exists and auto-approves, the vault resolves to an address, and how long a signature takes. It signs under its own domain, so the result authorises nothing, costs no gas, and touches no chain.
The address Verify reports is written into the bot's configuration. It is not read from an API response. It is the address a real signature recovered to. Verify also records which Fireblocks asset resolved the address, and the bot signs under that asset from then on.
5. Approve Permit2
Permit2 pulls your tokens when a quote fills. Until you grant it an allowance, the bot quotes and every fill fails.
An approval is a transaction, which a typed-message signer cannot sign. The panel uses a different Fireblocks operation for it: a contract call. Fireblocks builds, prices, nonces, signs and broadcasts the transaction itself. The bot never handles a transaction hash, and no extra entitlement is needed.
Before the first approval:
Add a Contract Call policy rule, scoped to the same vault account and API user as the Typed Message rule, with the destination set to one-time address.
Do not set this rule to auto-approve. Route it to an approver. Approvals are rare and an operator is at the panel when they happen. The panel waits up to three minutes for the call to mine, which is enough time to approve it in the console or the mobile app.
When the request arrives, check it against Contract addresses. The destination is one of the token contracts for that chain. The spender in the calldata is Permit2.
Hold native gas in the vault on each chain you quote. An ERC-20
approvecosts about 46,000 gas.
The add-bot wizard sends one approval per input token on the way to a live bot. To approve later, or to top one up, open the bot's Tools tab and press Approve beside each token marked "Not approved".
The panel resolves the Fireblocks asset id for the chain by reading your workspace. The call is filed under the correct network without manual configuration. A chain your workspace does not have is reported as such.
The request stays open until the call is mined, normally a few seconds after approval. If it hangs after approval, confirm that the rule covers this vault account and API user.
The panel grants an unlimited allowance. This is the standard Permit2 setup. The bot can quote the full vault balance without periodic re-approval. If your custody policy requires a bounded allowance, see Bounding the Permit2 allowance.
Latency
Fireblocks signing is asynchronous. Stitch creates a signing request and polls for the result. With an auto-approving policy, a signature normally completes in a few hundred milliseconds.
Textile gives a filler 5.5 seconds to answer a quote request. A signature that completes within a second leaves ample room for the bot to price and respond. Verify reports the measured round trip. Check it once during setup.
If a signature takes several seconds, the usual cause is a policy rule that routes to an approver instead of auto-approving. Fix the rule and run Verify again. Every signature is verified before use in either case. Latency only affects how many quotes you win.
What runs and what does not
A typed-message signer cannot sign transactions. That defines what a Fireblocks filler can do.
Runs. Answering RFQ quote requests, and resting orders on the public ladder. Both sign Permit2 orders only.
Does not run. Filling other users' resting limit orders, and the closer. Both build and sign their own transactions on every fill. Stitch rejects these legs when you save the configuration rather than failing at the first fill.
The panel's Withdraw does not run either, for the same reason. Move funds out of the vault from the Fireblocks console.
If the on-chain legs matter to your strategy, run them from a second bot with its own key. One Fireblocks bot for RFQ and the ladder, and a separate bot for the on-chain legs, is a common setup. Stitch also accepts raw_signing = true for workspaces that hold the Raw Signing entitlement. That widens the key from the EIP-712 structures Stitch produces to any transaction, and we do not recommend it.
Contract addresses
Every contract the bot touches on chain, per network. The token rows are the destinations you will see on Contract Call approval requests. Permit2 is the spender those approvals grant. The reactor is the spender named inside every Permit2 order, which matters if you run the callback handler described under Hardening.
Permit2 is the canonical deployment at 0x000000000022D473030F116dDEE9F6B43aC78BA3 on every chain below.
| Chain | Contract | Address |
|---|---|---|
| BNB Smart Chain (56) | LimitOrderReactor | 0xe03261c0436DB575F92F09EdDF3591E2566B7D97 |
| cNGN | 0xa8AEA66B361a8d53e8865c62D142167Af28Af058 | |
| USDT | 0x55d398326f99059fF775485246999027B3197955 | |
| Ethereum (1) | LimitOrderReactor | 0x26a1aedaa26affc4fd9f910acfca754a31bc43c6 |
| WETH | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 | |
| USDT | 0xdAC17F958D2ee523a2206206994597C13D831ec7 | |
| Celo (42220) | LimitOrderReactor | 0xa9AA0a64769cBed4d3B1Ceb4Df01CdE915C235b3 |
| cNGN | 0xF6829D7393dAe24509eb1E52eE8e572e2E271a4f | |
| USDC | 0xcebA9300f2b948710d2653dD7B07f33A8B32118C | |
| USDT | 0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e | |
| wARS | 0x0DC4F92879B7670e5f4e4e6e3c801D229129D90D | |
| wBRL | 0xD76f5Faf6888e24D9F04Bf92a0c8B921FE4390e0 | |
| IDRX | 0x18Bc5bcC660cf2B9cE3cd51a404aFe1a0cBD3C22 |
The address book is the source of truth and also lists the BSC testnet deployment. If you quote a corridor that is not in this table, take its addresses from there.
Hardening
None of the measures below is required to run Stitch. The steps above give you a working, correctly scoped configuration. This section is for teams whose custody policy asks for more. Each measure stands on its own.
Validate message contents with a callback handler
Fireblocks policy scopes who signs for which vault. It does not read the contents of an EIP-712 message. A callback handler does. It ties the signing key to the specific messages Stitch produces. Without one, the Typed Message rule approves any typed message for the vault, not only the ones Stitch sends.
Run a Co-Signer Callback Handler. The co-signer sends every signing request to an HTTPS service you run, signed as a JWT, and your service returns approve or reject. Fireblocks publishes a plugin-based reference implementation.
Write it as an allowlist. Approve only the domains and primary types Stitch produces. Reject everything else. An allowlist is the right shape because the set of typed messages a contract might honour is open-ended. EIP-2612 permit under a token's own domain is the clearest example. It is a typed message, it grants a token allowance on its own, and the Permit2 allowance plays no part in it. The handler is what keeps that class of message out.
Three types pass the domain check:
PermitWitnessTransferFromunder the Permit2 domain. This is an order and needs the field checks below.MakerSessionandMakerEnrollunder the Textile venue domains. These authenticate the bot to the venue and move no funds.SignerCheckunder theStitch Signer Checkdomain. This is what the panel's Verify button signs. It authorises nothing. A handler that omits it makes Verify fail after every credential rotation.
For an order, check all of the following:
spenderis the LimitOrderReactor for that chain, listed under Contract addresses.witness.info.swapperis the vault address.- Every
witness.outputs[].recipientis the vault address. - Both tokens are on an allowlist.
permitted.amountis within a per-order cap.- The output amount against the input amount is within an acceptable rate band.
- The deadline is near-term.
The two recipient checks are the easiest to miss. BaseReactor._fill pays the output to whichever address the signed order names. An order that passes the reactor, token, cap and deadline checks but names another recipient would still fill, with the output paid there. Binding swapper and every output recipient to the vault guarantees that every fill pays your vault. The rate band does the same for price.
Contract calls go through the same handler. Approve an ERC-20 approve to the Permit2 address on a token you trade. Reject everything else.
Bounding the Permit2 allowance
A Permit2 order settles against the ERC-20 allowance you granted Permit2. That allowance is the most Permit2 orders can settle. With the unlimited allowance the panel grants, the bot can quote the full vault balance and never needs a top-up.
Some custody policies prefer a bounded allowance. If yours does, set it yourself. Send the approve from the Fireblocks console, or let the panel approve and then lower it in the Token Allowance Manager. The allowance is consumed as orders fill, so you re-approve periodically to keep quoting.
Replace "max" liquidity with fixed amounts first, or the bot will not start. The corridor presets ship buy_total_liquidity_debt and sell_total_liquidity_collateral set to "max", which means "quote everything funded". A side in that mode only accepts an effectively unlimited allowance, and Stitch's preflight refuses to start against a bounded one. Set a number on every enabled side in stitch.toml before you bound the allowance, and keep the allowance at or above it.
Restrict access to the bot host
fireblocks_secret.key is the credential that matters. The API key UUID is not usable without it. Stitch writes the key to disk owner-only, and both the bot and the panel read it. Anyone with access to the host has the key, and with it every signature the policy allows. Restrict host access the way you would restrict access to the key itself.
To rotate, create a new API user with a new CSR. The private key never leaves your machine in either direction.
Prepare a revocation path
Two controls stop the bot without touching the bot host. Both are in the Fireblocks console.
Revoke the Permit2 allowance in the Token Allowance Manager. Every outstanding order becomes unfillable the moment the revocation lands. Disable or delete the API user, and no new signatures are issued. Know where both are before you go live.
Reference
The technical walkthrough, including manual stitch.toml configuration and troubleshooting, is in the Stitch repository: docs/signer-fireblocks.md.