Safe Infra, Handling service unavailability: A DX Perspective

The recent ByBit attack had a significant ripple effect across the ecosystem, forcing Safe to halt mission-critical services on their interfaces and downstream SDKs. Many alternative interfaces and services that depended on Safe SDKs—such as AppKit and ProtocolKit—were also affected.

As a result, more teams are now considering running their own infrastructure to mitigate similar risks in the future. However, simply forking an interface isn’t enough—maintaining canonical, reliable mirrors of centralized infra requires deeper architectural considerations.

This post is intended to kickstart a discussion on how we can approach this problem from a Developer Experience (DX) SDK first perspective.

Key Considerations

  1. SafeApiKit Already Supports Additional txServices
    SafeApiKit is designed to work with multiple transaction services, making it a natural point of extension.

• Repo: SafeApiKit - Safe Transaction Service

export interface SafeApiKitConfig {
    /** chainId - The chainId */
    chainId: bigint;
    /** txServiceUrl - Safe Transaction Service URL */
    txServiceUrl?: string;
}

Challenges

  • While we would love to run and maintain this infra, it’s also simply cost prohibitive as a small vendor. (We would need to run a txn service per chain)
  • The fact that other major vendors (VC backed) whose interfaces have also halted tells us that this pattern is not necessarily widely adopted.

Possible solutions

Allow for fallback tx services

export interface SafeApiKitConfig {
   ...
   /** publicTxRelays - A list of well known public Safe Transaction Service URL/s */
   publicTxRelays?: string[]
}

Given running a service and keeping it up to date is non-trivial, could we use SAFE tokens to incentivise providers to keep up to date?

Maybe the Safe team could share feasibilities re: the complexites of running/maintaining this infra.

With more trusted public txn services we have more resilience.

Decouple centralised requests

Say we want to retrieve the current owners of a Safe. This is exposed on apiKit.getSafeInfo(). However This request relies on the tx service being avaliable.

// Example call getSafeInfo()
const apiKit = new SafeApiKit({
  chainId: BigInt(10)
})
apiKit.getSafeInfo('Some-Safe')

// Under the hood relies on the txn-service state
async getSafeInfo(safeAddress: string): Promise<SafeInfoResponse> {
    ...
    return sendRequest({
      url: `${this.#txServiceBaseUrl}/v1/safes/${address}/`,
      method: HttpMethod.Get
    }).then((response: any) => {
    ...
}

In cases like this when the service is unavailable it would be nice for apiKit to to fallback to an on-chain resolution (where possible)

// Example call getSafeInfo()
const apiKit = new SafeApiKit({
  chainId: BigInt(10),
  provider: BYORpc // 
})
apiKit.getOwners('Some-Safe')


async getOwners(safeAddress: string): Promise<SafeInfoResponse> {
    // Attempt from txn svc, if 503 fallback to on-chain    
    // ... resolve correct ABI for deployed safe
    // ...resolveOwners    
}

Exploring solutions like this means as developers we can worry less about defensive programming and having to duplicate basic code.

By no means is this a concrete solution but just asking the broader community how they plan to address some of these key issues.

While anyone could implement these mechanisms themselves, It is worth considering if Safe could provide some official pathways to leverage in situations like this.

By no means do I underestimate the work involved to achieve the above, but if successfully implemented. It would mean greater adoption of Safe SDKs when building out integrations.

3 Likes