An SDK that serves Safe's transaction API from Ceramic
Daoism Systems, funded by the Safe Grants Program, wanted Safe apps to coordinate multisig transactions without Safe's hosted service. We built an SDK that answers the same API requests from Ceramic and falls back to Safe's own service.
- Client
- Daoism Systems
- Service
- Blockchain
- When
- October 2023 to March 2024
- Team
- Mihir, Angel Lakra
- Status
- Prototype
The problem
A Safe is a multisig wallet: a transaction runs only once enough of its owners have signed it. Before that, the owners find the proposal, add their signatures and see who has confirmed. Safe keeps those records in its Transaction Service, which Safe runs on its own servers. Apps read and write the records through the Safe Transaction API. An app built on Safe's hosted service relies on that one hosted backend to collect signatures.
Daoism Systems, funded by the Safe Grants Program, set out to move those records onto Ceramic, a decentralized data network, with ComposeDB as the database. A developer had to be able to switch an app over without rebuilding the app around a new store.
What we built
We built the SDK around one method, fetchLegacy. It takes the same HTTP method and route a developer already sends to the Safe Transaction API. The SDK matches the route against a table of patterns and hands the request to a ComposeDB handler. The handlers create a transaction and add a confirmation. They read a Safe, its transactions and their confirmations. They list the Safes an owner belongs to. They add and read delegates. To move an app over, a developer changes the call and keeps the flow.
When a route has no handler, or a handler reports a failure, the SDK sends the request to Safe's own API. The response says which of the two served the request. We kept that fallback so an app keeps working while the Ceramic side covers only part of the API. The cost is that the app still depends on Safe's servers for every route the SDK doesn't serve.
The user signs every write. In the browser, the SDK starts a session from the connected Ethereum wallet. The SDK ties the records it writes to Ceramic to the owner's key.
What was hard
Safe's data is relational: Safes, owners, transactions, confirmations and delegates all reference one another. ComposeDB stores documents, so we wrote each table as a ComposeDB model with references and indexes. We shipped the deployed definitions inside the package. An integrator gets a working schema without deploying one.
A fresh Ceramic store starts empty, so it has none of the history Safe's service already holds. When an owner proposes a transaction, the SDK fetches the Safe's details from Safe's API. The first time the SDK sees a Safe, an owner or the link between them, it creates each one. Only then does the SDK write the transaction and its first confirmation.
The result
We published the SDK to npm as @daoism-systems/dsafe-sdk. We wrote its integration guides and completed every milestone of the grant. The Safe Grants Program funded dSafe with $25k. Our demo app ran the SDK on Sepolia, an Ethereum test network, against a Ceramic test node. The demo showed that a Safe app can move its transaction records to a decentralized store by changing its API calls, with Safe's own API behind it as a fallback.
Built with
| SDK | TypeScript, published to npm |
|---|---|
| Data | Ceramic and ComposeDB, with GraphQL models |
| Signing in | DID sessions from the user's Ethereum wallet |
| Fallback | Safe Transaction API |
| Monorepo | Turborepo with Yarn workspaces |
| Tests | Jest |
| Demo app | React, Vite, Tailwind CSS, RainbowKit and wagmi |