1
Install
npm i @meru.app/device-signer
# pubspec.yaml
dependencies:
meru_device_wallet: ^0.1.0
// Package.swift
.package(url: "https://github.com/getmeru/meru-device-wallet-swift.git", from: "0.1.0")
// build.gradle.kts
implementation("app.meru:meru-device-wallet:0.1.0")
2
Sign in (email OTP or passkey)
import { createMeruAuth } from "@meru.app/device-signer/auth";
import * as SecureStore from "expo-secure-store";
const auth = createMeruAuth({
authBase: "https://stablecoin-api.getmeru.com",
companyId: "<your public company id>",
passkeyDomain: "yourapp.com",
storage: {
getItem: (k) => SecureStore.getItemAsync(k),
setItem: (k, v) => SecureStore.setItemAsync(k, v),
removeItem: (k) => SecureStore.deleteItemAsync(k),
},
});
await auth.sendEmailCode(email);
const session = await auth.verifyEmailCode(email, code);
// returning users: const session = await auth.loginWithPasskey();
final auth = MeruAuth(
authBase: 'https://stablecoin-api.getmeru.com',
companyId: '<your public company id>',
passkeyDomain: 'yourapp.com',
);
await auth.sendEmailCode(email);
final session = await auth.verifyEmailCode(email, code);
// returning users: final session = await auth.loginWithPasskey();
let auth = MeruAuth(
authBase: URL(string: "https://stablecoin-api.getmeru.com")!,
companyId: "<your public company id>",
passkeyDomain: "yourapp.com"
)
_ = try await auth.sendEmailCode(email)
let session = try await auth.verifyEmailCode(email, code: code)
// returning users: let session = try await auth.loginWithPasskey()
val auth = MeruAuth(
context = applicationContext,
authBase = "https://stablecoin-api.getmeru.com",
companyId = "<your public company id>",
passkeyDomain = "yourapp.com",
)
auth.sendEmailCode(email)
val session = auth.verifyEmailCode(email, code)
// returning users: val session = auth.loginWithPasskey(activity)
3
Run the wallet flow
status() tells each device exactly what to do — so a second phone gets
recover, never a duplicate wallet.import { MeruDeviceWallet } from "@meru.app/device-signer/wallet";
const wallet = new MeruDeviceWallet({
baseUrl: "https://your-app.com/api/meru",
userId: session.user.id,
getToken: async () => auth.current()?.token ?? null,
});
const { state } = await wallet.status();
if (state === "create") await wallet.enroll({ activate: true });
else if (state === "recover") await wallet.recover();
await wallet.send("GB7C…FB2Q", 5); // device-signed, Meru pays the fee
final wallet = MeruDeviceWallet(
baseUrl: 'https://your-app.com/api/meru',
getToken: () async => auth.current?.token,
keyId: 'meru.device-key.${session.user.id}',
);
switch ((await wallet.status()).state) {
case WalletState.create: await wallet.enroll(activate: true);
case WalletState.recover: await wallet.recover();
case WalletState.ready: break;
}
await wallet.send('GB7C…FB2Q', 5);
let wallet = MeruDeviceWallet(
baseURL: URL(string: "https://your-app.com/api/meru")!,
userId: session.user.id,
getToken: { auth.current?.token }
)
switch try await wallet.status().state {
case .create: try await wallet.enroll(activate: true)
case .recover: try await wallet.recover()
case .ready: break
}
let txHash = try await wallet.send(to: "GB7C…FB2Q", amount: 5)
val wallet = MeruDeviceWallet(
baseUrl = "https://your-app.com/api/meru",
keys = DeviceKeyStore(applicationContext),
userId = session.user.id,
getToken = { auth.current?.token },
)
when (wallet.status().state) {
WalletState.CREATE -> wallet.enroll(activate = true)
WalletState.RECOVER -> wallet.recover()
WalletState.READY -> {}
}
val txHash = wallet.send("GB7C…FB2Q", 5.0)
The
authBase + companyId come from Meru; the backendBase
(https://your-app.com/api/meru) is your own server running createMeruHandler.
See Overview.