API reference

Every exported function, class, and type, grouped the way you'll actually reach for them.

createMorva(config)

The entry point. Returns a Morva instance — construct once per app.

interface MorvaConfig {
  particle: { projectId: string; projectClientKey: string; projectAppUuid: string };
  registryAddress?: Address;          // optional until you deploy MorvaRegistry
  registryDeploymentBlock?: bigint;   // required to call getAllMerchants()
  registryRpcUrl?: string;            // RPC for reading MorvaRegistry itself (always Arbitrum One)
  settlementRpcUrls?: Partial<Record<SupportedSettlementChainId, string>>; // per-chain overrides for pay()
  slippageBps?: number;               // default 100 (1%)
  settlementTimeoutMs?: number;       // default 120_000 — how long pay() polls for settlement
}

function createMorva(config: MorvaConfig): Morva

Morva

MethodReturnsNotes
connect(signer)Promise&lt;BuyerSession&gt;Constructs the buyer's Universal Account (EIP-7702 mode).
createDirectIntent(args)PaymentIntentSynchronous, no registry lookup.
createPaymentIntent(args)Promise&lt;PaymentIntent&gt;Reads settlement config from MorvaRegistry; throws MerchantInactive if deactivated.
registerMerchant(cfg, walletClient)Promise&lt;Hex&gt;Merchant-side. Needs a viem WalletClient for the merchant's own wallet.
updateMerchant(cfg, walletClient)Promise&lt;Hex&gt;Same config shape as register.
getMerchant(address)Promise&lt;MerchantConfig&gt;Throws MerchantNotFound if unregistered.
getAllMerchants()Promise&lt;Array&lt;{'{'}address{'}'} &amp; MerchantConfig&gt;&gt;Active merchants only; scans MerchantRegistered logs from registryDeploymentBlock.

createDirectIntent / createPaymentIntent arguments

morva.createDirectIntent({
  amount: string;              // human units, e.g. "4.99"
  orderId: string;
  settlementToken: Address;
  settlementRecipient: Address;
  settlementChainId?: SupportedSettlementChainId; // defaults to 42161 (Arbitrum One)
}): PaymentIntent

morva.createPaymentIntent({
  merchant: Address;
  amount: string;
  orderId: string;
}): Promise<PaymentIntent>   // settlementChainId comes from the merchant's registry config

BuyerSession

MethodReturnsNotes
getUnifiedBalance()Promise&lt;UnifiedBalance&gt;Cross-chain holdings, from getPrimaryAssets().
pay(intent, opts?)Promise&lt;PaymentResult&gt;The full sign → authorize → submit → settle sequence.
interface PaymentResult { transactionId: string; explorerUrl: string }
type PaymentStatus =
  | "building" | "authorizing" | "signing" | "submitted" | "settled"
  | "unknown"  // SettlementTimeout — genuinely unresolved, not a confirmed failure
  | "failed";

session.pay(intent: PaymentIntent, opts?: {
  onStatus?: (status: PaymentStatus) => void;
  onDebug?: (event: string, detail?: Record<string, unknown>) => void;
  // Already have a transactionId from an "unknown"-status attempt on this
  // exact intent? Pass it here to resume polling instead of submitting a
  // second transfer — see the errors page's retry-safety note.
  resumeTransactionId?: string;
  buildExplorerUrl?: (transactionId: string) => string; // defaults to universalx.app
}): Promise<PaymentResult>

Types

PaymentIntent

interface PaymentIntent {
  merchant?: Address;           // present only when built via createPaymentIntent
  orderId: string;
  amount: string;               // human-readable settlement-token units, e.g. "4.99"
  settlementToken: Address;
  settlementRecipient: Address;
  settlementChainId: SupportedSettlementChainId; // 1 | 56 | 8453 | 196 | 42161 — defaults to 42161
}

// Every settlement chain this SDK supports — the EVM subset of what
// Particle's Universal Account SDK supports in EIP-7702 mode.
type SupportedSettlementChainId = 1 | 56 | 8453 | 196 | 42161;

MerchantConfig / MerchantConfigInput

interface MerchantConfig {
  settlementToken: Address;
  settlementRecipient: Address;
  active: boolean;
  settlementChainId: number;    // validated against SupportedSettlementChainId when building an intent
  metadataURI: string;          // off-chain JSON: { name, logoUrl, description }
  metadata?: MerchantMetadata;  // fetched from metadataURI, best-effort — never throws
}

type MerchantConfigInput = Omit<MerchantConfig, "metadata">;

UnifiedBalance

interface UnifiedBalance {
  totalUsd: string;
  assets: UnifiedBalanceAsset[];
  raw: IAssetsResponse; // untransformed getPrimaryAssets() response
}

interface UnifiedBalanceAsset {
  symbol: string;
  totalAmount: string;
  usdValue: string;
  chains: UnifiedBalanceChain[];
}

interface UnifiedBalanceChain { chainId: number; amount: string }

Signers

interface MorvaSigner {
  address: Address;
  signMessage(message: Uint8Array): Promise<Hex>;
  signAuthorization(auth: AuthorizationRequest): Promise<SignedAuthorization>;
}

interface AuthorizationRequest { contractAddress: Address; chainId: number; nonce: number }

class LocalSigner implements MorvaSigner {
  constructor(privateKey: Hex)
}

LocalSigner is for scripts and tests only

It signs directly with a raw private key held in memory. Real frontends implement MorvaSigner against an embedded wallet provider instead — see the integration guide for a complete Magic-backed implementation.

MORVA_REGISTRY_ABI

The full contract ABI, exported for direct use with viem if you need to interact with MorvaRegistry outside the Morvaclass's own methods.