Making three custody providers interchangeable behind one wallet interface
Three MPC custody models with different trust boundaries and key ceremonies, abstracted so product code never learns which one is underneath.
- client
- Anonymised, current engagement
- period
- Current
- track
- #engineering#advisory
Context
The platform bridges DeFi, CeFi and traditional finance under a regulated wrapper. We deliver backend, platform and security work into it as an engineering vendor, under fixed-price engagements with milestone-gated payment, separation of duties, and acceptance tied to merged pull requests.
I lead the engagement portfolio. I scope and price the work, then I am accountable for the team that delivers it.
This study is the wallet and custody workstream.
The problem
Custody is the part of a crypto platform you cannot get wrong twice.
The platform needed more than one custody provider: for resilience, for commercial leverage, and because different asset and jurisdiction combinations are served well by different providers.
Those providers agree on almost nothing that matters. One splits key material three ways and requires all three shares to sign. Another brokers signing through a co-signer service carrying its own activation state. A third exposes a different key hierarchy and fails in a different way. Trust boundaries, key ceremonies, error semantics and operational assumptions all differ.
Writing product code against any one of them means writing product code against all of them, or rewriting it the day a provider is replaced. The wallet layer had to make them interchangeable without pretending they were the same thing.
What I did
- Designed the provider-agnostic base layer first, before any adapter existed. The abstraction was specified across a numbered work breakdown, so the boundary was argued on paper before it was argued in code.
- Built adapters for three custody providers against that boundary. Each adapter owns its provider's key ceremony, trust boundary and failure semantics. Nothing above the boundary knows which provider is underneath.
- Held a 3-of-3 MPC custody boundary where the provider's model required it, with RSA-AES hardening on the transport of key material.
- Produced non-activation evidence for a co-signer service, because a regulated audit treats "this component is not enabled" as a claim requiring proof.
- Scoped each adapter as its own fixed-price package with its own work breakdown and merge-request packages, so a provider could be added, deferred or dropped without reopening the whole engagement.
- Delivered under separation of duties. No engineer approved their own work, and a senior independent reviewer attested each package.
What it cost
- The abstraction is honest about what it cannot hide. Custody models differ in ways that leak: activation state, signing latency, and what "the key is unavailable" actually means. Those surface at the boundary instead of being smoothed over. The interface is less elegant for it and the system is more truthful.
- Building the base layer first delayed the first working adapter. Fixed-price delivery punishes that ordering, because the client sees no wallet at all during the opening package. It was still the right call, and it is why the second and third adapters cost a fraction of the first.
- Each package carried an explicit out-of-scope list, including providers named as excluded and delivered later under a separate package. Naming a gap costs a conversation. Half-integrating a custody provider costs considerably more.
Outcome
Three custody providers sit behind one interface, and product code has never had to know which. Adding a fourth means writing one more adapter.
No figures here. Hours, rates and finding counts belong to the client, and the platform is current, so it is not named.