Skip to content
LogoLogo

Adapters

The @mure/sdk uses an adapter pattern to stay wallet-agnostic. An adapter implements the AdapterServiceApi contract, which tells the SDK how to send transactions, read the current account, and read the current chain.

The only adapter shipped today is built on viem, but you can write your own by satisfying the same interface.

AdapterService

AdapterService is an Effect Context.Service. It defines the boundary between the SDK and the blockchain.

Interface

MethodSignatureDescription
sendTransaction(tx: Transaction) => Effect<Hex, AdapterServiceError>Send a single transaction and return its hash.
sendTransactions(txs: Transaction[]) => Effect<AdapterSendOutcome[], AdapterServiceError>Send a batch and return one outcome per input, in input order.
accountEffect<CAIPAccountCompact>The active account address in CAIP-10 compact format.
chainEffect<CAIPChainCompact>The active chain identifier in CAIP-2 format.

Outcome states

sendTransactions reports one AdapterSendOutcome per input.

StatusPayloadMeaning
successhashThe transaction was included in a block.
revertedhashThe transaction was included but reverted on-chain.
failedcauseThe adapter rejected the transaction before inclusion.
unknownhash?The adapter could not confirm the result within the poll bound.

AdapterServiceError

All adapter failures are surfaced as AdapterServiceError, a typed Effect error that carries the original cause.

import { AdapterServiceError } from "@mure/sdk/adapters";
 
// Error shape:
// {
//   _tag: "AdapterServiceError",
//   cause: Error  // the original viem or provider error
// }

ViemAdapter

ViemAdapter is the production adapter for any EVM chain. It wraps a viem WalletClient and implements the full AdapterServiceApi.

Factory: viem()

The viem() helper is the easiest way to create an adapter. It accepts either a ready-made WalletClient or a configuration object.

import { http } from "viem";
import { sepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
import { createWalletClient } from "viem";
import { viem } from "@mure/sdk/adapters";
 
const walletClient = createWalletClient({
  chain: sepolia,
  transport: http(),
  account: privateKeyToAccount("0x..."), // replace with your private key
});
 
const adapter = viem(walletClient);

Batch transactions with sendTransactions

ViemAdapter supports EIP-5792 batch calls through sendTransactions. It calls viem's sendCalls, polls the call status with a fixed 1 second interval and a 120 attempt cap, and returns one outcome per input in input order.

An empty batch resolves to [] and does not call sendCalls.

When the wallet does not support batching, the adapter falls back to sequential sends. Each send is classified by its receipt. A rejected send becomes failed with the real cause. An input whose receipt does not arrive within the poll bound becomes unknown.

import { Effect } from "effect";
import { AdapterService } from "@mure/sdk/adapters";
 
const program = Effect.gen(function* () {
  const adapter = yield* AdapterService;
  const outcomes = yield* adapter.sendTransactions([
    { to: "0x...", data: "0x...", value: 0n },
    { to: "0x...", data: "0x...", value: 0n },
  ]);
  return outcomes;
});

Adapter contract

An adapter must satisfy these obligations:

  • sendTransaction resolves to a well-formed transaction hash.
  • sendTransactions resolves to exactly one outcome per input, in input order.
  • An empty batch resolves to [] without calling the batch action.
  • A batch reports mixed outcomes. It does not fail as a whole because one input failed.
  • A rejected input is failed with the original cause.
  • An unresolved input is unknown.
  • account and chain are consistent CAIP compact values. account starts with chain:.
  • An attempt-level failure fails with AdapterServiceError carrying the original cause.

The SDK ships a conformance suite that checks these obligations against the viem adapter and a synthetic adapter. The suite stays internal and is not part of the package export map.

Low-level: ViemAdapter

If you already have a WalletClient and want to skip the viem() factory, use ViemAdapter directly.

import { ViemAdapter } from "@mure/sdk/adapters";
 
const adapter = ViemAdapter(walletClient);

Usage

An adapter is required to create a Client. Pass it to createClient along with the API URL.

import { viem } from "@mure/sdk/adapters";
import { createClient } from "@mure/sdk";
import { http } from "viem";
import { sepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
 
const adapter = viem({
  chain: sepolia,
  transport: http(),
  account: privateKeyToAccount("0x..."), // replace with your private key
});
 
const client = createClient({ adapter });

For advanced Effect patterns, you can provide the adapter Layer to any program that requires AdapterService.

import { Effect } from "effect";
import { AdapterService } from "@mure/sdk/adapters";
 
const program = Effect.gen(function* () {
  const adapter = yield* AdapterService;
  const account = yield* adapter.account;
  console.log("Connected:", account);
});
 
const runnable = program.pipe(Effect.provide(adapter));