Skip to Content
🌐 Frontend SDKAdvanced API

Advanced API

Low-level primitives exposed by @embarkai/ui-kit for apps that need direct control outside the standard hooks flow.

Most apps use the hooks under Hooks and never need this page. The APIs below exist for imperative scripts, custom flows (background tasks, non-React integrations), and advanced verification.

Error Handling

The UI kit exposes a small error hierarchy so you can discriminate user actions from protocol failures:

import { AccountAbstractionError, UserRejectedError, ErrorCodes, createAccountAbstractionError, } from '@embarkai/ui-kit'
SymbolKindDescription
AccountAbstractionErrorclass extends ErrorBase class. Has a string code field for programmatic handling.
UserRejectedErrorclass extends AccountAbstractionErrorThrown when the user cancels a signing or transaction prompt. code === 'USER_REJECTED'.
ErrorCodesconstFrozen map of stable error codes: MPC_SIGNING_ERROR, USER_REJECTED.
createAccountAbstractionError({ message, code? })functionReconstructs a typed error from a serialized { message, code } payload. Used internally to rehydrate errors that cross the iframe postMessage boundary.

Discriminating errors

import { sendUserOperation, UserRejectedError } from '@embarkai/ui-kit' try { await sendUserOperation(session, { to, value }) } catch (err) { if (err instanceof UserRejectedError) { toast('You cancelled the transaction.') return } throw err // unexpected — re-raise }

UserOperationTimeoutError

Thrown by waitForUserOperationReceipt(...) when the receipt does not appear before the configured timeout. Import from @embarkai/ui-kit:

import { waitForUserOperationReceipt, UserOperationTimeoutError } from '@embarkai/ui-kit' try { const receipt = await waitForUserOperationReceipt(userOpHash, { timeoutMs: 60_000 }) } catch (err) { if (err instanceof UserOperationTimeoutError) { // receipt not available yet — poll later } }

Account Operations

Imperative counterparts to the React hooks. Use them from non-component code (web workers, event listeners, setup scripts) where hooks aren’t available.

import { sendUserOperation, prepareUserOperation, signTypedData, deployAccount, getAllSmartAccounts, getSmartAccountForChain, getUserOperationByHash, getUserOperationReceipt, waitForUserOperationReceipt, } from '@embarkai/ui-kit' import type { AccountSession, SendTransactionParams, SignTypedDataParams, SmartAccountEntry, RegisterSmartAccountResult, } from '@embarkai/ui-kit'

All transaction functions take an AccountSession (obtained from useAccountSession() or the session store) and accept an optional chainId — if omitted, the active chain from the session store is used.

prepareUserOperation(session, params)

Builds and signs a UserOperation without submitting it to the bundler. Returns the signed UserOperationV07 and its hash — useful when you want to verify or persist the op before broadcasting it.

const { userOp, userOpHash } = await prepareUserOperation(session, { to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', value: '1000000000000000000', data: '0x', feeType: 'standard', // 'economy' | 'standard' (default) | 'fast' chainId: 994873017, // optional })

sendUserOperation(session, params)

Prepares, signs, and submits a UserOperation. Returns the user-op hash. Same params shape as prepareUserOperation.

signTypedData(session, params)

EIP-712 typed-data signing routed through the MPC iframe:

const signature = await signTypedData(session, { domain: { name: 'MyApp', version: '1', chainId: 994873017, verifyingContract: '0x...' }, types: { /* ... */ }, primaryType: 'Order', message: { /* ... */ }, chainId: 994873017, // optional — must match domain.chainId if both are set })

deployAccount(session, feeType?, options?)

Deploys the user’s smart account on a chain. Checks on-chain first and skips deployment if already deployed (unless options.force === true). Returns the deploy UserOp hash, or null if already deployed.

await deployAccount(session, 'economy', { chainId: 994873017, force: false })

getAllSmartAccounts() / getSmartAccountForChain(chainId)

Read the list of smart-account addresses the current user has registered with the TSS backend, across chains. Useful for multi-chain dashboards.

const accounts: SmartAccountEntry[] = await getAllSmartAccounts() // → [{ chainId, address, factoryAddress, registeredAt }, ...] const lumiaEntry = await getSmartAccountForChain(994873017)

UserOperation status helpers

const op = await getUserOperationByHash(userOpHash) // may be null const receipt = await getUserOperationReceipt(userOpHash) // may be null const final = await waitForUserOperationReceipt(userOpHash, { timeoutMs: 60_000, pollIntervalMs: 2_000, })

Throws UserOperationTimeoutError from the wait* variant on timeout. See Error Handling above.


Profile API

Manage the authenticated user’s profile (display name, avatar, linked providers).

import { getUserProfile, updateUserProfile } from '@embarkai/ui-kit' import type { UserProfile, UpdateProfileRequest, ProviderDetail } from '@embarkai/ui-kit'

getUserProfile()

Fetches the full profile from /api/auth/profile, including linked provider details.

const profile: UserProfile = await getUserProfile() console.log(profile.userId, profile.displayName, profile.avatar) for (const p of profile.providers) { console.log(p.provider, p.externalId, p.verified, p.lastUsedAt) }

ProviderDetail fields:

FieldTypeDescription
idstringComposite provider:externalId. Stable key.
providerstring'email' | 'telegram' | 'passkey' | 'wallet' | …
verifiedbooleanProvider-side verification status.
linkedAtstringISO-8601.
lastUsedAtstringISO-8601.
externalIdstringEmail address, Telegram user ID, credential ID, etc.
metaRecord<string, any>?Provider-specific extras.

updateUserProfile(updates)

Patches mutable profile fields. Currently only displayName is writable.

await updateUserProfile({ displayName: 'Alice' })

useLinkedProfiles()

React hook — thin reactive wrapper over getUserProfile().providers. Prefer the hook in components; use getUserProfile() in imperative code.


Nickname Fingerprint Verification

When resolving a nickname through the TSS service, the backend returns both the owner address and a short XXXX-XXXX fingerprint derived from that address. Always verify the fingerprint locally before showing the resolved address to the user — this protects against a compromised backend swapping addresses silently.

import { generateFingerprint, verifyFingerprint, verifyFingerprintDetailed, } from '@embarkai/ui-kit' import type { FingerprintVerificationResult } from '@embarkai/ui-kit'

generateFingerprint(ownerAddress)

Deterministic function that mirrors the backend algorithm: keccak256 of the checksummed address, first 5 bytes, base32 (Crockford-like, 0/O/I/L excluded), split into XXXX-XXXX.

const fp = generateFingerprint('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb') // → e.g. "A3BK-9XYZ"

verifyFingerprint(ownerAddress, receivedFingerprint) → boolean

Quick boolean check. Use this at the call site of any nickname resolution:

import { useNicknameResolve, verifyFingerprint } from '@embarkai/ui-kit' const { data } = useNicknameResolve('alice') if (data && !verifyFingerprint(data.address, data.fingerprint)) { throw new Error('Nickname resolution fingerprint mismatch — do not use this address.') }

verifyFingerprintDetailed(...)

Same check, but returns a FingerprintVerificationResult so you can surface diagnostic info to the user or logs:

const result: FingerprintVerificationResult = verifyFingerprintDetailed(addr, received) // { isValid, computedFingerprint, receivedFingerprint, error? }

Never skip verification. A false result means the server’s claim is inconsistent with the cryptographic derivation — treat it as a security incident, not a UX annoyance.


See Also

  • Hooks — the idiomatic React way to consume most of these APIs
  • Provider Setup — obtaining the AccountSession used by the imperative APIs above
  • Core SDK → Auth — for backend / non-React auth flows
Last updated on