Vanilla JS / TS
The Sigil class is the only public surface of the vanilla entrypoint.
It mounts the iframe, drives the postMessage protocol, and exposes a
small async API.
Full example
import { Sigil } from '@sigilkeys/sdk';
const wallet = new Sigil({ organizationId: 'org_xxx', publishableKey: 'pk_live_xxx', iframeUrl: 'https://wallet.sigilkeys.com', authMode: 'sigil', // optional: where the iframe gets mounted. Defaults to document.body. iframeContainer: document.getElementById('wallet-slot') ?? undefined,});
await wallet.init();console.log(wallet.getAddress()); // 0x...console.log(wallet.getWallets()); // [{ network, address, curve }, ...]const sig = await wallet.signMessage('hello');await wallet.logout();wallet.destroy(); // tear down iframe + transportMethods
new Sigil(config)
Synchronous. Just wires the config; no network, no DOM mutation yet.
await sigil.init()
Mounts the iframe in iframeContainer (or document.body), waits for
its load event, opens a postMessage transport against its origin, and
runs the auth handshake:
authMode: 'sigil'— the iframe shows the email-OTP UI;init()resolves once the user has signed in (or immediately if the user already has a session in localStorage).authMode: 'oidc'— the SDK callsgetToken()once, sends the JWT to the iframe; the iframe validates it against your provider via Sigil backend and either reuses the existing wallet or creates one.
After init() resolves you can call the remaining methods. Calling it a
second time is a no-op.
sigil.getAddress(): string | null
Returns the wallet address for the primary network (Ethereum by
default), or null if there’s no wallet yet (authenticated user without
a wallet — happens between sign-in and wallet creation).
sigil.getWallets(): WalletInfo[]
Returns all wallets across configured networks (one per curve). Each
entry has { network, address, curve }. Useful when your integration
targets multiple chains and you need the address on each.
sigil.getAddressForNetwork(network: string): string | null
Shorthand for finding the address on a specific network. Returns null
if no wallet exists for that network.
const arbAddress = sigil.getAddressForNetwork('arbitrum');await sigil.signMessage(message: string, options?): Promise<string>
Opens a confirmation modal in the iframe, the user approves, the iframe reconstructs the key in memory, signs (ECDSA secp256k1), wipes, and returns the signature in hex.
Pass { network } in options to select which network’s key signs the
message. Defaults to the primary network.
const sig = await sigil.signMessage('hello', { network: 'arbitrum' });If the user rejects, signMessage rejects with SigilRejectedError.
If the iframe doesn’t respond within 30s, it rejects with SigilTimeoutError.
await sigil.signRaw(digest: Uint8Array, options?): Promise<string>
Signs a raw 32-byte digest without any prefix or hashing. This is the lowest-level signing primitive — use it when you need to sign a digest that has already been hashed by your application (e.g. for protocols with custom signing schemes).
Pass { network } to select which network’s key signs the digest.
const sig = await sigil.signRaw(digest, { network: 'ethereum' });await sigil.signTypedData(td): Promise<{ signature, digest }>
EIP-712 typed-data signing. Returns both the 65-byte signature and the 32-byte EIP-712 digest the iframe actually hashed, so you can ecrecover the signer server-side without re-implementing EIP-712 in your backend.
await sigil.sendTransaction(input): Promise<{ chain, chainId, txHash, from }>
Raw EIP-1559 send from the wallet’s EOA. The user pays gas from their wallet’s native balance on the target chain.
const r = await sigil.sendTransaction({ chain: 'arbitrum', to: '0x2Df1c51E09aECF9cacB7bc98cB1742757f163dF7', // Hyperliquid Bridge2 data: encodeDeposit(amountUSDC), value: '0', preview: { title: 'Deposit to Hyperliquid' },});console.log(r.txHash); // on-chain hashconsole.log(r.from); // EOA that signed| Field | Type | Notes |
|---|---|---|
chain | string | 'ethereum' | 'polygon' | 'arbitrum' | 'base' | 'optimism' | 'bsc' | 'avalanche' | 'solana' |
to | string | Contract or recipient. |
data | string | undefined | 0x-prefixed call data; omit for native transfers. |
value | string | undefined | Decimal wei as a string. |
gasLimit | string | undefined | Override the estimate when needed. |
preview | object | undefined | UX summary the iframe shows on confirm. |
await sigil.logout(): Promise<void>
Clears the session token. The Device share stays so the same end user can sign in again without going through recovery.
sigil.destroy(): void
Removes the iframe from the DOM, closes the transport, and frees memory.
After destroy() the instance is unusable; create a new one for another
session.
Lifecycle in a single function
async function signOnce(message: string) { const wallet = new Sigil({ /* ... */ }); try { await wallet.init(); return await wallet.signMessage(message); } finally { wallet.destroy(); }}For long-lived integrations, prefer the React provider which keeps one instance alive for the lifetime of the app.