Tesseract
How we helped Tesseract migrate custody from Copper to Fireblocks under a year-end MiCA deadline, using a Facade middleware that kept the backend's API contract intact.
CompletedFrom Copper to Fireblocks: how we helped Tesseract migrate custody under a MiCA deadline
A six-week engagement, a regulator-driven cutoff, and the case for letting middleware absorb a migration your backend can’t.
At a glance
- Tesseract, a regulated digital asset firm, had to migrate custody from Copper to Fireblocks before a year-end MiCA cutoff or lose its license
- Copper and Fireblocks expose fundamentally different wallet models: hierarchical Portfolios vs. flat Vault Accounts
- Tesseract’s internal Earn API was written against Copper’s hierarchy, and a direct port would have meant rewriting business-critical backend services under deadline pressure
- We built a Facade service, middleware that kept the Copper-shaped API contract intact and translated calls into native Fireblocks operations underneath
- The initial scaffold landed early, after which it was extended to production with an omnibus vault model, sweeping logic, and per-partner authentication
- Tesseract met the deadline, and the Earn API was still speaking to the same contract it always had.
This post is about how we bridged that gap without rewriting Tesseract’s backend, and what survived the handover into production.
The problem nobody wants on a deadline
MiCA, the EU’s Markets in Crypto-Assets Regulation, which requires firms holding or moving digital assets to use a licensed custodian (a CASP, Crypto-Asset Service Provider), is now forcing institutions to commit to specific custody providers under fixed deadlines. Choosing the custodian is the easy part. The hard part is moving live custody infrastructure to a new provider while trading continues and customer balances stay verifiably intact.
Custody is now regulated infrastructure
Banks, asset managers, and fintechs are running tokenized assets, stablecoin payments, and programmable settlement (automated transfers that execute when contractual conditions are met) in production. The plumbing that holds it together (wallets, custody, transaction signing, key management) used to be a crypto-native concern. It now sits inside the engineering remit of any firm that holds client funds.
Two things follow from this. The first is that custodian selection is no longer a vendor decision; under MiCA, your custodian must be a licensed CASP, and the choice carries compliance deadlines you don’t control. The second is that the cost of switching custodians is paid in engineering risk. Providers differ in data models, API design, and wallet architecture. A migration is not a configuration change; it’s a structural rebuild of the assumptions your backend was written against.
The firms that come through these migrations cleanly are the ones who can decouple their internal contracts from any single custodian’s wire format. That’s the architectural lesson the Tesseract migration produced, and the rest of this post walks through how we got there.
The Tesseract migration
Custody migrations go wrong in a few specific ways: signing keys get misconfigured, balances drift from custodian state, and withdrawals freeze mid-transfer. Time pressure makes all three more likely, and Tesseract had plenty of it. The window was six weeks, with a hard regulatory cutoff, no remediation budget if the deadline slipped, and an internal stack tightly coupled to the outgoing provider’s data model.
The technical mismatch sat at the level of wallet architecture. Copper, the outgoing custodian, organizes wallets into “Portfolios” belonging to an “Organisation”; operations can be issued at the Portfolio level (“withdraw 10 BTC from Portfolio A”) and Copper handles wallet selection internally. Fireblocks, the incoming custodian, uses a flat hierarchy of Vault Accounts, each containing a single wallet per asset, with no native concept of Portfolios. A request that worked against Copper had no direct equivalent against Fireblocks.
What we built
A Facade service: a middleware layer that exposed the same API contract Tesseract’s existing services were already calling against Copper, and routed each call to the equivalent Fireblocks operation underneath. The Facade speaks Copper to the rest of Tesseract’s stack and Fireblocks to the custodian.
The point of the pattern is containment. Tesseract’s downstream services, the Earn API in particular, treated the Facade as if it were Copper. The migration’s blast radius (the set of services that could break if something went wrong) was bounded to one service rather than spreading across the rest of the backend.
Who should care about this pattern? Any team facing a custodian migration under deadline pressure, particularly if their internal services have been written against a single provider’s data model. The Facade approach applies anywhere a regulated contract has to survive a substrate change: custody is one instance, and payment rails and settlement infrastructure are others.
How it works
Portfolio-to-vault mapping
Copper’s “Organisation → Portfolio → Wallet” hierarchy was projected onto Fireblocks’ flat Vault Account model using deterministic naming conventions and a persistent mapping layer in PostgreSQL. The mapping was validated against live Fireblocks reads before any write traffic was switched over, a deliberate choice to catch projection errors before they could affect customer funds.
The portfolio-based transfer engine
The hardest part of the system to replicate was Copper’s Portfolio Transfers. Fireblocks requires a specific source vault for every transaction; Copper allowed withdrawals to be issued against an abstract Portfolio and handled wallet selection itself. Reproducing that behavior meant pushing the wallet-selection logic into the Facade.
The engine resolves an incoming request against “Portfolio A”, reads the mapped Fireblocks vaults from the persistent map, and pulls live balances directly from Fireblocks at request time; the system does not trust an internal balance cache. It then selects source vaults to cover the requested amount, including network gas costs, splits the order across multiple Fireblocks transactions when no single vault holds the full amount, and reconciles every child Fireblocks transaction back to the parent Order through a state machine.
We delivered the engine’s scaffolding during the initial engagement, after which we supported Tesseract’s engineers in extending it for production.
The omnibus vault model
After the scaffolding was delivered, an omnibus vault structure was built out (a shared vault per asset class, with each customer’s funds segregated by address or memo rather than by separate vault) handling three asset shapes:
- UTXO assets like BTC (Bitcoin’s unspent-transaction-output model) share a vault under unique addresses
- TAG assets like XRP and XLM (which share a single address and use a memo or tag to route funds) share a vault under unique memos
- ACCOUNT assets like ETH and SOL (single-balance accounts) are routed through intermediary vaults with sweeping logic that moves funds from deposit vaults to portfolio vaults
They also added three gas pre-funding strategies (GAS_STATION using Fireblocks’ built-in gas station, SELF_FEE using each vault’s own balance, and MANUAL_GAS for manual top-ups), letting the system pick the right approach per chain.
Security and source-of-truth discipline
The Facade does not maintain its own internal ledger of balances. Authoritative balances are read live from Fireblocks at the point of need, with brief webhook-driven caching where performance demands it. Transaction status is mirrored from Fireblocks events; only the higher-level Order state machine is owned inside the Facade. This rules out a class of double-spend and ledger-drift failure modes by construction rather than by convention.
The authentication requirement shifted mid-engagement from header-based API keys to signed Authorization headers, and the security architecture was re-cut accordingly. Inbound Fireblocks webhook events are verified against RSA-SHA512 signatures before any state change is processed. The Copper-facing webhook channel is secured with HMAC-SHA256 signatures over the CopperWebhookBody format with per-partner URL routing. Tesseract’s engineers then built the per-partner authentication layer on this substrate: service accounts with encrypted API keys, a many-to-many partner-to-portfolio binding, and middleware-enforced isolation so that a valid credential for one partner cannot read or mutate another partner’s portfolios.
System components
The service layer runs TypeScript on Fastify. PostgreSQL stores the Portfolio-to-Vault mapping and Order lifecycle state; the schema grew from 5 tables and 2 migrations at handover to 8 tables and 14 migrations once Tesseract’s engineers had taken it through to production. pg-boss job queues handle long-running transaction monitoring and inbound webhooks without blocking the API path.
What we took away
A hard regulatory cutoff compresses every decision into a smaller window than the architecture deserves. The work that survives is the work that respects existing contracts, even when those contracts were shaped by the provider you’re trying to leave. Facade let us change the custodian without changing the rest of the stack, in the six weeks we had.
Equilibrium works with regulated firms on custody migrations, custodian integrations, and the kinds of infrastructure rebuilds that come with hard regulatory deadlines. Regulatory deadlines don’t move; architectures do, when you preserve the contracts that matter and let everything underneath change shape.